1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)readcf.c 6.9 (Berkeley) 02/20/93"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include <sys/stat.h> 15 # include <unistd.h> 16 #ifdef NAMED_BIND 17 # include <arpa/nameser.h> 18 # include <resolv.h> 19 #endif 20 21 /* System 5 compatibility */ 22 #ifndef S_ISREG 23 #define S_ISREG(foo) ((foo & S_IFREG) == S_IFREG) 24 #endif 25 #ifndef S_IWGRP 26 #define S_IWGRP 020 27 #endif 28 #ifndef S_IWOTH 29 #define S_IWOTH 002 30 #endif 31 32 /* 33 ** READCF -- read control file. 34 ** 35 ** This routine reads the control file and builds the internal 36 ** form. 37 ** 38 ** The file is formatted as a sequence of lines, each taken 39 ** atomically. The first character of each line describes how 40 ** the line is to be interpreted. The lines are: 41 ** Dxval Define macro x to have value val. 42 ** Cxword Put word into class x. 43 ** Fxfile [fmt] Read file for lines to put into 44 ** class x. Use scanf string 'fmt' 45 ** or "%s" if not present. Fmt should 46 ** only produce one string-valued result. 47 ** Hname: value Define header with field-name 'name' 48 ** and value as specified; this will be 49 ** macro expanded immediately before 50 ** use. 51 ** Sn Use rewriting set n. 52 ** Rlhs rhs Rewrite addresses that match lhs to 53 ** be rhs. 54 ** Mn arg=val... Define mailer. n is the internal name. 55 ** Args specify mailer parameters. 56 ** Oxvalue Set option x to value. 57 ** Pname=value Set precedence name to value. 58 ** Vversioncode Version level of configuration syntax. 59 ** Kmapname mapclass arguments.... 60 ** Define keyed lookup of a given class. 61 ** Arguments are class dependent. 62 ** 63 ** Parameters: 64 ** cfname -- control file name. 65 ** safe -- TRUE if this is the system config file; 66 ** FALSE otherwise. 67 ** e -- the main envelope. 68 ** 69 ** Returns: 70 ** none. 71 ** 72 ** Side Effects: 73 ** Builds several internal tables. 74 */ 75 76 readcf(cfname, safe, e) 77 char *cfname; 78 bool safe; 79 register ENVELOPE *e; 80 { 81 FILE *cf; 82 int ruleset = 0; 83 char *q; 84 char **pv; 85 struct rewrite *rwp = NULL; 86 char *bp; 87 int nfuzzy; 88 char buf[MAXLINE]; 89 register char *p; 90 extern char **prescan(); 91 extern char **copyplist(); 92 struct stat statb; 93 char exbuf[MAXLINE]; 94 char pvpbuf[PSBUFSIZE]; 95 extern char *fgetfolded(); 96 extern char *munchstring(); 97 extern void makemapentry(); 98 99 FileName = cfname; 100 LineNumber = 0; 101 102 cf = fopen(cfname, "r"); 103 if (cf == NULL) 104 { 105 syserr("cannot open"); 106 exit(EX_OSFILE); 107 } 108 109 if (fstat(fileno(cf), &statb) < 0) 110 { 111 syserr("cannot fstat"); 112 exit(EX_OSFILE); 113 } 114 115 if (!S_ISREG(statb.st_mode)) 116 { 117 syserr("not a plain file"); 118 exit(EX_OSFILE); 119 } 120 121 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 122 { 123 if (OpMode == MD_DAEMON || OpMode == MD_FREEZE) 124 fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 125 FileName); 126 #ifdef LOG 127 if (LogLevel > 0) 128 syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", 129 FileName); 130 #endif 131 } 132 133 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 134 { 135 if (bp[0] == '#') 136 { 137 if (bp != buf) 138 free(bp); 139 continue; 140 } 141 142 /* map $ into \201 for macro expansion */ 143 for (p = bp; *p != '\0'; p++) 144 { 145 if (*p == '#' && p > bp && ConfigLevel >= 3) 146 { 147 /* this is an on-line comment */ 148 register char *e; 149 150 switch (*--p & 0377) 151 { 152 case MACROEXPAND: 153 /* it's from $# -- let it go through */ 154 p++; 155 break; 156 157 case '\\': 158 /* it's backslash escaped */ 159 (void) strcpy(p, p + 1); 160 break; 161 162 default: 163 /* delete preceeding white space */ 164 while (isascii(*p) && isspace(*p) && p > bp) 165 p--; 166 if ((e = strchr(++p, '\n')) != NULL) 167 (void) strcpy(p, e); 168 else 169 p[0] = p[1] = '\0'; 170 break; 171 } 172 continue; 173 } 174 175 if (*p != '$') 176 continue; 177 178 if (p[1] == '$') 179 { 180 /* actual dollar sign.... */ 181 (void) strcpy(p, p + 1); 182 continue; 183 } 184 185 /* convert to macro expansion character */ 186 *p = MACROEXPAND; 187 } 188 189 /* interpret this line */ 190 switch (bp[0]) 191 { 192 case '\0': 193 case '#': /* comment */ 194 break; 195 196 case 'R': /* rewriting rule */ 197 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 198 continue; 199 200 if (*p == '\0') 201 { 202 syserr("invalid rewrite line \"%s\"", bp); 203 break; 204 } 205 206 /* allocate space for the rule header */ 207 if (rwp == NULL) 208 { 209 RewriteRules[ruleset] = rwp = 210 (struct rewrite *) xalloc(sizeof *rwp); 211 } 212 else 213 { 214 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 215 rwp = rwp->r_next; 216 } 217 rwp->r_next = NULL; 218 219 /* expand and save the LHS */ 220 *p = '\0'; 221 expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e); 222 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf); 223 nfuzzy = 0; 224 if (rwp->r_lhs != NULL) 225 { 226 register char **ap; 227 228 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 229 230 /* count the number of fuzzy matches in LHS */ 231 for (ap = rwp->r_lhs; *ap != NULL; ap++) 232 { 233 switch (**ap & 0377) 234 { 235 case MATCHZANY: 236 case MATCHANY: 237 case MATCHONE: 238 case MATCHCLASS: 239 case MATCHNCLASS: 240 nfuzzy++; 241 } 242 } 243 } 244 else 245 syserr("R line: null LHS"); 246 247 /* expand and save the RHS */ 248 while (*++p == '\t') 249 continue; 250 q = p; 251 while (*p != '\0' && *p != '\t') 252 p++; 253 *p = '\0'; 254 expand(q, exbuf, &exbuf[sizeof exbuf], e); 255 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf); 256 if (rwp->r_rhs != NULL) 257 { 258 register char **ap; 259 260 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 261 262 /* check no out-of-bounds replacements */ 263 nfuzzy += '0'; 264 for (ap = rwp->r_rhs; *ap != NULL; ap++) 265 { 266 if ((**ap & 0377) != MATCHREPL) 267 continue; 268 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 269 { 270 syserr("replacement $%c out of bounds", 271 (*ap)[1]); 272 } 273 } 274 } 275 else 276 syserr("R line: null RHS"); 277 break; 278 279 case 'S': /* select rewriting set */ 280 ruleset = atoi(&bp[1]); 281 if (ruleset >= MAXRWSETS || ruleset < 0) 282 { 283 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 284 ruleset = 0; 285 } 286 rwp = NULL; 287 break; 288 289 case 'D': /* macro definition */ 290 define(bp[1], newstr(munchstring(&bp[2])), e); 291 break; 292 293 case 'H': /* required header line */ 294 (void) chompheader(&bp[1], TRUE, e); 295 break; 296 297 case 'C': /* word class */ 298 case 'F': /* word class from file */ 299 /* read list of words from argument or file */ 300 if (bp[0] == 'F') 301 { 302 /* read from file */ 303 for (p = &bp[2]; 304 *p != '\0' && !(isascii(*p) && isspace(*p)); 305 p++) 306 continue; 307 if (*p == '\0') 308 p = "%s"; 309 else 310 { 311 *p = '\0'; 312 while (isascii(*++p) && isspace(*p)) 313 continue; 314 } 315 fileclass(bp[1], &bp[2], p, safe); 316 break; 317 } 318 319 /* scan the list of words and set class for all */ 320 for (p = &bp[2]; *p != '\0'; ) 321 { 322 register char *wd; 323 char delim; 324 325 while (*p != '\0' && isascii(*p) && isspace(*p)) 326 p++; 327 wd = p; 328 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 329 p++; 330 delim = *p; 331 *p = '\0'; 332 if (wd[0] != '\0') 333 setclass(bp[1], wd); 334 *p = delim; 335 } 336 break; 337 338 case 'M': /* define mailer */ 339 makemailer(&bp[1]); 340 break; 341 342 case 'O': /* set option */ 343 setoption(bp[1], &bp[2], safe, FALSE); 344 break; 345 346 case 'P': /* set precedence */ 347 if (NumPriorities >= MAXPRIORITIES) 348 { 349 toomany('P', MAXPRIORITIES); 350 break; 351 } 352 for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 353 continue; 354 if (*p == '\0') 355 goto badline; 356 *p = '\0'; 357 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 358 Priorities[NumPriorities].pri_val = atoi(++p); 359 NumPriorities++; 360 break; 361 362 case 'T': /* trusted user(s) */ 363 p = &bp[1]; 364 while (*p != '\0') 365 { 366 while (isascii(*p) && isspace(*p)) 367 p++; 368 q = p; 369 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 370 p++; 371 if (*p != '\0') 372 *p++ = '\0'; 373 if (*q == '\0') 374 continue; 375 for (pv = TrustedUsers; *pv != NULL; pv++) 376 continue; 377 if (pv >= &TrustedUsers[MAXTRUST]) 378 { 379 toomany('T', MAXTRUST); 380 break; 381 } 382 *pv = newstr(q); 383 } 384 break; 385 386 case 'V': /* configuration syntax version */ 387 ConfigLevel = atoi(&bp[1]); 388 break; 389 390 case 'K': 391 makemapentry(&bp[1]); 392 break; 393 394 default: 395 badline: 396 syserr("unknown control line \"%s\"", bp); 397 } 398 if (bp != buf) 399 free(bp); 400 } 401 if (ferror(cf)) 402 { 403 syserr("I/O read error", cfname); 404 exit(EX_OSFILE); 405 } 406 fclose(cf); 407 FileName = NULL; 408 409 if (stab("host", ST_MAP, ST_FIND) == NULL) 410 { 411 /* user didn't initialize: set up host map */ 412 strcpy(buf, "host host"); 413 if (ConfigLevel >= 2) 414 strcat(buf, " -a."); 415 makemapentry(buf); 416 } 417 } 418 /* 419 ** TOOMANY -- signal too many of some option 420 ** 421 ** Parameters: 422 ** id -- the id of the error line 423 ** maxcnt -- the maximum possible values 424 ** 425 ** Returns: 426 ** none. 427 ** 428 ** Side Effects: 429 ** gives a syserr. 430 */ 431 432 toomany(id, maxcnt) 433 char id; 434 int maxcnt; 435 { 436 syserr("too many %c lines, %d max", id, maxcnt); 437 } 438 /* 439 ** FILECLASS -- read members of a class from a file 440 ** 441 ** Parameters: 442 ** class -- class to define. 443 ** filename -- name of file to read. 444 ** fmt -- scanf string to use for match. 445 ** 446 ** Returns: 447 ** none 448 ** 449 ** Side Effects: 450 ** 451 ** puts all lines in filename that match a scanf into 452 ** the named class. 453 */ 454 455 fileclass(class, filename, fmt, safe) 456 int class; 457 char *filename; 458 char *fmt; 459 bool safe; 460 { 461 FILE *f; 462 struct stat stbuf; 463 char buf[MAXLINE]; 464 465 if (stat(filename, &stbuf) < 0) 466 { 467 syserr("fileclass: cannot stat %s", filename); 468 return; 469 } 470 if (!S_ISREG(stbuf.st_mode)) 471 { 472 syserr("fileclass: %s not a regular file", filename); 473 return; 474 } 475 if (!safe && access(filename, R_OK) < 0) 476 { 477 syserr("fileclass: access denied on %s", filename); 478 return; 479 } 480 f = fopen(filename, "r"); 481 if (f == NULL) 482 { 483 syserr("fileclass: cannot open %s", filename); 484 return; 485 } 486 487 while (fgets(buf, sizeof buf, f) != NULL) 488 { 489 register STAB *s; 490 register char *p; 491 # ifdef SCANF 492 char wordbuf[MAXNAME+1]; 493 494 if (sscanf(buf, fmt, wordbuf) != 1) 495 continue; 496 p = wordbuf; 497 # else /* SCANF */ 498 p = buf; 499 # endif /* SCANF */ 500 501 /* 502 ** Break up the match into words. 503 */ 504 505 while (*p != '\0') 506 { 507 register char *q; 508 509 /* strip leading spaces */ 510 while (isascii(*p) && isspace(*p)) 511 p++; 512 if (*p == '\0') 513 break; 514 515 /* find the end of the word */ 516 q = p; 517 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 518 p++; 519 if (*p != '\0') 520 *p++ = '\0'; 521 522 /* enter the word in the symbol table */ 523 s = stab(q, ST_CLASS, ST_ENTER); 524 setbitn(class, s->s_class); 525 } 526 } 527 528 (void) fclose(f); 529 } 530 /* 531 ** MAKEMAILER -- define a new mailer. 532 ** 533 ** Parameters: 534 ** line -- description of mailer. This is in labeled 535 ** fields. The fields are: 536 ** P -- the path to the mailer 537 ** F -- the flags associated with the mailer 538 ** A -- the argv for this mailer 539 ** S -- the sender rewriting set 540 ** R -- the recipient rewriting set 541 ** E -- the eol string 542 ** The first word is the canonical name of the mailer. 543 ** 544 ** Returns: 545 ** none. 546 ** 547 ** Side Effects: 548 ** enters the mailer into the mailer table. 549 */ 550 551 makemailer(line) 552 char *line; 553 { 554 register char *p; 555 register struct mailer *m; 556 register STAB *s; 557 int i; 558 char fcode; 559 auto char *endp; 560 extern int NextMailer; 561 extern char **makeargv(); 562 extern char *munchstring(); 563 extern char *DelimChar; 564 extern long atol(); 565 566 /* allocate a mailer and set up defaults */ 567 m = (struct mailer *) xalloc(sizeof *m); 568 bzero((char *) m, sizeof *m); 569 m->m_eol = "\n"; 570 571 /* collect the mailer name */ 572 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 573 continue; 574 if (*p != '\0') 575 *p++ = '\0'; 576 m->m_name = newstr(line); 577 578 /* now scan through and assign info from the fields */ 579 while (*p != '\0') 580 { 581 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 582 p++; 583 584 /* p now points to field code */ 585 fcode = *p; 586 while (*p != '\0' && *p != '=' && *p != ',') 587 p++; 588 if (*p++ != '=') 589 { 590 syserr("mailer %s: `=' expected", m->m_name); 591 return; 592 } 593 while (isascii(*p) && isspace(*p)) 594 p++; 595 596 /* p now points to the field body */ 597 p = munchstring(p); 598 599 /* install the field into the mailer struct */ 600 switch (fcode) 601 { 602 case 'P': /* pathname */ 603 m->m_mailer = newstr(p); 604 break; 605 606 case 'F': /* flags */ 607 for (; *p != '\0'; p++) 608 if (!(isascii(*p) && isspace(*p))) 609 setbitn(*p, m->m_flags); 610 break; 611 612 case 'S': /* sender rewriting ruleset */ 613 case 'R': /* recipient rewriting ruleset */ 614 i = strtol(p, &endp, 10); 615 if (i < 0 || i >= MAXRWSETS) 616 { 617 syserr("invalid rewrite set, %d max", MAXRWSETS); 618 return; 619 } 620 if (fcode == 'S') 621 m->m_sh_rwset = m->m_se_rwset = i; 622 else 623 m->m_rh_rwset = m->m_re_rwset = i; 624 625 p = endp; 626 if (*p == '/') 627 { 628 i = strtol(p, NULL, 10); 629 if (i < 0 || i >= MAXRWSETS) 630 { 631 syserr("invalid rewrite set, %d max", 632 MAXRWSETS); 633 return; 634 } 635 if (fcode == 'S') 636 m->m_sh_rwset = i; 637 else 638 m->m_rh_rwset = i; 639 } 640 break; 641 642 case 'E': /* end of line string */ 643 m->m_eol = newstr(p); 644 break; 645 646 case 'A': /* argument vector */ 647 m->m_argv = makeargv(p); 648 break; 649 650 case 'M': /* maximum message size */ 651 m->m_maxsize = atol(p); 652 break; 653 654 case 'L': /* maximum line length */ 655 m->m_linelimit = atoi(p); 656 break; 657 } 658 659 p = DelimChar; 660 } 661 662 /* do some heuristic cleanup for back compatibility */ 663 if (bitnset(M_LIMITS, m->m_flags)) 664 { 665 if (m->m_linelimit == 0) 666 m->m_linelimit = SMTPLINELIM; 667 if (ConfigLevel < 2) 668 setbitn(M_7BITS, m->m_flags); 669 } 670 671 if (NextMailer >= MAXMAILERS) 672 { 673 syserr("too many mailers defined (%d max)", MAXMAILERS); 674 return; 675 } 676 677 s = stab(m->m_name, ST_MAILER, ST_ENTER); 678 if (s->s_mailer != NULL) 679 { 680 i = s->s_mailer->m_mno; 681 free(s->s_mailer); 682 } 683 else 684 { 685 i = NextMailer++; 686 } 687 Mailer[i] = s->s_mailer = m; 688 m->m_mno = i; 689 } 690 /* 691 ** MUNCHSTRING -- translate a string into internal form. 692 ** 693 ** Parameters: 694 ** p -- the string to munch. 695 ** 696 ** Returns: 697 ** the munched string. 698 ** 699 ** Side Effects: 700 ** Sets "DelimChar" to point to the string that caused us 701 ** to stop. 702 */ 703 704 char * 705 munchstring(p) 706 register char *p; 707 { 708 register char *q; 709 bool backslash = FALSE; 710 bool quotemode = FALSE; 711 static char buf[MAXLINE]; 712 extern char *DelimChar; 713 714 for (q = buf; *p != '\0'; p++) 715 { 716 if (backslash) 717 { 718 /* everything is roughly literal */ 719 backslash = FALSE; 720 switch (*p) 721 { 722 case 'r': /* carriage return */ 723 *q++ = '\r'; 724 continue; 725 726 case 'n': /* newline */ 727 *q++ = '\n'; 728 continue; 729 730 case 'f': /* form feed */ 731 *q++ = '\f'; 732 continue; 733 734 case 'b': /* backspace */ 735 *q++ = '\b'; 736 continue; 737 } 738 *q++ = *p; 739 } 740 else 741 { 742 if (*p == '\\') 743 backslash = TRUE; 744 else if (*p == '"') 745 quotemode = !quotemode; 746 else if (quotemode || *p != ',') 747 *q++ = *p; 748 else 749 break; 750 } 751 } 752 753 DelimChar = p; 754 *q++ = '\0'; 755 return (buf); 756 } 757 /* 758 ** MAKEARGV -- break up a string into words 759 ** 760 ** Parameters: 761 ** p -- the string to break up. 762 ** 763 ** Returns: 764 ** a char **argv (dynamically allocated) 765 ** 766 ** Side Effects: 767 ** munges p. 768 */ 769 770 char ** 771 makeargv(p) 772 register char *p; 773 { 774 char *q; 775 int i; 776 char **avp; 777 char *argv[MAXPV + 1]; 778 779 /* take apart the words */ 780 i = 0; 781 while (*p != '\0' && i < MAXPV) 782 { 783 q = p; 784 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 785 p++; 786 while (isascii(*p) && isspace(*p)) 787 *p++ = '\0'; 788 argv[i++] = newstr(q); 789 } 790 argv[i++] = NULL; 791 792 /* now make a copy of the argv */ 793 avp = (char **) xalloc(sizeof *avp * i); 794 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 795 796 return (avp); 797 } 798 /* 799 ** PRINTRULES -- print rewrite rules (for debugging) 800 ** 801 ** Parameters: 802 ** none. 803 ** 804 ** Returns: 805 ** none. 806 ** 807 ** Side Effects: 808 ** prints rewrite rules. 809 */ 810 811 printrules() 812 { 813 register struct rewrite *rwp; 814 register int ruleset; 815 816 for (ruleset = 0; ruleset < 10; ruleset++) 817 { 818 if (RewriteRules[ruleset] == NULL) 819 continue; 820 printf("\n----Rule Set %d:", ruleset); 821 822 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 823 { 824 printf("\nLHS:"); 825 printav(rwp->r_lhs); 826 printf("RHS:"); 827 printav(rwp->r_rhs); 828 } 829 } 830 } 831 832 /* 833 ** SETOPTION -- set global processing option 834 ** 835 ** Parameters: 836 ** opt -- option name. 837 ** val -- option value (as a text string). 838 ** safe -- set if this came from a configuration file. 839 ** Some options (if set from the command line) will 840 ** reset the user id to avoid security problems. 841 ** sticky -- if set, don't let other setoptions override 842 ** this value. 843 ** 844 ** Returns: 845 ** none. 846 ** 847 ** Side Effects: 848 ** Sets options as implied by the arguments. 849 */ 850 851 static BITMAP StickyOpt; /* set if option is stuck */ 852 853 854 #ifdef NAMED_BIND 855 856 struct resolverflags 857 { 858 char *rf_name; /* name of the flag */ 859 long rf_bits; /* bits to set/clear */ 860 } ResolverFlags[] = 861 { 862 "debug", RES_DEBUG, 863 "aaonly", RES_AAONLY, 864 "usevc", RES_USEVC, 865 "primary", RES_PRIMARY, 866 "igntc", RES_IGNTC, 867 "recurse", RES_RECURSE, 868 "defnames", RES_DEFNAMES, 869 "stayopen", RES_STAYOPEN, 870 "dnsrch", RES_DNSRCH, 871 NULL, 0 872 }; 873 874 #endif 875 876 setoption(opt, val, safe, sticky) 877 char opt; 878 char *val; 879 bool safe; 880 bool sticky; 881 { 882 register char *p; 883 extern bool atobool(); 884 extern time_t convtime(); 885 extern int QueueLA; 886 extern int RefuseLA; 887 extern bool trusteduser(); 888 extern char *username(); 889 890 if (tTd(37, 1)) 891 printf("setoption %c=%s", opt, val); 892 893 /* 894 ** See if this option is preset for us. 895 */ 896 897 if (bitnset(opt, StickyOpt)) 898 { 899 if (tTd(37, 1)) 900 printf(" (ignored)\n"); 901 return; 902 } 903 904 /* 905 ** Check to see if this option can be specified by this user. 906 */ 907 908 if (!safe && getuid() == 0) 909 safe = TRUE; 910 if (!safe && strchr("bdeEiLmoprsvC8", opt) == NULL) 911 { 912 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 913 { 914 if (tTd(37, 1)) 915 printf(" (unsafe)"); 916 if (getuid() != geteuid()) 917 { 918 if (tTd(37, 1)) 919 printf("(Resetting uid)"); 920 (void) setgid(getgid()); 921 (void) setuid(getuid()); 922 } 923 } 924 } 925 if (tTd(37, 1)) 926 printf("\n"); 927 928 switch (opt) 929 { 930 case '8': /* allow eight-bit input */ 931 EightBit = atobool(val); 932 break; 933 934 case 'A': /* set default alias file */ 935 if (val[0] == '\0') 936 AliasFile = "aliases"; 937 else 938 AliasFile = newstr(val); 939 break; 940 941 case 'a': /* look N minutes for "@:@" in alias file */ 942 if (val[0] == '\0') 943 SafeAlias = 5; 944 else 945 SafeAlias = atoi(val); 946 break; 947 948 case 'B': /* substitution for blank character */ 949 SpaceSub = val[0]; 950 if (SpaceSub == '\0') 951 SpaceSub = ' '; 952 break; 953 954 case 'b': /* minimum number of blocks free on queue fs */ 955 MinBlocksFree = atol(val); 956 break; 957 958 case 'c': /* don't connect to "expensive" mailers */ 959 NoConnect = atobool(val); 960 break; 961 962 case 'C': /* checkpoint every N addresses */ 963 CheckpointInterval = atoi(val); 964 break; 965 966 case 'd': /* delivery mode */ 967 switch (*val) 968 { 969 case '\0': 970 SendMode = SM_DELIVER; 971 break; 972 973 case SM_QUEUE: /* queue only */ 974 #ifndef QUEUE 975 syserr("need QUEUE to set -odqueue"); 976 #endif /* QUEUE */ 977 /* fall through..... */ 978 979 case SM_DELIVER: /* do everything */ 980 case SM_FORK: /* fork after verification */ 981 SendMode = *val; 982 break; 983 984 default: 985 syserr("Unknown delivery mode %c", *val); 986 exit(EX_USAGE); 987 } 988 break; 989 990 case 'D': /* rebuild alias database as needed */ 991 AutoRebuild = atobool(val); 992 break; 993 994 case 'E': /* error message header/header file */ 995 if (*val != '\0') 996 ErrMsgFile = newstr(val); 997 break; 998 999 case 'e': /* set error processing mode */ 1000 switch (*val) 1001 { 1002 case EM_QUIET: /* be silent about it */ 1003 case EM_MAIL: /* mail back */ 1004 case EM_BERKNET: /* do berknet error processing */ 1005 case EM_WRITE: /* write back (or mail) */ 1006 HoldErrs = TRUE; 1007 /* fall through... */ 1008 1009 case EM_PRINT: /* print errors normally (default) */ 1010 ErrorMode = *val; 1011 break; 1012 } 1013 break; 1014 1015 case 'F': /* file mode */ 1016 FileMode = atooct(val) & 0777; 1017 break; 1018 1019 case 'f': /* save Unix-style From lines on front */ 1020 SaveFrom = atobool(val); 1021 break; 1022 1023 case 'G': /* match recipients against GECOS field */ 1024 MatchGecos = atobool(val); 1025 break; 1026 1027 case 'g': /* default gid */ 1028 DefGid = atoi(val); 1029 break; 1030 1031 case 'H': /* help file */ 1032 if (val[0] == '\0') 1033 HelpFile = "sendmail.hf"; 1034 else 1035 HelpFile = newstr(val); 1036 break; 1037 1038 case 'h': /* maximum hop count */ 1039 MaxHopCount = atoi(val); 1040 break; 1041 1042 case 'I': /* use internet domain name server */ 1043 #ifdef NAMED_BIND 1044 UseNameServer = TRUE; 1045 for (p = val; *p != 0; ) 1046 { 1047 bool clearmode; 1048 char *q; 1049 struct resolverflags *rfp; 1050 1051 while (*p == ' ') 1052 p++; 1053 if (*p == '\0') 1054 break; 1055 clearmode = FALSE; 1056 if (*p == '-') 1057 clearmode = TRUE; 1058 else if (*p != '+') 1059 p--; 1060 p++; 1061 q = p; 1062 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1063 p++; 1064 if (*p != '\0') 1065 *p++ = '\0'; 1066 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1067 { 1068 if (strcasecmp(q, rfp->rf_name) == 0) 1069 break; 1070 } 1071 if (clearmode) 1072 _res.options &= ~rfp->rf_bits; 1073 else 1074 _res.options |= rfp->rf_bits; 1075 } 1076 if (tTd(8, 2)) 1077 printf("_res.options = %x\n", _res.options); 1078 #else 1079 usrerr("name server (I option) specified but BIND not compiled in"); 1080 #endif 1081 break; 1082 1083 case 'i': /* ignore dot lines in message */ 1084 IgnrDot = atobool(val); 1085 break; 1086 1087 case 'J': /* .forward search path */ 1088 ForwardPath = newstr(val); 1089 break; 1090 1091 case 'k': /* connection cache size */ 1092 MaxMciCache = atoi(val); 1093 if (MaxMciCache < 0) 1094 MaxMciCache = 0; 1095 break; 1096 1097 case 'K': /* connection cache timeout */ 1098 MciCacheTimeout = convtime(val); 1099 break; 1100 1101 case 'L': /* log level */ 1102 LogLevel = atoi(val); 1103 break; 1104 1105 case 'M': /* define macro */ 1106 define(val[0], newstr(&val[1]), CurEnv); 1107 sticky = FALSE; 1108 break; 1109 1110 case 'm': /* send to me too */ 1111 MeToo = atobool(val); 1112 break; 1113 1114 case 'n': /* validate RHS in newaliases */ 1115 CheckAliases = atobool(val); 1116 break; 1117 1118 case 'o': /* assume old style headers */ 1119 if (atobool(val)) 1120 CurEnv->e_flags |= EF_OLDSTYLE; 1121 else 1122 CurEnv->e_flags &= ~EF_OLDSTYLE; 1123 break; 1124 1125 case 'p': /* select privacy level */ 1126 p = val; 1127 for (;;) 1128 { 1129 register struct prival *pv; 1130 extern struct prival PrivacyValues[]; 1131 1132 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1133 p++; 1134 if (*p == '\0') 1135 break; 1136 val = p; 1137 while (isascii(*p) && isalnum(*p)) 1138 p++; 1139 if (*p != '\0') 1140 *p++ = '\0'; 1141 1142 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1143 { 1144 if (strcasecmp(val, pv->pv_name) == 0) 1145 break; 1146 } 1147 PrivacyFlags |= pv->pv_flag; 1148 } 1149 break; 1150 1151 case 'P': /* postmaster copy address for returned mail */ 1152 PostMasterCopy = newstr(val); 1153 break; 1154 1155 case 'q': /* slope of queue only function */ 1156 QueueFactor = atoi(val); 1157 break; 1158 1159 case 'Q': /* queue directory */ 1160 if (val[0] == '\0') 1161 QueueDir = "mqueue"; 1162 else 1163 QueueDir = newstr(val); 1164 break; 1165 1166 case 'r': /* read timeout */ 1167 ReadTimeout = convtime(val); 1168 break; 1169 1170 case 'S': /* status file */ 1171 if (val[0] == '\0') 1172 StatFile = "sendmail.st"; 1173 else 1174 StatFile = newstr(val); 1175 break; 1176 1177 case 's': /* be super safe, even if expensive */ 1178 SuperSafe = atobool(val); 1179 break; 1180 1181 case 'T': /* queue timeout */ 1182 TimeOut = convtime(val); 1183 break; 1184 1185 case 't': /* time zone name */ 1186 TimeZoneSpec = newstr(val); 1187 break; 1188 1189 case 'U': /* location of user database */ 1190 UdbSpec = newstr(val); 1191 break; 1192 1193 case 'u': /* set default uid */ 1194 DefUid = atoi(val); 1195 setdefuser(); 1196 break; 1197 1198 case 'v': /* run in verbose mode */ 1199 Verbose = atobool(val); 1200 break; 1201 1202 case 'x': /* load avg at which to auto-queue msgs */ 1203 QueueLA = atoi(val); 1204 break; 1205 1206 case 'X': /* load avg at which to auto-reject connections */ 1207 RefuseLA = atoi(val); 1208 break; 1209 1210 case 'y': /* work recipient factor */ 1211 WkRecipFact = atoi(val); 1212 break; 1213 1214 case 'Y': /* fork jobs during queue runs */ 1215 ForkQueueRuns = atobool(val); 1216 break; 1217 1218 case 'z': /* work message class factor */ 1219 WkClassFact = atoi(val); 1220 break; 1221 1222 case 'Z': /* work time factor */ 1223 WkTimeFact = atoi(val); 1224 break; 1225 1226 default: 1227 break; 1228 } 1229 if (sticky) 1230 setbitn(opt, StickyOpt); 1231 return; 1232 } 1233 /* 1234 ** SETCLASS -- set a word into a class 1235 ** 1236 ** Parameters: 1237 ** class -- the class to put the word in. 1238 ** word -- the word to enter 1239 ** 1240 ** Returns: 1241 ** none. 1242 ** 1243 ** Side Effects: 1244 ** puts the word into the symbol table. 1245 */ 1246 1247 setclass(class, word) 1248 int class; 1249 char *word; 1250 { 1251 register STAB *s; 1252 1253 if (tTd(37, 8)) 1254 printf("%s added to class %c\n", word, class); 1255 s = stab(word, ST_CLASS, ST_ENTER); 1256 setbitn(class, s->s_class); 1257 } 1258 /* 1259 ** MAKEMAPENTRY -- create a map entry 1260 ** 1261 ** Parameters: 1262 ** line -- the config file line 1263 ** 1264 ** Returns: 1265 ** TRUE if it successfully entered the map entry. 1266 ** FALSE otherwise (usually syntax error). 1267 ** 1268 ** Side Effects: 1269 ** Enters the map into the dictionary. 1270 */ 1271 1272 void 1273 makemapentry(line) 1274 char *line; 1275 { 1276 register char *p; 1277 char *mapname; 1278 char *classname; 1279 register STAB *map; 1280 STAB *class; 1281 1282 for (p = line; isascii(*p) && isspace(*p); p++) 1283 continue; 1284 if (!(isascii(*p) && isalnum(*p))) 1285 { 1286 syserr("readcf: config K line: no map name"); 1287 return; 1288 } 1289 1290 mapname = p; 1291 while (isascii(*++p) && isalnum(*p)) 1292 continue; 1293 if (*p != '\0') 1294 *p++ = '\0'; 1295 while (isascii(*p) && isspace(*p)) 1296 p++; 1297 if (!(isascii(*p) && isalnum(*p))) 1298 { 1299 syserr("readcf: config K line, map %s: no map class", mapname); 1300 return; 1301 } 1302 classname = p; 1303 while (isascii(*++p) && isalnum(*p)) 1304 continue; 1305 if (*p != '\0') 1306 *p++ = '\0'; 1307 while (isascii(*p) && isspace(*p)) 1308 p++; 1309 1310 /* look up the class */ 1311 class = stab(classname, ST_MAPCLASS, ST_FIND); 1312 if (class == NULL) 1313 { 1314 syserr("readcf: map %s: class %s not available", mapname, classname); 1315 return; 1316 } 1317 1318 /* enter the map */ 1319 map = stab(mapname, ST_MAP, ST_ENTER); 1320 map->s_map.map_class = &class->s_mapclass; 1321 1322 if ((*class->s_mapclass.map_init)(&map->s_map, mapname, p)) 1323 map->s_map.map_flags |= MF_VALID; 1324 } 1325