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