1: /* 2: ** Sendmail 3: ** Copyright (c) 1983 Eric P. Allman 4: ** Berkeley, California 5: ** 6: ** Copyright (c) 1983 Regents of the University of California. 7: ** All rights reserved. The Berkeley software License Agreement 8: ** specifies the terms and conditions for redistribution. 9: */ 10: 11: #if !defined(lint) && !defined(NOSCCS) 12: static char SccsId[] = "@(#)util.c 5.8.3 (2.11BSD GTE) 1997/10/3"; 13: #endif 14: 15: # include <stdio.h> 16: # include <sys/types.h> 17: # include <sys/stat.h> 18: # include <sysexits.h> 19: # include <ctype.h> 20: # include "sendmail.h" 21: 22: /* 23: ** STRIPQUOTES -- Strip quotes & quote bits from a string. 24: ** 25: ** Runs through a string and strips off unquoted quote 26: ** characters and quote bits. This is done in place. 27: ** 28: ** Parameters: 29: ** s -- the string to strip. 30: ** qf -- if set, remove actual `` " '' characters 31: ** as well as the quote bits. 32: ** 33: ** Returns: 34: ** none. 35: ** 36: ** Side Effects: 37: ** none. 38: ** 39: ** Called By: 40: ** deliver 41: */ 42: 43: stripquotes(s, qf) 44: char *s; 45: bool qf; 46: { 47: register char *p; 48: register char *q; 49: register char c; 50: 51: if (s == NULL) 52: return; 53: 54: for (p = q = s; (c = *p++) != '\0'; ) 55: { 56: if (c != '"' || !qf) 57: *q++ = c & 0177; 58: } 59: *q = '\0'; 60: } 61: /* 62: ** QSTRLEN -- give me the string length assuming 0200 bits add a char 63: ** 64: ** Parameters: 65: ** s -- the string to measure. 66: ** 67: ** Reurns: 68: ** The length of s, including space for backslash escapes. 69: ** 70: ** Side Effects: 71: ** none. 72: */ 73: 74: qstrlen(s) 75: register char *s; 76: { 77: register int l = 0; 78: register char c; 79: 80: while ((c = *s++) != '\0') 81: { 82: if (bitset(0200, c)) 83: l++; 84: l++; 85: } 86: return (l); 87: } 88: /* 89: ** CAPITALIZE -- return a copy of a string, properly capitalized. 90: ** 91: ** Parameters: 92: ** s -- the string to capitalize. 93: ** 94: ** Returns: 95: ** a pointer to a properly capitalized string. 96: ** 97: ** Side Effects: 98: ** none. 99: */ 100: 101: char * 102: capitalize(s) 103: register char *s; 104: { 105: static char buf[50]; 106: register char *p; 107: 108: p = buf; 109: 110: for (;;) 111: { 112: while (!isalpha(*s) && *s != '\0') 113: *p++ = *s++; 114: if (*s == '\0') 115: break; 116: *p++ = toupper(*s++); 117: while (isalpha(*s)) 118: *p++ = *s++; 119: } 120: 121: *p = '\0'; 122: return (buf); 123: } 124: /* 125: ** XALLOC -- Allocate memory and bitch wildly on failure. 126: ** 127: ** THIS IS A CLUDGE. This should be made to give a proper 128: ** error -- but after all, what can we do? 129: ** 130: ** Parameters: 131: ** sz -- size of area to allocate. 132: ** 133: ** Returns: 134: ** pointer to data region. 135: ** 136: ** Side Effects: 137: ** Memory is allocated. 138: */ 139: 140: char * 141: xalloc(sz) 142: register int sz; 143: { 144: register char *p; 145: 146: p = (char *)malloc((unsigned) sz); 147: if (p == NULL) 148: { 149: syserr("Out of memory!!"); 150: abort(); 151: /* exit(EX_UNAVAILABLE); */ 152: } 153: return (p); 154: } 155: /* 156: ** COPYPLIST -- copy list of pointers. 157: ** 158: ** This routine is the equivalent of newstr for lists of 159: ** pointers. 160: ** 161: ** Parameters: 162: ** list -- list of pointers to copy. 163: ** Must be NULL terminated. 164: ** copycont -- if TRUE, copy the contents of the vector 165: ** (which must be a string) also. 166: ** 167: ** Returns: 168: ** a copy of 'list'. 169: ** 170: ** Side Effects: 171: ** none. 172: */ 173: 174: char ** 175: copyplist(list, copycont) 176: char **list; 177: bool copycont; 178: { 179: register char **vp; 180: register char **newvp; 181: 182: for (vp = list; *vp != NULL; vp++) 183: continue; 184: 185: vp++; 186: 187: newvp = (char **) xalloc((int) (vp - list) * sizeof *vp); 188: bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp); 189: 190: if (copycont) 191: { 192: for (vp = newvp; *vp != NULL; vp++) 193: *vp = newstr(*vp); 194: } 195: 196: return (newvp); 197: } 198: /* 199: ** PRINTAV -- print argument vector. 200: ** 201: ** Parameters: 202: ** av -- argument vector. 203: ** 204: ** Returns: 205: ** none. 206: ** 207: ** Side Effects: 208: ** prints av. 209: */ 210: 211: printav(av) 212: register char **av; 213: { 214: while (*av != NULL) 215: { 216: if (tTd(0, 44)) 217: printf("\n\t%08x=", *av); 218: else 219: (void) putchar(' '); 220: xputs(*av++); 221: } 222: (void) putchar('\n'); 223: } 224: /* 225: ** LOWER -- turn letter into lower case. 226: ** 227: ** Parameters: 228: ** c -- character to turn into lower case. 229: ** 230: ** Returns: 231: ** c, in lower case. 232: ** 233: ** Side Effects: 234: ** none. 235: */ 236: 237: char 238: lower(c) 239: register char c; 240: { 241: if (isascii(c) && isupper(c)) 242: c = c - 'A' + 'a'; 243: return (c); 244: } 245: /* 246: ** XPUTS -- put string doing control escapes. 247: ** 248: ** Parameters: 249: ** s -- string to put. 250: ** 251: ** Returns: 252: ** none. 253: ** 254: ** Side Effects: 255: ** output to stdout 256: */ 257: 258: xputs(s) 259: register char *s; 260: { 261: register char c; 262: 263: if (s == NULL) 264: { 265: printf("<null>"); 266: return; 267: } 268: (void) putchar('"'); 269: while ((c = *s++) != '\0') 270: { 271: if (!isascii(c)) 272: { 273: (void) putchar('\\'); 274: c &= 0177; 275: } 276: if (c < 040 || c >= 0177) 277: { 278: (void) putchar('^'); 279: c ^= 0100; 280: } 281: (void) putchar(c); 282: } 283: (void) putchar('"'); 284: (void) fflush(stdout); 285: } 286: /* 287: ** MAKELOWER -- Translate a line into lower case 288: ** 289: ** Parameters: 290: ** p -- the string to translate. If NULL, return is 291: ** immediate. 292: ** 293: ** Returns: 294: ** none. 295: ** 296: ** Side Effects: 297: ** String pointed to by p is translated to lower case. 298: ** 299: ** Called By: 300: ** parse 301: */ 302: 303: makelower(p) 304: register char *p; 305: { 306: register char c; 307: 308: if (p == NULL) 309: return; 310: for (; (c = *p) != '\0'; p++) 311: if (isascii(c) && isupper(c)) 312: *p = c - 'A' + 'a'; 313: } 314: /* 315: ** SAMEWORD -- return TRUE if the words are the same 316: ** 317: ** Ignores case. 318: ** 319: ** Parameters: 320: ** a, b -- the words to compare. 321: ** 322: ** Returns: 323: ** TRUE if a & b match exactly (modulo case) 324: ** FALSE otherwise. 325: ** 326: ** Side Effects: 327: ** none. 328: */ 329: 330: bool 331: sameword(a, b) 332: register char *a, *b; 333: { 334: char ca, cb; 335: 336: do 337: { 338: ca = *a++; 339: cb = *b++; 340: if (isascii(ca) && isupper(ca)) 341: ca = ca - 'A' + 'a'; 342: if (isascii(cb) && isupper(cb)) 343: cb = cb - 'A' + 'a'; 344: } while (ca != '\0' && ca == cb); 345: return (ca == cb); 346: } 347: /* 348: ** BUILDFNAME -- build full name from gecos style entry. 349: ** 350: ** This routine interprets the strange entry that would appear 351: ** in the GECOS field of the password file. 352: ** 353: ** Parameters: 354: ** p -- name to build. 355: ** login -- the login name of this user (for &). 356: ** buf -- place to put the result. 357: ** 358: ** Returns: 359: ** none. 360: ** 361: ** Side Effects: 362: ** none. 363: */ 364: 365: buildfname(p, login, buf) 366: register char *p; 367: char *login; 368: char *buf; 369: { 370: register char *bp = buf; 371: 372: if (*p == '*') 373: p++; 374: while (*p != '\0' && *p != ',' && *p != ';' && *p != '%') 375: { 376: if (*p == '&') 377: { 378: (void) strcpy(bp, login); 379: *bp = toupper(*bp); 380: while (*bp != '\0') 381: bp++; 382: p++; 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 -- uid to compare against. 395: ** mode -- mode bits that must match. 396: ** 397: ** Returns: 398: ** TRUE if fn exists, is owned by uid, and matches mode. 399: ** FALSE otherwise. 400: ** 401: ** Side Effects: 402: ** none. 403: */ 404: 405: bool 406: safefile(fn, uid, mode) 407: char *fn; 408: int uid; 409: int mode; 410: { 411: struct stat stbuf; 412: 413: if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid && 414: (stbuf.st_mode & mode) == mode) 415: return (TRUE); 416: errno = 0; 417: return (FALSE); 418: } 419: /* 420: ** FIXCRLF -- fix <CR><LF> in line. 421: ** 422: ** Looks for the <CR><LF> combination and turns it into the 423: ** UNIX canonical <NL> character. It only takes one line, 424: ** i.e., it is assumed that the first <NL> found is the end 425: ** of the line. 426: ** 427: ** Parameters: 428: ** line -- the line to fix. 429: ** stripnl -- if true, strip the newline also. 430: ** 431: ** Returns: 432: ** none. 433: ** 434: ** Side Effects: 435: ** line is changed in place. 436: */ 437: 438: fixcrlf(line, stripnl) 439: char *line; 440: bool stripnl; 441: { 442: register char *p; 443: 444: p = index(line, '\n'); 445: if (p == NULL) 446: return; 447: if (p[-1] == '\r') 448: p--; 449: if (!stripnl) 450: *p++ = '\n'; 451: *p = '\0'; 452: } 453: /* 454: ** DFOPEN -- determined file open 455: ** 456: ** This routine has the semantics of fopen, except that it will 457: ** keep trying a few times to make this happen. The idea is that 458: ** on very loaded systems, we may run out of resources (inodes, 459: ** whatever), so this tries to get around it. 460: */ 461: 462: FILE * 463: dfopen(filename, mode) 464: char *filename; 465: char *mode; 466: { 467: register int tries; 468: register FILE *fp; 469: 470: for (tries = 0; tries < 10; tries++) 471: { 472: sleep((unsigned) (10 * tries)); 473: errno = 0; 474: fp = fopen(filename, mode); 475: if (fp != NULL) 476: break; 477: if (errno != ENFILE && errno != EINTR) 478: break; 479: } 480: errno = 0; 481: return (fp); 482: } 483: /* 484: ** PUTLINE -- put a line like fputs obeying SMTP conventions 485: ** 486: ** This routine always guarantees outputing a newline (or CRLF, 487: ** as appropriate) at the end of the string. 488: ** 489: ** Parameters: 490: ** l -- line to put. 491: ** fp -- file to put it onto. 492: ** m -- the mailer used to control output. 493: ** 494: ** Returns: 495: ** none 496: ** 497: ** Side Effects: 498: ** output of l to fp. 499: */ 500: 501: # define SMTPLINELIM 990 /* maximum line length */ 502: 503: putline(l, fp, m) 504: register char *l; 505: FILE *fp; 506: MAILER *m; 507: { 508: register char *p; 509: char svchar; 510: 511: /* strip out 0200 bits -- these can look like TELNET protocol */ 512: if (bitnset(M_LIMITS, m->m_flags)) 513: { 514: p = l; 515: while ((*p++ &= ~0200) != 0) 516: continue; 517: } 518: 519: do 520: { 521: /* find the end of the line */ 522: p = index(l, '\n'); 523: if (p == NULL) 524: p = &l[strlen(l)]; 525: 526: /* check for line overflow */ 527: while ((p - l) > SMTPLINELIM && bitnset(M_LIMITS, m->m_flags)) 528: { 529: register char *q = &l[SMTPLINELIM - 1]; 530: 531: svchar = *q; 532: *q = '\0'; 533: if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) 534: (void) putc('.', fp); 535: fputs(l, fp); 536: (void) putc('!', fp); 537: fputs(m->m_eol, fp); 538: *q = svchar; 539: l = q; 540: } 541: 542: /* output last part */ 543: svchar = *p; 544: *p = '\0'; 545: if (l[0] == '.' && bitnset(M_XDOT, m->m_flags)) 546: (void) putc('.', fp); 547: fputs(l, fp); 548: fputs(m->m_eol, fp); 549: *p = svchar; 550: l = p; 551: if (*l == '\n') 552: l++; 553: } while (l[0] != '\0'); 554: } 555: /* 556: ** XUNLINK -- unlink a file, doing logging as appropriate. 557: ** 558: ** Parameters: 559: ** f -- name of file to unlink. 560: ** 561: ** Returns: 562: ** none. 563: ** 564: ** Side Effects: 565: ** f is unlinked. 566: */ 567: 568: xunlink(f) 569: char *f; 570: { 571: register int i; 572: 573: # ifdef LOG 574: if (LogLevel > 20) 575: syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f); 576: # endif LOG 577: 578: i = unlink(f); 579: # ifdef LOG 580: if (i < 0 && LogLevel > 21) 581: syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno); 582: # endif LOG 583: } 584: /* 585: ** SFGETS -- "safe" fgets -- times out and ignores random interrupts. 586: ** 587: ** Parameters: 588: ** buf -- place to put the input line. 589: ** siz -- size of buf. 590: ** fp -- file to read from. 591: ** 592: ** Returns: 593: ** NULL on error (including timeout). This will also leave 594: ** buf containing a null string. 595: ** buf otherwise. 596: ** 597: ** Side Effects: 598: ** none. 599: */ 600: 601: static jmp_buf CtxReadTimeout; 602: 603: #ifndef ETIMEDOUT 604: #define ETIMEDOUT EINTR 605: #endif 606: 607: char * 608: sfgets(buf, siz, fp) 609: char *buf; 610: int siz; 611: FILE *fp; 612: { 613: register EVENT *ev = NULL; 614: register char *p; 615: int readtimeout(); 616: 617: /* set the timeout */ 618: if (ReadTimeout != 0) 619: { 620: if (setjmp(CtxReadTimeout) != 0) 621: { 622: errno = ETIMEDOUT; 623: syserr("net timeout"); 624: buf[0] = '\0'; 625: return (NULL); 626: } 627: ev = setevent((time_t) ReadTimeout, readtimeout, 0); 628: } 629: 630: /* try to read */ 631: p = NULL; 632: while (p == NULL && !feof(fp) && !ferror(fp)) 633: { 634: errno = 0; 635: p = fgets(buf, siz, fp); 636: if (errno == EINTR) 637: clearerr(fp); 638: } 639: 640: /* clear the event if it has not sprung */ 641: clrevent(ev); 642: 643: /* clean up the books and exit */ 644: LineNumber++; 645: if (p == NULL) 646: { 647: buf[0] = '\0'; 648: return (NULL); 649: } 650: for (p = buf; *p != '\0'; p++) 651: *p &= ~0200; 652: return (buf); 653: } 654: 655: readtimeout() 656: { 657: longjmp(CtxReadTimeout, 1); 658: } 659: /* 660: ** FGETFOLDED -- like fgets, but know about folded lines. 661: ** 662: ** Parameters: 663: ** buf -- place to put result. 664: ** n -- bytes available. 665: ** f -- file to read from. 666: ** 667: ** Returns: 668: ** buf on success, NULL on error or EOF. 669: ** 670: ** Side Effects: 671: ** buf gets lines from f, with continuation lines (lines 672: ** with leading white space) appended. CRLF's are mapped 673: ** into single newlines. Any trailing NL is stripped. 674: */ 675: 676: char * 677: fgetfolded(buf, n, f) 678: char *buf; 679: int n; 680: register FILE *f; 681: { 682: register char *p = buf; 683: register int i; 684: 685: n--; 686: while ((i = getc(f)) != EOF) 687: { 688: if (i == '\r') 689: { 690: i = getc(f); 691: if (i != '\n') 692: { 693: if (i != EOF) 694: (void) ungetc(i, f); 695: i = '\r'; 696: } 697: } 698: if (--n > 0) 699: *p++ = i; 700: if (i == '\n') 701: { 702: LineNumber++; 703: i = getc(f); 704: if (i != EOF) 705: (void) ungetc(i, f); 706: if (i != ' ' && i != '\t') 707: { 708: *--p = '\0'; 709: return (buf); 710: } 711: } 712: } 713: return (NULL); 714: } 715: /* 716: ** CURTIME -- return current time. 717: ** 718: ** Parameters: 719: ** none. 720: ** 721: ** Returns: 722: ** the current time. 723: ** 724: ** Side Effects: 725: ** none. 726: */ 727: 728: time_t 729: curtime() 730: { 731: auto time_t t; 732: 733: (void) time(&t); 734: return (t); 735: } 736: /* 737: ** ATOBOOL -- convert a string representation to boolean. 738: ** 739: ** Defaults to "TRUE" 740: ** 741: ** Parameters: 742: ** s -- string to convert. Takes "tTyY" as true, 743: ** others as false. 744: ** 745: ** Returns: 746: ** A boolean representation of the string. 747: ** 748: ** Side Effects: 749: ** none. 750: */ 751: 752: bool 753: atobool(s) 754: register char *s; 755: { 756: if (*s == '\0' || index("tTyY", *s) != NULL) 757: return (TRUE); 758: return (FALSE); 759: } 760: /* 761: ** ATOOCT -- convert a string representation to octal. 762: ** 763: ** Parameters: 764: ** s -- string to convert. 765: ** 766: ** Returns: 767: ** An integer representing the string interpreted as an 768: ** octal number. 769: ** 770: ** Side Effects: 771: ** none. 772: */ 773: 774: atooct(s) 775: register char *s; 776: { 777: register int i = 0; 778: 779: while (*s >= '0' && *s <= '7') 780: i = (i << 3) | (*s++ - '0'); 781: return (i); 782: } 783: /* 784: ** WAITFOR -- wait for a particular process id. 785: ** 786: ** Parameters: 787: ** pid -- process id to wait for. 788: ** 789: ** Returns: 790: ** status of pid. 791: ** -1 if pid never shows up. 792: ** 793: ** Side Effects: 794: ** none. 795: */ 796: 797: waitfor(pid) 798: int pid; 799: { 800: auto int st; 801: register int i; 802: 803: do 804: { 805: errno = 0; 806: i = wait(&st); 807: } while ((i >= 0 || errno == EINTR) && i != pid); 808: if (i < 0) 809: st = -1; 810: return (st); 811: } 812: /* 813: ** BITINTERSECT -- tell if two bitmaps intersect 814: ** 815: ** Parameters: 816: ** a, b -- the bitmaps in question 817: ** 818: ** Returns: 819: ** TRUE if they have a non-null intersection 820: ** FALSE otherwise 821: ** 822: ** Side Effects: 823: ** none. 824: */ 825: 826: bool 827: bitintersect(a, b) 828: BITMAP a; 829: BITMAP b; 830: { 831: register int i; 832: 833: for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 834: if ((a[i] & b[i]) != 0) 835: return (TRUE); 836: return (FALSE); 837: } 838: /* 839: ** BITZEROP -- tell if a bitmap is all zero 840: ** 841: ** Parameters: 842: ** map -- the bit map to check 843: ** 844: ** Returns: 845: ** TRUE if map is all zero. 846: ** FALSE if there are any bits set in map. 847: ** 848: ** Side Effects: 849: ** none. 850: */ 851: 852: bool 853: bitzerop(map) 854: BITMAP map; 855: { 856: register int i; 857: 858: for (i = BITMAPBYTES / sizeof (int); --i >= 0; ) 859: if (map[i] != 0) 860: return (FALSE); 861: return (TRUE); 862: } 863: /* 864: ** CLEANSTRCPY -- copy string keeping out bogus characters 865: ** 866: ** Parameters: 867: ** t -- "to" string. 868: ** f -- "from" string. 869: ** l -- length of space available in "to" string. 870: ** 871: ** Returns: 872: ** none. 873: */ 874: 875: void 876: cleanstrcpy(t, f, l) 877: register char *t; 878: register char *f; 879: int l; 880: { 881: #ifdef LOG 882: /* check for newlines and log if necessary */ 883: (void) denlstring(f); 884: #endif 885: 886: l--; 887: while (l > 0 && *f != '\0') 888: { 889: if (isascii(*f) && 890: (isalnum(*f) || strchr("!#$%&'*+-./^_`{|}~", *f) != NULL)) 891: { 892: l--; 893: *t++ = *f; 894: } 895: f++; 896: } 897: *t = '\0'; 898: } 899: /* 900: ** DENLSTRING -- convert newlines in a string to spaces 901: ** 902: ** Parameters: 903: ** s -- the input string 904: ** 905: ** Returns: 906: ** A pointer to a version of the string with newlines 907: ** mapped to spaces. This should be copied. 908: */ 909: 910: char * 911: denlstring(s) 912: char *s; 913: { 914: register char *p; 915: int l; 916: static char *bp = NULL; 917: static int bl = 0; 918: extern char *macvalue(); 919: 920: if (strchr(s, '\n') == NULL) 921: return s; 922: 923: l = strlen(s) + 1; 924: if (bl < l) 925: { 926: /* allocate more space */ 927: if (bp != NULL) 928: free(bp); 929: bp = xalloc(l); 930: bl = l; 931: } 932: strcpy(bp, s); 933: for (p = bp; (p = strchr(p, '\n')) != NULL; ) 934: *p++ = ' '; 935: 936: #ifdef LOG 937: /* 938: * V5 doesn't have IDENT capabilities so there is no macro '_'. 939: * Instead we use the less "trusted" (but better than nothing) 'f' 940: * macro value. 941: */ 942: p = macvalue('f', CurEnv); 943: syslog(LOG_ALERT, "POSSIBLE ATTACK from %s: newline in string \"%s\"", 944: p == NULL ? "[UNKNOWN]" : p, bp); 945: #endif 946: 947: return bp; 948: }