1 /* 2 * Copyright (c) 1983, 1995 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.78 (Berkeley) 06/21/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 void 35 stripquotes(s) 36 char *s; 37 { 38 register char *p; 39 register char *q; 40 register char c; 41 42 if (s == NULL) 43 return; 44 45 p = q = s; 46 do 47 { 48 c = *p++; 49 if (c == '\\') 50 c = *p++; 51 else if (c == '"') 52 continue; 53 *q++ = c; 54 } while (c != '\0'); 55 } 56 /* 57 ** XALLOC -- Allocate memory and bitch wildly on failure. 58 ** 59 ** THIS IS A CLUDGE. This should be made to give a proper 60 ** error -- but after all, what can we do? 61 ** 62 ** Parameters: 63 ** sz -- size of area to allocate. 64 ** 65 ** Returns: 66 ** pointer to data region. 67 ** 68 ** Side Effects: 69 ** Memory is allocated. 70 */ 71 72 char * 73 xalloc(sz) 74 register int sz; 75 { 76 register char *p; 77 78 /* some systems can't handle size zero mallocs */ 79 if (sz <= 0) 80 sz = 1; 81 82 p = malloc((unsigned) sz); 83 if (p == NULL) 84 { 85 syserr("!Out of memory!!"); 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 void 186 printav(av) 187 register char **av; 188 { 189 while (*av != NULL) 190 { 191 if (tTd(0, 44)) 192 printf("\n\t%08x=", *av); 193 else 194 (void) putchar(' '); 195 xputs(*av++); 196 } 197 (void) putchar('\n'); 198 } 199 /* 200 ** LOWER -- turn letter into lower case. 201 ** 202 ** Parameters: 203 ** c -- character to turn into lower case. 204 ** 205 ** Returns: 206 ** c, in lower case. 207 ** 208 ** Side Effects: 209 ** none. 210 */ 211 212 char 213 lower(c) 214 register char c; 215 { 216 return((isascii(c) && isupper(c)) ? tolower(c) : c); 217 } 218 /* 219 ** XPUTS -- put string doing control escapes. 220 ** 221 ** Parameters: 222 ** s -- string to put. 223 ** 224 ** Returns: 225 ** none. 226 ** 227 ** Side Effects: 228 ** output to stdout 229 */ 230 231 void 232 xputs(s) 233 register const char *s; 234 { 235 register int c; 236 register struct metamac *mp; 237 extern struct metamac MetaMacros[]; 238 239 if (s == NULL) 240 { 241 printf("<null>"); 242 return; 243 } 244 while ((c = (*s++ & 0377)) != '\0') 245 { 246 if (!isascii(c)) 247 { 248 if (c == MATCHREPL) 249 { 250 putchar('$'); 251 continue; 252 } 253 if (c == MACROEXPAND) 254 { 255 putchar('$'); 256 if (bitset(0200, *s)) 257 printf("{%s}", macname(*s++ & 0377)); 258 continue; 259 } 260 for (mp = MetaMacros; mp->metaname != '\0'; mp++) 261 { 262 if ((mp->metaval & 0377) == c) 263 { 264 printf("$%c", mp->metaname); 265 break; 266 } 267 } 268 if (mp->metaname != '\0') 269 continue; 270 (void) putchar('\\'); 271 c &= 0177; 272 } 273 if (isprint(c)) 274 { 275 putchar(c); 276 continue; 277 } 278 279 /* wasn't a meta-macro -- find another way to print it */ 280 switch (c) 281 { 282 case '\n': 283 c = 'n'; 284 break; 285 286 case '\r': 287 c = 'r'; 288 break; 289 290 case '\t': 291 c = 't'; 292 break; 293 294 default: 295 (void) putchar('^'); 296 (void) putchar(c ^ 0100); 297 continue; 298 } 299 (void) putchar('\\'); 300 (void) putchar(c); 301 } 302 (void) fflush(stdout); 303 } 304 /* 305 ** MAKELOWER -- Translate a line into lower case 306 ** 307 ** Parameters: 308 ** p -- the string to translate. If NULL, return is 309 ** immediate. 310 ** 311 ** Returns: 312 ** none. 313 ** 314 ** Side Effects: 315 ** String pointed to by p is translated to lower case. 316 ** 317 ** Called By: 318 ** parse 319 */ 320 321 void 322 makelower(p) 323 register char *p; 324 { 325 register char c; 326 327 if (p == NULL) 328 return; 329 for (; (c = *p) != '\0'; p++) 330 if (isascii(c) && isupper(c)) 331 *p = tolower(c); 332 } 333 /* 334 ** BUILDFNAME -- build full name from gecos style entry. 335 ** 336 ** This routine interprets the strange entry that would appear 337 ** in the GECOS field of the password file. 338 ** 339 ** Parameters: 340 ** p -- name to build. 341 ** login -- the login name of this user (for &). 342 ** buf -- place to put the result. 343 ** 344 ** Returns: 345 ** none. 346 ** 347 ** Side Effects: 348 ** none. 349 */ 350 351 void 352 buildfname(gecos, login, buf) 353 register char *gecos; 354 char *login; 355 char *buf; 356 { 357 register char *p; 358 register char *bp = buf; 359 int l; 360 361 if (*gecos == '*') 362 gecos++; 363 364 /* find length of final string */ 365 l = 0; 366 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 367 { 368 if (*p == '&') 369 l += strlen(login); 370 else 371 l++; 372 } 373 374 /* now fill in buf */ 375 for (p = gecos; *p != '\0' && *p != ',' && *p != ';' && *p != '%'; p++) 376 { 377 if (*p == '&') 378 { 379 (void) strcpy(bp, login); 380 *bp = toupper(*bp); 381 while (*bp != '\0') 382 bp++; 383 } 384 else 385 *bp++ = *p; 386 } 387 *bp = '\0'; 388 } 389 /* 390 ** SAFEFILE -- return true if a file exists and is safe for a user. 391 ** 392 ** Parameters: 393 ** fn -- filename to check. 394 ** uid -- user id to compare against. 395 ** gid -- group id to compare against. 396 ** uname -- user name to compare against (used for group 397 ** sets). 398 ** flags -- modifiers: 399 ** SFF_MUSTOWN -- "uid" must own this file. 400 ** SFF_NOSLINK -- file cannot be a symbolic link. 401 ** mode -- mode bits that must match. 402 ** st -- if set, points to a stat structure that will 403 ** get the stat info for the file. 404 ** 405 ** Returns: 406 ** 0 if fn exists, is owned by uid, and matches mode. 407 ** An errno otherwise. The actual errno is cleared. 408 ** 409 ** Side Effects: 410 ** none. 411 */ 412 413 #include <grp.h> 414 415 #ifndef S_IXOTH 416 # define S_IXOTH (S_IEXEC >> 6) 417 #endif 418 419 #ifndef S_IXGRP 420 # define S_IXGRP (S_IEXEC >> 3) 421 #endif 422 423 #ifndef S_IXUSR 424 # define S_IXUSR (S_IEXEC) 425 #endif 426 427 #define ST_MODE_NOFILE 0171147 /* unlikely to occur */ 428 429 int 430 safefile(fn, uid, gid, uname, flags, mode, st) 431 char *fn; 432 uid_t uid; 433 gid_t gid; 434 char *uname; 435 int flags; 436 int mode; 437 struct stat *st; 438 { 439 register char *p; 440 register struct group *gr = NULL; 441 struct stat stbuf; 442 443 if (tTd(54, 4)) 444 printf("safefile(%s, uid=%d, gid=%d, flags=%x, mode=%o):\n", 445 fn, uid, gid, flags, mode); 446 errno = 0; 447 if (st == NULL) 448 st = &stbuf; 449 450 if (!bitset(SFF_NOPATHCHECK, flags) || 451 (uid == 0 && !bitset(SFF_ROOTOK, flags))) 452 { 453 /* check the path to the file for acceptability */ 454 for (p = fn; (p = strchr(++p, '/')) != NULL; *p = '/') 455 { 456 *p = '\0'; 457 if (stat(fn, &stbuf) < 0) 458 break; 459 if (uid == 0 && bitset(S_IWGRP|S_IWOTH, stbuf.st_mode)) 460 message("051 WARNING: writable directory %s", 461 fn); 462 if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 463 { 464 if (bitset(S_IXOTH, stbuf.st_mode)) 465 continue; 466 break; 467 } 468 if (stbuf.st_uid == uid && 469 bitset(S_IXUSR, stbuf.st_mode)) 470 continue; 471 if (stbuf.st_gid == gid && 472 bitset(S_IXGRP, stbuf.st_mode)) 473 continue; 474 #ifndef NO_GROUP_SET 475 if (uname != NULL && 476 ((gr != NULL && gr->gr_gid == stbuf.st_gid) || 477 (gr = getgrgid(stbuf.st_gid)) != NULL)) 478 { 479 register char **gp; 480 481 for (gp = gr->gr_mem; gp != NULL && *gp != NULL; gp++) 482 if (strcmp(*gp, uname) == 0) 483 break; 484 if (gp != NULL && *gp != NULL && 485 bitset(S_IXGRP, stbuf.st_mode)) 486 continue; 487 } 488 #endif 489 if (!bitset(S_IXOTH, stbuf.st_mode)) 490 break; 491 } 492 if (p != NULL) 493 { 494 int ret = errno; 495 496 if (ret == 0) 497 ret = EACCES; 498 if (tTd(54, 4)) 499 printf("\t[dir %s] %s\n", fn, errstring(ret)); 500 *p = '/'; 501 return ret; 502 } 503 } 504 505 #ifdef HASLSTAT 506 if ((bitset(SFF_NOSLINK, flags) ? lstat(fn, st) 507 : stat(fn, st)) < 0) 508 #else 509 if (stat(fn, st) < 0) 510 #endif 511 { 512 int ret = errno; 513 514 if (tTd(54, 4)) 515 printf("\t%s\n", errstring(ret)); 516 517 errno = 0; 518 if (!bitset(SFF_CREAT, flags)) 519 return ret; 520 521 /* check to see if legal to create the file */ 522 p = strrchr(fn, '/'); 523 if (p == NULL) 524 return ENOTDIR; 525 *p = '\0'; 526 if (stat(fn, &stbuf) >= 0) 527 { 528 int md = S_IWRITE|S_IEXEC; 529 if (stbuf.st_uid != uid) 530 md >>= 6; 531 if ((stbuf.st_mode & md) != md) 532 errno = EACCES; 533 } 534 ret = errno; 535 if (tTd(54, 4)) 536 printf("\t[final dir %s uid %d mode %o] %s\n", 537 fn, stbuf.st_uid, stbuf.st_mode, 538 errstring(ret)); 539 *p = '/'; 540 st->st_mode = ST_MODE_NOFILE; 541 return ret; 542 } 543 544 #ifdef S_ISLNK 545 if (bitset(SFF_NOSLINK, flags) && S_ISLNK(st->st_mode)) 546 { 547 if (tTd(54, 4)) 548 printf("\t[slink mode %o]\tEPERM\n", st->st_mode); 549 return EPERM; 550 } 551 #endif 552 if (bitset(SFF_REGONLY, flags) && !S_ISREG(st->st_mode)) 553 { 554 if (tTd(54, 4)) 555 printf("\t[non-reg mode %o]\tEPERM\n", st->st_mode); 556 return EPERM; 557 } 558 if (bitset(S_IWUSR|S_IWGRP|S_IWOTH, mode) && bitset(0111, st->st_mode)) 559 { 560 if (tTd(29, 5)) 561 printf("failed (mode %o: x bits)\n", st->st_mode); 562 return EPERM; 563 } 564 565 if (bitset(SFF_SETUIDOK, flags)) 566 { 567 #ifdef SUID_ROOT_FILES_OK 568 if (bitset(S_ISUID, st->st_mode)) 569 #else 570 if (bitset(S_ISUID, st->st_mode) && st->st_uid != 0) 571 #endif 572 { 573 uid = st->st_uid; 574 uname = NULL; 575 } 576 #ifdef SUID_ROOT_FILES_OK 577 if (bitset(S_ISGID, st->st_mode)) 578 #else 579 if (bitset(S_ISGID, st->st_mode) && st->st_gid != 0) 580 #endif 581 gid = st->st_gid; 582 } 583 584 if (uid == 0 && !bitset(SFF_ROOTOK, flags)) 585 mode >>= 6; 586 else if (st->st_uid != uid) 587 { 588 mode >>= 3; 589 if (st->st_gid == gid) 590 ; 591 #ifndef NO_GROUP_SET 592 else if (uname != NULL && 593 ((gr != NULL && gr->gr_gid == st->st_gid) || 594 (gr = getgrgid(st->st_gid)) != NULL)) 595 { 596 register char **gp; 597 598 for (gp = gr->gr_mem; *gp != NULL; gp++) 599 if (strcmp(*gp, uname) == 0) 600 break; 601 if (*gp == NULL) 602 mode >>= 3; 603 } 604 #endif 605 else 606 mode >>= 3; 607 } 608 if (tTd(54, 4)) 609 printf("\t[uid %d, stat %o, mode %o] ", 610 st->st_uid, st->st_mode, mode); 611 if ((st->st_uid == uid || st->st_uid == 0 || 612 !bitset(SFF_MUSTOWN, flags)) && 613 (st->st_mode & mode) == mode) 614 { 615 if (tTd(54, 4)) 616 printf("\tOK\n"); 617 return 0; 618 } 619 if (tTd(54, 4)) 620 printf("\tEACCES\n"); 621 return EACCES; 622 } 623 /* 624 ** SAFEFOPEN -- do a file open with extra checking 625 ** 626 ** Parameters: 627 ** fn -- the file name to open. 628 ** omode -- the open-style mode flags. 629 ** cmode -- the create-style mode flags. 630 ** sff -- safefile flags. 631 ** 632 ** Returns: 633 ** Same as fopen. 634 */ 635 636 #ifndef O_ACCMODE 637 # define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) 638 #endif 639 640 FILE * 641 safefopen(fn, omode, cmode, sff) 642 char *fn; 643 int omode; 644 int cmode; 645 int sff; 646 { 647 int rval; 648 FILE *fp; 649 int smode; 650 struct stat stb, sta; 651 652 if (bitset(O_CREAT, omode)) 653 sff |= SFF_CREAT; 654 smode = 0; 655 switch (omode & O_ACCMODE) 656 { 657 case O_RDONLY: 658 smode = S_IREAD; 659 break; 660 661 case O_WRONLY: 662 smode = S_IWRITE; 663 break; 664 665 case O_RDWR: 666 smode = S_IREAD|S_IWRITE; 667 break; 668 669 default: 670 smode = 0; 671 break; 672 } 673 if (bitset(SFF_OPENASROOT, sff)) 674 rval = safefile(fn, 0, 0, NULL, sff, smode, &stb); 675 else 676 rval = safefile(fn, RealUid, RealGid, RealUserName, 677 sff, smode, &stb); 678 if (rval != 0) 679 { 680 errno = rval; 681 return NULL; 682 } 683 if (stb.st_mode == ST_MODE_NOFILE) 684 omode |= O_EXCL; 685 686 fp = dfopen(fn, omode, cmode); 687 if (fp == NULL) 688 return NULL; 689 if (bitset(O_EXCL, omode)) 690 return fp; 691 if (fstat(fileno(fp), &sta) < 0 || 692 sta.st_nlink != stb.st_nlink || 693 sta.st_dev != stb.st_dev || 694 sta.st_ino != stb.st_ino || 695 sta.st_uid != stb.st_uid || 696 sta.st_gid != stb.st_gid) 697 { 698 syserr("554 cannot open: file %s changed after open", fn); 699 fclose(fp); 700 errno = EPERM; 701 return NULL; 702 } 703 return fp; 704 } 705 /* 706 ** FIXCRLF -- fix <CR><LF> in line. 707 ** 708 ** Looks for the <CR><LF> combination and turns it into the 709 ** UNIX canonical <NL> character. It only takes one line, 710 ** i.e., it is assumed that the first <NL> found is the end 711 ** of the line. 712 ** 713 ** Parameters: 714 ** line -- the line to fix. 715 ** stripnl -- if true, strip the newline also. 716 ** 717 ** Returns: 718 ** none. 719 ** 720 ** Side Effects: 721 ** line is changed in place. 722 */ 723 724 void 725 fixcrlf(line, stripnl) 726 char *line; 727 bool stripnl; 728 { 729 register char *p; 730 731 p = strchr(line, '\n'); 732 if (p == NULL) 733 return; 734 if (p > line && p[-1] == '\r') 735 p--; 736 if (!stripnl) 737 *p++ = '\n'; 738 *p = '\0'; 739 } 740 /* 741 ** DFOPEN -- determined file open 742 ** 743 ** This routine has the semantics of fopen, except that it will 744 ** keep trying a few times to make this happen. The idea is that 745 ** on very loaded systems, we may run out of resources (inodes, 746 ** whatever), so this tries to get around it. 747 */ 748 749 struct omodes 750 { 751 int mask; 752 int mode; 753 char *farg; 754 } OpenModes[] = 755 { 756 O_ACCMODE, O_RDONLY, "r", 757 O_ACCMODE|O_APPEND, O_WRONLY, "w", 758 O_ACCMODE|O_APPEND, O_WRONLY|O_APPEND, "a", 759 O_TRUNC, 0, "w+", 760 O_APPEND, O_APPEND, "a+", 761 0, 0, "r+", 762 }; 763 764 FILE * 765 dfopen(filename, omode, cmode) 766 char *filename; 767 int omode; 768 int cmode; 769 { 770 register int tries; 771 int fd; 772 register struct omodes *om; 773 struct stat st; 774 775 for (om = OpenModes; om->mask != 0; om++) 776 if ((omode & om->mask) == om->mode) 777 break; 778 779 for (tries = 0; tries < 10; tries++) 780 { 781 sleep((unsigned) (10 * tries)); 782 errno = 0; 783 fd = open(filename, omode, cmode); 784 if (fd >= 0) 785 break; 786 switch (errno) 787 { 788 case ENFILE: /* system file table full */ 789 case EINTR: /* interrupted syscall */ 790 #ifdef ETXTBSY 791 case ETXTBSY: /* Apollo: net file locked */ 792 #endif 793 continue; 794 } 795 break; 796 } 797 if (fd >= 0 && fstat(fd, &st) >= 0 && S_ISREG(st.st_mode)) 798 { 799 int locktype; 800 801 /* lock the file to avoid accidental conflicts */ 802 if ((omode & O_ACCMODE) != O_RDONLY) 803 locktype = LOCK_EX; 804 else 805 locktype = LOCK_SH; 806 (void) lockfile(fd, filename, NULL, locktype); 807 errno = 0; 808 } 809 if (fd < 0) 810 return NULL; 811 else 812 return fdopen(fd, om->farg); 813 } 814 /* 815 ** PUTLINE -- put a line like fputs obeying SMTP conventions 816 ** 817 ** This routine always guarantees outputing a newline (or CRLF, 818 ** as appropriate) at the end of the string. 819 ** 820 ** Parameters: 821 ** l -- line to put. 822 ** mci -- the mailer connection information. 823 ** 824 ** Returns: 825 ** none 826 ** 827 ** Side Effects: 828 ** output of l to fp. 829 */ 830 831 void 832 putline(l, mci) 833 register char *l; 834 register MCI *mci; 835 { 836 putxline(l, mci, PXLF_MAPFROM); 837 } 838 /* 839 ** PUTXLINE -- putline with flags bits. 840 ** 841 ** This routine always guarantees outputing a newline (or CRLF, 842 ** as appropriate) at the end of the string. 843 ** 844 ** Parameters: 845 ** l -- line to put. 846 ** mci -- the mailer connection information. 847 ** pxflags -- flag bits: 848 ** PXLF_MAPFROM -- map From_ to >From_. 849 ** PXLF_STRIP8BIT -- strip 8th bit. 850 ** 851 ** Returns: 852 ** none 853 ** 854 ** Side Effects: 855 ** output of l to fp. 856 */ 857 858 void 859 putxline(l, mci, pxflags) 860 register char *l; 861 register MCI *mci; 862 int pxflags; 863 { 864 register char *p; 865 register char svchar; 866 int slop = 0; 867 868 /* strip out 0200 bits -- these can look like TELNET protocol */ 869 if (bitset(MCIF_7BIT, mci->mci_flags) || 870 bitset(PXLF_STRIP8BIT, pxflags)) 871 { 872 for (p = l; (svchar = *p) != '\0'; ++p) 873 if (bitset(0200, svchar)) 874 *p = svchar &~ 0200; 875 } 876 877 do 878 { 879 /* find the end of the line */ 880 p = strchr(l, '\n'); 881 if (p == NULL) 882 p = &l[strlen(l)]; 883 884 if (TrafficLogFile != NULL) 885 fprintf(TrafficLogFile, "%05d >>> ", getpid()); 886 887 /* check for line overflow */ 888 while (mci->mci_mailer->m_linelimit > 0 && 889 (p - l + slop) > mci->mci_mailer->m_linelimit) 890 { 891 register char *q = &l[mci->mci_mailer->m_linelimit - slop - 1]; 892 893 svchar = *q; 894 *q = '\0'; 895 if (l[0] == '.' && slop == 0 && 896 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 897 { 898 (void) putc('.', mci->mci_out); 899 if (TrafficLogFile != NULL) 900 (void) putc('.', TrafficLogFile); 901 } 902 else if (l[0] == 'F' && slop == 0 && 903 bitset(PXLF_MAPFROM, pxflags) && 904 strncmp(l, "From ", 5) == 0 && 905 bitnset(M_ESCFROM, mci->mci_mailer->m_flags)) 906 { 907 (void) putc('>', mci->mci_out); 908 if (TrafficLogFile != NULL) 909 (void) putc('>', TrafficLogFile); 910 } 911 fputs(l, mci->mci_out); 912 (void) putc('!', mci->mci_out); 913 fputs(mci->mci_mailer->m_eol, mci->mci_out); 914 (void) putc(' ', mci->mci_out); 915 if (TrafficLogFile != NULL) 916 fprintf(TrafficLogFile, "%s!\n%05d >>> ", 917 l, getpid()); 918 *q = svchar; 919 l = q; 920 slop = 1; 921 } 922 923 /* output last part */ 924 if (l[0] == '.' && slop == 0 && 925 bitnset(M_XDOT, mci->mci_mailer->m_flags)) 926 { 927 (void) putc('.', mci->mci_out); 928 if (TrafficLogFile != NULL) 929 (void) putc('.', TrafficLogFile); 930 } 931 if (TrafficLogFile != NULL) 932 fprintf(TrafficLogFile, "%.*s\n", p - l, l); 933 for ( ; l < p; ++l) 934 (void) putc(*l, mci->mci_out); 935 fputs(mci->mci_mailer->m_eol, mci->mci_out); 936 if (*l == '\n') 937 ++l; 938 } while (l[0] != '\0'); 939 } 940 /* 941 ** XUNLINK -- unlink a file, doing logging as appropriate. 942 ** 943 ** Parameters: 944 ** f -- name of file to unlink. 945 ** 946 ** Returns: 947 ** none. 948 ** 949 ** Side Effects: 950 ** f is unlinked. 951 */ 952 953 void 954 xunlink(f) 955 char *f; 956 { 957 register int i; 958 959 # ifdef LOG 960 if (LogLevel > 98) 961 syslog(LOG_DEBUG, "%s: unlink %s", CurEnv->e_id, f); 962 # endif /* LOG */ 963 964 i = unlink(f); 965 # ifdef LOG 966 if (i < 0 && LogLevel > 97) 967 syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 968 # endif /* LOG */ 969 } 970 /* 971 ** XFCLOSE -- close a file, doing logging as appropriate. 972 ** 973 ** Parameters: 974 ** fp -- file pointer for the file to close 975 ** a, b -- miscellaneous crud to print for debugging 976 ** 977 ** Returns: 978 ** none. 979 ** 980 ** Side Effects: 981 ** fp is closed. 982 */ 983 984 void 985 xfclose(fp, a, b) 986 FILE *fp; 987 char *a, *b; 988 { 989 if (tTd(53, 99)) 990 printf("xfclose(%x) %s %s\n", fp, a, b); 991 #if XDEBUG 992 if (fileno(fp) == 1) 993 syserr("xfclose(%s %s): fd = 1", a, b); 994 #endif 995 if (fclose(fp) < 0 && tTd(53, 99)) 996 printf("xfclose FAILURE: %s\n", errstring(errno)); 997 } 998 /* 999 ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 1000 ** 1001 ** Parameters: 1002 ** buf -- place to put the input line. 1003 ** siz -- size of buf. 1004 ** fp -- file to read from. 1005 ** timeout -- the timeout before error occurs. 1006 ** during -- what we are trying to read (for error messages). 1007 ** 1008 ** Returns: 1009 ** NULL on error (including timeout). This will also leave 1010 ** buf containing a null string. 1011 ** buf otherwise. 1012 ** 1013 ** Side Effects: 1014 ** none. 1015 */ 1016 1017 static jmp_buf CtxReadTimeout; 1018 static void readtimeout(); 1019 1020 char * 1021 sfgets(buf, siz, fp, timeout, during) 1022 char *buf; 1023 int siz; 1024 FILE *fp; 1025 time_t timeout; 1026 char *during; 1027 { 1028 register EVENT *ev = NULL; 1029 register char *p; 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 # ifdef LOG 1043 syslog(LOG_NOTICE, 1044 "timeout waiting for input from %s during %s\n", 1045 CurHostName? CurHostName: "local", during); 1046 # endif 1047 errno = 0; 1048 usrerr("451 timeout waiting for input during %s", 1049 during); 1050 buf[0] = '\0'; 1051 #if XDEBUG 1052 checkfd012(during); 1053 #endif 1054 return (NULL); 1055 } 1056 ev = setevent(timeout, readtimeout, 0); 1057 } 1058 1059 /* try to read */ 1060 p = NULL; 1061 while (!feof(fp) && !ferror(fp)) 1062 { 1063 errno = 0; 1064 p = fgets(buf, siz, fp); 1065 if (p != NULL || errno != EINTR) 1066 break; 1067 clearerr(fp); 1068 } 1069 1070 /* clear the event if it has not sprung */ 1071 clrevent(ev); 1072 1073 /* clean up the books and exit */ 1074 LineNumber++; 1075 if (p == NULL) 1076 { 1077 buf[0] = '\0'; 1078 if (TrafficLogFile != NULL) 1079 fprintf(TrafficLogFile, "%05d <<< [EOF]\n", getpid()); 1080 return (NULL); 1081 } 1082 if (TrafficLogFile != NULL) 1083 fprintf(TrafficLogFile, "%05d <<< %s", getpid(), buf); 1084 if (SevenBitInput) 1085 { 1086 for (p = buf; *p != '\0'; p++) 1087 *p &= ~0200; 1088 } 1089 else if (!HasEightBits) 1090 { 1091 for (p = buf; *p != '\0'; p++) 1092 { 1093 if (bitset(0200, *p)) 1094 { 1095 HasEightBits = TRUE; 1096 break; 1097 } 1098 } 1099 } 1100 return (buf); 1101 } 1102 1103 static void 1104 readtimeout(timeout) 1105 time_t timeout; 1106 { 1107 longjmp(CtxReadTimeout, 1); 1108 } 1109 /* 1110 ** FGETFOLDED -- like fgets, but know about folded lines. 1111 ** 1112 ** Parameters: 1113 ** buf -- place to put result. 1114 ** n -- bytes available. 1115 ** f -- file to read from. 1116 ** 1117 ** Returns: 1118 ** input line(s) on success, NULL on error or EOF. 1119 ** This will normally be buf -- unless the line is too 1120 ** long, when it will be xalloc()ed. 1121 ** 1122 ** Side Effects: 1123 ** buf gets lines from f, with continuation lines (lines 1124 ** with leading white space) appended. CRLF's are mapped 1125 ** into single newlines. Any trailing NL is stripped. 1126 */ 1127 1128 char * 1129 fgetfolded(buf, n, f) 1130 char *buf; 1131 register int n; 1132 FILE *f; 1133 { 1134 register char *p = buf; 1135 char *bp = buf; 1136 register int i; 1137 1138 n--; 1139 while ((i = getc(f)) != EOF) 1140 { 1141 if (i == '\r') 1142 { 1143 i = getc(f); 1144 if (i != '\n') 1145 { 1146 if (i != EOF) 1147 (void) ungetc(i, f); 1148 i = '\r'; 1149 } 1150 } 1151 if (--n <= 0) 1152 { 1153 /* allocate new space */ 1154 char *nbp; 1155 int nn; 1156 1157 nn = (p - bp); 1158 if (nn < MEMCHUNKSIZE) 1159 nn *= 2; 1160 else 1161 nn += MEMCHUNKSIZE; 1162 nbp = xalloc(nn); 1163 bcopy(bp, nbp, p - bp); 1164 p = &nbp[p - bp]; 1165 if (bp != buf) 1166 free(bp); 1167 bp = nbp; 1168 n = nn - (p - bp); 1169 } 1170 *p++ = i; 1171 if (i == '\n') 1172 { 1173 LineNumber++; 1174 i = getc(f); 1175 if (i != EOF) 1176 (void) ungetc(i, f); 1177 if (i != ' ' && i != '\t') 1178 break; 1179 } 1180 } 1181 if (p == bp) 1182 return (NULL); 1183 if (p[-1] == '\n') 1184 p--; 1185 *p = '\0'; 1186 return (bp); 1187 } 1188 /* 1189 ** CURTIME -- return current time. 1190 ** 1191 ** Parameters: 1192 ** none. 1193 ** 1194 ** Returns: 1195 ** the current time. 1196 ** 1197 ** Side Effects: 1198 ** none. 1199 */ 1200 1201 time_t 1202 curtime() 1203 { 1204 auto time_t t; 1205 1206 (void) time(&t); 1207 return (t); 1208 } 1209 /* 1210 ** ATOBOOL -- convert a string representation to boolean. 1211 ** 1212 ** Defaults to "TRUE" 1213 ** 1214 ** Parameters: 1215 ** s -- string to convert. Takes "tTyY" as true, 1216 ** others as false. 1217 ** 1218 ** Returns: 1219 ** A boolean representation of the string. 1220 ** 1221 ** Side Effects: 1222 ** none. 1223 */ 1224 1225 bool 1226 atobool(s) 1227 register char *s; 1228 { 1229 if (s == NULL || *s == '\0' || strchr("tTyY", *s) != NULL) 1230 return (TRUE); 1231 return (FALSE); 1232 } 1233 /* 1234 ** ATOOCT -- convert a string representation to octal. 1235 ** 1236 ** Parameters: 1237 ** s -- string to convert. 1238 ** 1239 ** Returns: 1240 ** An integer representing the string interpreted as an 1241 ** octal number. 1242 ** 1243 ** Side Effects: 1244 ** none. 1245 */ 1246 1247 int 1248 atooct(s) 1249 register char *s; 1250 { 1251 register int i = 0; 1252 1253 while (*s >= '0' && *s <= '7') 1254 i = (i << 3) | (*s++ - '0'); 1255 return (i); 1256 } 1257 /* 1258 ** WAITFOR -- wait for a particular process id. 1259 ** 1260 ** Parameters: 1261 ** pid -- process id to wait for. 1262 ** 1263 ** Returns: 1264 ** status of pid. 1265 ** -1 if pid never shows up. 1266 ** 1267 ** Side Effects: 1268 ** none. 1269 */ 1270 1271 int 1272 waitfor(pid) 1273 int pid; 1274 { 1275 #ifdef WAITUNION 1276 union wait st; 1277 #else 1278 auto int st; 1279 #endif 1280 int i; 1281 1282 do 1283 { 1284 errno = 0; 1285 i = wait(&st); 1286 } while ((i >= 0 || errno == EINTR) && i != pid); 1287 if (i < 0) 1288 return -1; 1289 #ifdef WAITUNION 1290 return st.w_status; 1291 #else 1292 return st; 1293 #endif 1294 } 1295 /* 1296 ** BITINTERSECT -- tell if two bitmaps intersect 1297 ** 1298 ** Parameters: 1299 ** a, b -- the bitmaps in question 1300 ** 1301 ** Returns: 1302 ** TRUE if they have a non-null intersection 1303 ** FALSE otherwise 1304 ** 1305 ** Side Effects: 1306 ** none. 1307 */ 1308 1309 bool 1310 bitintersect(a, b) 1311 BITMAP a; 1312 BITMAP b; 1313 { 1314 int i; 1315 1316 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1317 if ((a[i] & b[i]) != 0) 1318 return (TRUE); 1319 return (FALSE); 1320 } 1321 /* 1322 ** BITZEROP -- tell if a bitmap is all zero 1323 ** 1324 ** Parameters: 1325 ** map -- the bit map to check 1326 ** 1327 ** Returns: 1328 ** TRUE if map is all zero. 1329 ** FALSE if there are any bits set in map. 1330 ** 1331 ** Side Effects: 1332 ** none. 1333 */ 1334 1335 bool 1336 bitzerop(map) 1337 BITMAP map; 1338 { 1339 int i; 1340 1341 for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 1342 if (map[i] != 0) 1343 return (FALSE); 1344 return (TRUE); 1345 } 1346 /* 1347 ** STRCONTAINEDIN -- tell if one string is contained in another 1348 ** 1349 ** Parameters: 1350 ** a -- possible substring. 1351 ** b -- possible superstring. 1352 ** 1353 ** Returns: 1354 ** TRUE if a is contained in b. 1355 ** FALSE otherwise. 1356 */ 1357 1358 bool 1359 strcontainedin(a, b) 1360 register char *a; 1361 register char *b; 1362 { 1363 int la; 1364 int lb; 1365 int c; 1366 1367 la = strlen(a); 1368 lb = strlen(b); 1369 c = *a; 1370 if (isascii(c) && isupper(c)) 1371 c = tolower(c); 1372 for (; lb-- >= la; b++) 1373 { 1374 if (*b != c && isascii(*b) && isupper(*b) && tolower(*b) != c) 1375 continue; 1376 if (strncasecmp(a, b, la) == 0) 1377 return TRUE; 1378 } 1379 return FALSE; 1380 } 1381 /* 1382 ** CHECKFD012 -- check low numbered file descriptors 1383 ** 1384 ** File descriptors 0, 1, and 2 should be open at all times. 1385 ** This routine verifies that, and fixes it if not true. 1386 ** 1387 ** Parameters: 1388 ** where -- a tag printed if the assertion failed 1389 ** 1390 ** Returns: 1391 ** none 1392 */ 1393 1394 void 1395 checkfd012(where) 1396 char *where; 1397 { 1398 #if XDEBUG 1399 register int i; 1400 struct stat stbuf; 1401 1402 for (i = 0; i < 3; i++) 1403 { 1404 if (fstat(i, &stbuf) < 0 && errno != EOPNOTSUPP) 1405 { 1406 /* oops.... */ 1407 int fd; 1408 1409 syserr("%s: fd %d not open", where, i); 1410 fd = open("/dev/null", i == 0 ? O_RDONLY : O_WRONLY, 0666); 1411 if (fd != i) 1412 { 1413 (void) dup2(fd, i); 1414 (void) close(fd); 1415 } 1416 } 1417 } 1418 #endif /* XDEBUG */ 1419 } 1420 /* 1421 ** PRINTOPENFDS -- print the open file descriptors (for debugging) 1422 ** 1423 ** Parameters: 1424 ** logit -- if set, send output to syslog; otherwise 1425 ** print for debugging. 1426 ** 1427 ** Returns: 1428 ** none. 1429 */ 1430 1431 #include <arpa/inet.h> 1432 1433 void 1434 printopenfds(logit) 1435 bool logit; 1436 { 1437 register int fd; 1438 extern int DtableSize; 1439 1440 for (fd = 0; fd < DtableSize; fd++) 1441 dumpfd(fd, FALSE, logit); 1442 } 1443 /* 1444 ** DUMPFD -- dump a file descriptor 1445 ** 1446 ** Parameters: 1447 ** fd -- the file descriptor to dump. 1448 ** printclosed -- if set, print a notification even if 1449 ** it is closed; otherwise print nothing. 1450 ** logit -- if set, send output to syslog instead of stdout. 1451 */ 1452 1453 void 1454 dumpfd(fd, printclosed, logit) 1455 int fd; 1456 bool printclosed; 1457 bool logit; 1458 { 1459 register char *p; 1460 char *hp; 1461 char *fmtstr; 1462 SOCKADDR sa; 1463 auto int slen; 1464 struct stat st; 1465 char buf[200]; 1466 extern char *hostnamebyanyaddr(); 1467 1468 p = buf; 1469 sprintf(p, "%3d: ", fd); 1470 p += strlen(p); 1471 1472 if (fstat(fd, &st) < 0) 1473 { 1474 if (printclosed || errno != EBADF) 1475 { 1476 sprintf(p, "CANNOT STAT (%s)", errstring(errno)); 1477 goto printit; 1478 } 1479 return; 1480 } 1481 1482 slen = fcntl(fd, F_GETFL, NULL); 1483 if (slen != -1) 1484 { 1485 sprintf(p, "fl=0x%x, ", slen); 1486 p += strlen(p); 1487 } 1488 1489 sprintf(p, "mode=%o: ", st.st_mode); 1490 p += strlen(p); 1491 switch (st.st_mode & S_IFMT) 1492 { 1493 #ifdef S_IFSOCK 1494 case S_IFSOCK: 1495 sprintf(p, "SOCK "); 1496 p += strlen(p); 1497 slen = sizeof sa; 1498 if (getsockname(fd, &sa.sa, &slen) < 0) 1499 sprintf(p, "(%s)", errstring(errno)); 1500 else 1501 { 1502 hp = hostnamebyanyaddr(&sa); 1503 if (sa.sa.sa_family == AF_INET) 1504 sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port)); 1505 else 1506 sprintf(p, "%s", hp); 1507 } 1508 p += strlen(p); 1509 sprintf(p, "->"); 1510 p += strlen(p); 1511 slen = sizeof sa; 1512 if (getpeername(fd, &sa.sa, &slen) < 0) 1513 sprintf(p, "(%s)", errstring(errno)); 1514 else 1515 { 1516 hp = hostnamebyanyaddr(&sa); 1517 if (sa.sa.sa_family == AF_INET) 1518 sprintf(p, "%s/%d", hp, ntohs(sa.sin.sin_port)); 1519 else 1520 sprintf(p, "%s", hp); 1521 } 1522 break; 1523 #endif 1524 1525 case S_IFCHR: 1526 sprintf(p, "CHR: "); 1527 p += strlen(p); 1528 goto defprint; 1529 1530 case S_IFBLK: 1531 sprintf(p, "BLK: "); 1532 p += strlen(p); 1533 goto defprint; 1534 1535 #if defined(S_IFIFO) && (!defined(S_IFSOCK) || S_IFIFO != S_IFSOCK) 1536 case S_IFIFO: 1537 sprintf(p, "FIFO: "); 1538 p += strlen(p); 1539 goto defprint; 1540 #endif 1541 1542 #ifdef S_IFDIR 1543 case S_IFDIR: 1544 sprintf(p, "DIR: "); 1545 p += strlen(p); 1546 goto defprint; 1547 #endif 1548 1549 #ifdef S_IFLNK 1550 case S_IFLNK: 1551 sprintf(p, "LNK: "); 1552 p += strlen(p); 1553 goto defprint; 1554 #endif 1555 1556 default: 1557 defprint: 1558 if (sizeof st.st_size > sizeof (long)) 1559 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%qd"; 1560 else 1561 fmtstr = "dev=%d/%d, ino=%d, nlink=%d, u/gid=%d/%d, size=%ld"; 1562 sprintf(p, fmtstr, 1563 major(st.st_dev), minor(st.st_dev), st.st_ino, 1564 st.st_nlink, st.st_uid, st.st_gid, st.st_size); 1565 break; 1566 } 1567 1568 printit: 1569 #ifdef LOG 1570 if (logit) 1571 syslog(LOG_DEBUG, "%s", buf); 1572 else 1573 #endif 1574 printf("%s\n", buf); 1575 } 1576 /* 1577 ** SHORTENSTRING -- return short version of a string 1578 ** 1579 ** If the string is already short, just return it. If it is too 1580 ** long, return the head and tail of the string. 1581 ** 1582 ** Parameters: 1583 ** s -- the string to shorten. 1584 ** m -- the max length of the string. 1585 ** 1586 ** Returns: 1587 ** Either s or a short version of s. 1588 */ 1589 1590 #ifndef MAXSHORTSTR 1591 # define MAXSHORTSTR 203 1592 #endif 1593 1594 char * 1595 shortenstring(s, m) 1596 register const char *s; 1597 int m; 1598 { 1599 int l; 1600 static char buf[MAXSHORTSTR + 1]; 1601 1602 l = strlen(s); 1603 if (l < m) 1604 return (char *) s; 1605 if (m > MAXSHORTSTR) 1606 m = MAXSHORTSTR; 1607 else if (m < 10) 1608 { 1609 if (m < 5) 1610 { 1611 strncpy(buf, s, m); 1612 buf[m] = '\0'; 1613 return buf; 1614 } 1615 strncpy(buf, s, m - 3); 1616 strcpy(buf + m - 3, "..."); 1617 return buf; 1618 } 1619 m = (m - 3) / 2; 1620 strncpy(buf, s, m); 1621 strcpy(buf + m, "..."); 1622 strcpy(buf + m + 3, s + l - m); 1623 return buf; 1624 } 1625 /* 1626 ** SHORTEN_HOSTNAME -- strip local domain information off of hostname. 1627 ** 1628 ** Parameters: 1629 ** host -- the host to shorten (stripped in place). 1630 ** 1631 ** Returns: 1632 ** none. 1633 */ 1634 1635 void 1636 shorten_hostname(host) 1637 char host[]; 1638 { 1639 register char *p; 1640 char *mydom; 1641 int i; 1642 bool canon = FALSE; 1643 1644 /* strip off final dot */ 1645 p = &host[strlen(host) - 1]; 1646 if (*p == '.') 1647 { 1648 *p = '\0'; 1649 canon = TRUE; 1650 } 1651 1652 /* see if there is any domain at all -- if not, we are done */ 1653 p = strchr(host, '.'); 1654 if (p == NULL) 1655 return; 1656 1657 /* yes, we have a domain -- see if it looks like us */ 1658 mydom = macvalue('m', CurEnv); 1659 if (mydom == NULL) 1660 mydom = ""; 1661 i = strlen(++p); 1662 if ((canon ? strcasecmp(p, mydom) : strncasecmp(p, mydom, i)) == 0 && 1663 (mydom[i] == '.' || mydom[i] == '\0')) 1664 *--p = '\0'; 1665 } 1666 /* 1667 ** PROG_OPEN -- open a program for reading 1668 ** 1669 ** Parameters: 1670 ** argv -- the argument list. 1671 ** pfd -- pointer to a place to store the file descriptor. 1672 ** e -- the current envelope. 1673 ** 1674 ** Returns: 1675 ** pid of the process -- -1 if it failed. 1676 */ 1677 1678 int 1679 prog_open(argv, pfd, e) 1680 char **argv; 1681 int *pfd; 1682 ENVELOPE *e; 1683 { 1684 int pid; 1685 int i; 1686 int saveerrno; 1687 int fdv[2]; 1688 char *p, *q; 1689 char buf[MAXLINE + 1]; 1690 extern int DtableSize; 1691 1692 if (pipe(fdv) < 0) 1693 { 1694 syserr("%s: cannot create pipe for stdout", argv[0]); 1695 return -1; 1696 } 1697 pid = fork(); 1698 if (pid < 0) 1699 { 1700 syserr("%s: cannot fork", argv[0]); 1701 close(fdv[0]); 1702 close(fdv[1]); 1703 return -1; 1704 } 1705 if (pid > 0) 1706 { 1707 /* parent */ 1708 close(fdv[1]); 1709 *pfd = fdv[0]; 1710 return pid; 1711 } 1712 1713 /* child -- close stdin */ 1714 close(0); 1715 1716 /* stdout goes back to parent */ 1717 close(fdv[0]); 1718 if (dup2(fdv[1], 1) < 0) 1719 { 1720 syserr("%s: cannot dup2 for stdout", argv[0]); 1721 _exit(EX_OSERR); 1722 } 1723 close(fdv[1]); 1724 1725 /* stderr goes to transcript if available */ 1726 if (e->e_xfp != NULL) 1727 { 1728 if (dup2(fileno(e->e_xfp), 2) < 0) 1729 { 1730 syserr("%s: cannot dup2 for stderr", argv[0]); 1731 _exit(EX_OSERR); 1732 } 1733 } 1734 1735 /* this process has no right to the queue file */ 1736 if (e->e_lockfp != NULL) 1737 close(fileno(e->e_lockfp)); 1738 1739 /* run as default user */ 1740 endpwent(); 1741 setgid(DefGid); 1742 setuid(DefUid); 1743 1744 /* run in some directory */ 1745 if (ProgMailer != NULL) 1746 p = ProgMailer->m_execdir; 1747 else 1748 p = NULL; 1749 for (; p != NULL; p = q) 1750 { 1751 q = strchr(p, ':'); 1752 if (q != NULL) 1753 *q = '\0'; 1754 expand(p, buf, sizeof buf, e); 1755 if (q != NULL) 1756 *q++ = ':'; 1757 if (buf[0] != '\0' && chdir(buf) >= 0) 1758 break; 1759 } 1760 if (p == NULL) 1761 { 1762 /* backup directories */ 1763 if (chdir("/tmp") < 0) 1764 (void) chdir("/"); 1765 } 1766 1767 /* arrange for all the files to be closed */ 1768 for (i = 3; i < DtableSize; i++) 1769 { 1770 register int j; 1771 1772 if ((j = fcntl(i, F_GETFD, 0)) != -1) 1773 (void) fcntl(i, F_SETFD, j | 1); 1774 } 1775 1776 /* now exec the process */ 1777 execve(argv[0], (ARGV_T) argv, (ARGV_T) UserEnviron); 1778 1779 /* woops! failed */ 1780 saveerrno = errno; 1781 syserr("%s: cannot exec", argv[0]); 1782 if (transienterror(saveerrno)) 1783 _exit(EX_OSERR); 1784 _exit(EX_CONFIG); 1785 } 1786 /* 1787 ** GET_COLUMN -- look up a Column in a line buffer 1788 ** 1789 ** Parameters: 1790 ** line -- the raw text line to search. 1791 ** col -- the column number to fetch. 1792 ** delim -- the delimiter between columns. If null, 1793 ** use white space. 1794 ** buf -- the output buffer. 1795 ** 1796 ** Returns: 1797 ** buf if successful. 1798 ** NULL otherwise. 1799 */ 1800 1801 char * 1802 get_column(line, col, delim, buf) 1803 char line[]; 1804 int col; 1805 char delim; 1806 char buf[]; 1807 { 1808 char *p; 1809 char *begin, *end; 1810 int i; 1811 char delimbuf[3]; 1812 1813 if (delim == '\0') 1814 strcpy(delimbuf, "\n\t "); 1815 else 1816 { 1817 delimbuf[0] = delim; 1818 delimbuf[1] = '\0'; 1819 } 1820 1821 p = line; 1822 if (*p == '\0') 1823 return NULL; /* line empty */ 1824 if (*p == delim && col == 0) 1825 return NULL; /* first column empty */ 1826 1827 begin = line; 1828 1829 if (col == 0 && delim == '\0') 1830 { 1831 while (*begin && isspace(*begin)) 1832 begin++; 1833 } 1834 1835 for (i = 0; i < col; i++) 1836 { 1837 if ((begin = strpbrk(begin, delimbuf)) == NULL) 1838 return NULL; /* no such column */ 1839 begin++; 1840 if (delim == '\0') 1841 { 1842 while (*begin && isspace(*begin)) 1843 begin++; 1844 } 1845 } 1846 1847 end = strpbrk(begin, delimbuf); 1848 if (end == NULL) 1849 { 1850 strcpy(buf, begin); 1851 } 1852 else 1853 { 1854 strncpy(buf, begin, end - begin); 1855 buf[end - begin] = '\0'; 1856 } 1857 return buf; 1858 } 1859 /* 1860 ** CLEANSTRCPY -- copy string keeping out bogus characters 1861 ** 1862 ** Parameters: 1863 ** t -- "to" string. 1864 ** f -- "from" string. 1865 ** l -- length of space available in "to" string. 1866 ** 1867 ** Returns: 1868 ** none. 1869 */ 1870 1871 void 1872 cleanstrcpy(t, f, l) 1873 register char *t; 1874 register char *f; 1875 int l; 1876 { 1877 #ifdef LOG 1878 /* check for newlines and log if necessary */ 1879 (void) denlstring(f, TRUE, TRUE); 1880 #endif 1881 1882 l--; 1883 while (l > 0 && *f != '\0') 1884 { 1885 if (isascii(*f) && 1886 (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 1887 { 1888 l--; 1889 *t++ = *f; 1890 } 1891 f++; 1892 } 1893 *t = '\0'; 1894 } 1895 /* 1896 ** DENLSTRING -- convert newlines in a string to spaces 1897 ** 1898 ** Parameters: 1899 ** s -- the input string 1900 ** strict -- if set, don't permit continuation lines. 1901 ** logattacks -- if set, log attempted attacks. 1902 ** 1903 ** Returns: 1904 ** A pointer to a version of the string with newlines 1905 ** mapped to spaces. This should be copied. 1906 */ 1907 1908 char * 1909 denlstring(s, strict, logattacks) 1910 char *s; 1911 bool strict; 1912 bool logattacks; 1913 { 1914 register char *p; 1915 int l; 1916 static char *bp = NULL; 1917 static int bl = 0; 1918 1919 p = s; 1920 while ((p = strchr(p, '\n')) != NULL) 1921 if (strict || (*++p != ' ' && *p != '\t')) 1922 break; 1923 if (p == NULL) 1924 return s; 1925 1926 l = strlen(s) + 1; 1927 if (bl < l) 1928 { 1929 /* allocate more space */ 1930 if (bp != NULL) 1931 free(bp); 1932 bp = xalloc(l); 1933 bl = l; 1934 } 1935 strcpy(bp, s); 1936 for (p = bp; (p = strchr(p, '\n')) != NULL; ) 1937 *p++ = ' '; 1938 1939 /* 1940 #ifdef LOG 1941 if (logattacks) 1942 { 1943 syslog(LOG_NOTICE, "POSSIBLE ATTACK from %s: newline in string \"%s\"", 1944 RealHostName == NULL ? "[UNKNOWN]" : RealHostName, 1945 shortenstring(bp, 80)); 1946 } 1947 #endif 1948 */ 1949 1950 return bp; 1951 } 1952