1 /* 2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 3 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 4 * Copyright (c) 1988, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * By using this file, you agree to the terms and conditions set 8 * forth in the LICENSE file which can be found at the top level of 9 * the sendmail distribution. 10 * 11 */ 12 13 #ifndef lint 14 static char sccsid[] = "@(#)readcf.c 8.230 (Berkeley) 6/5/98"; 15 #endif /* not lint */ 16 17 # include "sendmail.h" 18 # include <grp.h> 19 #if NAMED_BIND 20 # include <resolv.h> 21 #endif 22 23 /* 24 ** READCF -- read configuration file. 25 ** 26 ** This routine reads the configuration file and builds the internal 27 ** form. 28 ** 29 ** The file is formatted as a sequence of lines, each taken 30 ** atomically. The first character of each line describes how 31 ** the line is to be interpreted. The lines are: 32 ** Dxval Define macro x to have value val. 33 ** Cxword Put word into class x. 34 ** Fxfile [fmt] Read file for lines to put into 35 ** class x. Use scanf string 'fmt' 36 ** or "%s" if not present. Fmt should 37 ** only produce one string-valued result. 38 ** Hname: value Define header with field-name 'name' 39 ** and value as specified; this will be 40 ** macro expanded immediately before 41 ** use. 42 ** Sn Use rewriting set n. 43 ** Rlhs rhs Rewrite addresses that match lhs to 44 ** be rhs. 45 ** Mn arg=val... Define mailer. n is the internal name. 46 ** Args specify mailer parameters. 47 ** Oxvalue Set option x to value. 48 ** Pname=value Set precedence name to value. 49 ** Vversioncode[/vendorcode] 50 ** Version level/vendor name of 51 ** configuration syntax. 52 ** Kmapname mapclass arguments.... 53 ** Define keyed lookup of a given class. 54 ** Arguments are class dependent. 55 ** Eenvar=value Set the environment value to the given value. 56 ** 57 ** Parameters: 58 ** cfname -- configuration file name. 59 ** safe -- TRUE if this is the system config file; 60 ** FALSE otherwise. 61 ** e -- the main envelope. 62 ** 63 ** Returns: 64 ** none. 65 ** 66 ** Side Effects: 67 ** Builds several internal tables. 68 */ 69 70 void 71 readcf(cfname, safe, e) 72 char *cfname; 73 bool safe; 74 register ENVELOPE *e; 75 { 76 FILE *cf; 77 int ruleset = 0; 78 char *q; 79 struct rewrite *rwp = NULL; 80 char *bp; 81 auto char *ep; 82 int nfuzzy; 83 char *file; 84 bool optional; 85 int mid; 86 register char *p; 87 int sff = SFF_OPENASROOT; 88 struct stat statb; 89 char buf[MAXLINE]; 90 char exbuf[MAXLINE]; 91 char pvpbuf[MAXLINE + MAXATOM]; 92 static char *null_list[1] = { NULL }; 93 extern char **copyplist __P((char **, bool)); 94 extern char *munchstring __P((char *, char **, int)); 95 extern void fileclass __P((int, char *, char *, bool, bool)); 96 extern void toomany __P((int, int)); 97 extern void translate_dollars __P((char *)); 98 extern void inithostmaps __P((void)); 99 100 FileName = cfname; 101 LineNumber = 0; 102 103 if (DontLockReadFiles) 104 sff |= SFF_NOLOCK; 105 cf = safefopen(cfname, O_RDONLY, 0444, sff); 106 if (cf == NULL) 107 { 108 syserr("cannot open"); 109 exit(EX_OSFILE); 110 } 111 112 if (fstat(fileno(cf), &statb) < 0) 113 { 114 syserr("cannot fstat"); 115 exit(EX_OSFILE); 116 } 117 118 if (!S_ISREG(statb.st_mode)) 119 { 120 syserr("not a plain file"); 121 exit(EX_OSFILE); 122 } 123 124 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 125 { 126 if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS) 127 fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 128 FileName); 129 if (LogLevel > 0) 130 sm_syslog(LOG_CRIT, NOQID, 131 "%s: WARNING: dangerous write permissions", 132 FileName); 133 } 134 135 #ifdef XLA 136 xla_zero(); 137 #endif 138 139 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 140 { 141 if (bp[0] == '#') 142 { 143 if (bp != buf) 144 free(bp); 145 continue; 146 } 147 148 /* do macro expansion mappings */ 149 translate_dollars(bp); 150 151 /* interpret this line */ 152 errno = 0; 153 switch (bp[0]) 154 { 155 case '\0': 156 case '#': /* comment */ 157 break; 158 159 case 'R': /* rewriting rule */ 160 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 161 continue; 162 163 if (*p == '\0') 164 { 165 syserr("invalid rewrite line \"%s\" (tab expected)", bp); 166 break; 167 } 168 169 /* allocate space for the rule header */ 170 if (rwp == NULL) 171 { 172 RewriteRules[ruleset] = rwp = 173 (struct rewrite *) xalloc(sizeof *rwp); 174 } 175 else 176 { 177 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 178 rwp = rwp->r_next; 179 } 180 rwp->r_next = NULL; 181 182 /* expand and save the LHS */ 183 *p = '\0'; 184 expand(&bp[1], exbuf, sizeof exbuf, e); 185 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, 186 sizeof pvpbuf, NULL, NULL); 187 nfuzzy = 0; 188 if (rwp->r_lhs != NULL) 189 { 190 register char **ap; 191 192 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 193 194 /* count the number of fuzzy matches in LHS */ 195 for (ap = rwp->r_lhs; *ap != NULL; ap++) 196 { 197 char *botch; 198 199 botch = NULL; 200 switch (**ap & 0377) 201 { 202 case MATCHZANY: 203 case MATCHANY: 204 case MATCHONE: 205 case MATCHCLASS: 206 case MATCHNCLASS: 207 nfuzzy++; 208 break; 209 210 case MATCHREPL: 211 botch = "$0-$9"; 212 break; 213 214 case CANONUSER: 215 botch = "$:"; 216 break; 217 218 case CALLSUBR: 219 botch = "$>"; 220 break; 221 222 case CONDIF: 223 botch = "$?"; 224 break; 225 226 case CONDFI: 227 botch = "$."; 228 break; 229 230 case HOSTBEGIN: 231 botch = "$["; 232 break; 233 234 case HOSTEND: 235 botch = "$]"; 236 break; 237 238 case LOOKUPBEGIN: 239 botch = "$("; 240 break; 241 242 case LOOKUPEND: 243 botch = "$)"; 244 break; 245 } 246 if (botch != NULL) 247 syserr("Inappropriate use of %s on LHS", 248 botch); 249 } 250 } 251 else 252 { 253 syserr("R line: null LHS"); 254 rwp->r_lhs = null_list; 255 } 256 257 /* expand and save the RHS */ 258 while (*++p == '\t') 259 continue; 260 q = p; 261 while (*p != '\0' && *p != '\t') 262 p++; 263 *p = '\0'; 264 expand(q, exbuf, sizeof exbuf, e); 265 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, 266 sizeof pvpbuf, NULL, NULL); 267 if (rwp->r_rhs != NULL) 268 { 269 register char **ap; 270 271 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 272 273 /* check no out-of-bounds replacements */ 274 nfuzzy += '0'; 275 for (ap = rwp->r_rhs; *ap != NULL; ap++) 276 { 277 char *botch; 278 279 botch = NULL; 280 switch (**ap & 0377) 281 { 282 case MATCHREPL: 283 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 284 { 285 syserr("replacement $%c out of bounds", 286 (*ap)[1]); 287 } 288 break; 289 290 case MATCHZANY: 291 botch = "$*"; 292 break; 293 294 case MATCHANY: 295 botch = "$+"; 296 break; 297 298 case MATCHONE: 299 botch = "$-"; 300 break; 301 302 case MATCHCLASS: 303 botch = "$="; 304 break; 305 306 case MATCHNCLASS: 307 botch = "$~"; 308 break; 309 } 310 if (botch != NULL) 311 syserr("Inappropriate use of %s on RHS", 312 botch); 313 } 314 } 315 else 316 { 317 syserr("R line: null RHS"); 318 rwp->r_rhs = null_list; 319 } 320 break; 321 322 case 'S': /* select rewriting set */ 323 expand(&bp[1], exbuf, sizeof exbuf, e); 324 ruleset = strtorwset(exbuf, NULL, ST_ENTER); 325 if (ruleset < 0) 326 break; 327 rwp = RewriteRules[ruleset]; 328 if (rwp != NULL) 329 { 330 if (OpMode == MD_TEST || tTd(37, 1)) 331 printf("WARNING: Ruleset %s has multiple definitions\n", 332 &bp[1]); 333 while (rwp->r_next != NULL) 334 rwp = rwp->r_next; 335 } 336 break; 337 338 case 'D': /* macro definition */ 339 mid = macid(&bp[1], &ep); 340 p = munchstring(ep, NULL, '\0'); 341 define(mid, newstr(p), e); 342 break; 343 344 case 'H': /* required header line */ 345 (void) chompheader(&bp[1], TRUE, NULL, e); 346 break; 347 348 case 'C': /* word class */ 349 case 'T': /* trusted user (set class `t') */ 350 if (bp[0] == 'C') 351 { 352 mid = macid(&bp[1], &ep); 353 expand(ep, exbuf, sizeof exbuf, e); 354 p = exbuf; 355 } 356 else 357 { 358 mid = 't'; 359 p = &bp[1]; 360 } 361 while (*p != '\0') 362 { 363 register char *wd; 364 char delim; 365 366 while (*p != '\0' && isascii(*p) && isspace(*p)) 367 p++; 368 wd = p; 369 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 370 p++; 371 delim = *p; 372 *p = '\0'; 373 if (wd[0] != '\0') 374 setclass(mid, wd); 375 *p = delim; 376 } 377 break; 378 379 case 'F': /* word class from file */ 380 mid = macid(&bp[1], &ep); 381 for (p = ep; isascii(*p) && isspace(*p); ) 382 p++; 383 if (p[0] == '-' && p[1] == 'o') 384 { 385 optional = TRUE; 386 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 387 p++; 388 while (isascii(*p) && isspace(*p)) 389 p++; 390 } 391 else 392 optional = FALSE; 393 file = p; 394 if (*file == '|') 395 p = "%s"; 396 else 397 { 398 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 399 p++; 400 if (*p == '\0') 401 p = "%s"; 402 else 403 { 404 *p = '\0'; 405 while (isascii(*++p) && isspace(*p)) 406 continue; 407 } 408 } 409 fileclass(mid, file, p, safe, optional); 410 break; 411 412 #ifdef XLA 413 case 'L': /* extended load average description */ 414 xla_init(&bp[1]); 415 break; 416 #endif 417 418 #if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) 419 case 'L': /* lookup macro */ 420 case 'G': /* lookup class */ 421 /* reserved for Sun -- NIS+ database lookup */ 422 if (VendorCode != VENDOR_SUN) 423 goto badline; 424 sun_lg_config_line(bp, e); 425 break; 426 #endif 427 428 case 'M': /* define mailer */ 429 makemailer(&bp[1]); 430 break; 431 432 case 'O': /* set option */ 433 setoption(bp[1], &bp[2], safe, FALSE, e); 434 break; 435 436 case 'P': /* set precedence */ 437 if (NumPriorities >= MAXPRIORITIES) 438 { 439 toomany('P', MAXPRIORITIES); 440 break; 441 } 442 for (p = &bp[1]; *p != '\0' && *p != '='; p++) 443 continue; 444 if (*p == '\0') 445 goto badline; 446 *p = '\0'; 447 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 448 Priorities[NumPriorities].pri_val = atoi(++p); 449 NumPriorities++; 450 break; 451 452 case 'V': /* configuration syntax version */ 453 for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 454 continue; 455 if (!isascii(*p) || !isdigit(*p)) 456 { 457 syserr("invalid argument to V line: \"%.20s\"", 458 &bp[1]); 459 break; 460 } 461 ConfigLevel = strtol(p, &ep, 10); 462 463 /* 464 ** Do heuristic tweaking for back compatibility. 465 */ 466 467 if (ConfigLevel >= 5) 468 { 469 /* level 5 configs have short name in $w */ 470 p = macvalue('w', e); 471 if (p != NULL && (p = strchr(p, '.')) != NULL) 472 *p = '\0'; 473 define('w', macvalue('w', e), e); 474 } 475 if (ConfigLevel >= 6) 476 { 477 ColonOkInAddr = FALSE; 478 } 479 480 /* 481 ** Look for vendor code. 482 */ 483 484 if (*ep++ == '/') 485 { 486 extern bool setvendor __P((char *)); 487 488 /* extract vendor code */ 489 for (p = ep; isascii(*p) && isalpha(*p); ) 490 p++; 491 *p = '\0'; 492 493 if (!setvendor(ep)) 494 syserr("invalid V line vendor code: \"%s\"", 495 ep); 496 } 497 break; 498 499 case 'K': 500 expand(&bp[1], exbuf, sizeof exbuf, e); 501 (void) makemapentry(exbuf); 502 break; 503 504 case 'E': 505 p = strchr(bp, '='); 506 if (p != NULL) 507 *p++ = '\0'; 508 setuserenv(&bp[1], p); 509 break; 510 511 default: 512 badline: 513 syserr("unknown configuration line \"%s\"", bp); 514 } 515 if (bp != buf) 516 free(bp); 517 } 518 if (ferror(cf)) 519 { 520 syserr("I/O read error"); 521 exit(EX_OSFILE); 522 } 523 fclose(cf); 524 FileName = NULL; 525 526 /* initialize host maps from local service tables */ 527 inithostmaps(); 528 529 /* determine if we need to do special name-server frotz */ 530 { 531 int nmaps; 532 char *maptype[MAXMAPSTACK]; 533 short mapreturn[MAXMAPACTIONS]; 534 535 nmaps = switch_map_find("hosts", maptype, mapreturn); 536 UseNameServer = FALSE; 537 if (nmaps > 0 && nmaps <= MAXMAPSTACK) 538 { 539 register int mapno; 540 541 for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++) 542 { 543 if (strcmp(maptype[mapno], "dns") == 0) 544 UseNameServer = TRUE; 545 } 546 } 547 548 #ifdef HESIOD 549 nmaps = switch_map_find("passwd", maptype, mapreturn); 550 UseHesiod = FALSE; 551 if (nmaps > 0 && nmaps <= MAXMAPSTACK) 552 { 553 register int mapno; 554 555 for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++) 556 { 557 if (strcmp(maptype[mapno], "hesiod") == 0) 558 UseHesiod = TRUE; 559 } 560 } 561 #endif 562 } 563 } 564 /* 565 ** TRANSLATE_DOLLARS -- convert $x into internal form 566 ** 567 ** Actually does all appropriate pre-processing of a config line 568 ** to turn it into internal form. 569 ** 570 ** Parameters: 571 ** bp -- the buffer to translate. 572 ** 573 ** Returns: 574 ** None. The buffer is translated in place. Since the 575 ** translations always make the buffer shorter, this is 576 ** safe without a size parameter. 577 */ 578 579 void 580 translate_dollars(bp) 581 char *bp; 582 { 583 register char *p; 584 auto char *ep; 585 586 for (p = bp; *p != '\0'; p++) 587 { 588 if (*p == '#' && p > bp && ConfigLevel >= 3) 589 { 590 /* this is an on-line comment */ 591 register char *e; 592 593 switch (*--p & 0377) 594 { 595 case MACROEXPAND: 596 /* it's from $# -- let it go through */ 597 p++; 598 break; 599 600 case '\\': 601 /* it's backslash escaped */ 602 (void) strcpy(p, p + 1); 603 break; 604 605 default: 606 /* delete preceeding white space */ 607 while (isascii(*p) && isspace(*p) && 608 *p != '\n' && p > bp) 609 p--; 610 if ((e = strchr(++p, '\n')) != NULL) 611 (void) strcpy(p, e); 612 else 613 *p-- = '\0'; 614 break; 615 } 616 continue; 617 } 618 619 if (*p != '$' || p[1] == '\0') 620 continue; 621 622 if (p[1] == '$') 623 { 624 /* actual dollar sign.... */ 625 (void) strcpy(p, p + 1); 626 continue; 627 } 628 629 /* convert to macro expansion character */ 630 *p++ = MACROEXPAND; 631 632 /* special handling for $=, $~, $&, and $? */ 633 if (*p == '=' || *p == '~' || *p == '&' || *p == '?') 634 p++; 635 636 /* convert macro name to code */ 637 *p = macid(p, &ep); 638 if (ep != p) 639 strcpy(p + 1, ep); 640 } 641 642 /* strip trailing white space from the line */ 643 while (--p > bp && isascii(*p) && isspace(*p)) 644 *p = '\0'; 645 } 646 /* 647 ** TOOMANY -- signal too many of some option 648 ** 649 ** Parameters: 650 ** id -- the id of the error line 651 ** maxcnt -- the maximum possible values 652 ** 653 ** Returns: 654 ** none. 655 ** 656 ** Side Effects: 657 ** gives a syserr. 658 */ 659 660 void 661 toomany(id, maxcnt) 662 int id; 663 int maxcnt; 664 { 665 syserr("too many %c lines, %d max", id, maxcnt); 666 } 667 /* 668 ** FILECLASS -- read members of a class from a file 669 ** 670 ** Parameters: 671 ** class -- class to define. 672 ** filename -- name of file to read. 673 ** fmt -- scanf string to use for match. 674 ** safe -- if set, this is a safe read. 675 ** optional -- if set, it is not an error for the file to 676 ** not exist. 677 ** 678 ** Returns: 679 ** none 680 ** 681 ** Side Effects: 682 ** 683 ** puts all lines in filename that match a scanf into 684 ** the named class. 685 */ 686 687 void 688 fileclass(class, filename, fmt, safe, optional) 689 int class; 690 char *filename; 691 char *fmt; 692 bool safe; 693 bool optional; 694 { 695 FILE *f; 696 int sff; 697 pid_t pid; 698 register char *p; 699 char buf[MAXLINE]; 700 701 if (tTd(37, 2)) 702 printf("fileclass(%s, fmt=%s)\n", filename, fmt); 703 704 if (filename[0] == '|') 705 { 706 auto int fd; 707 int i; 708 char *argv[MAXPV + 1]; 709 710 i = 0; 711 for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t")) 712 { 713 if (i >= MAXPV) 714 break; 715 argv[i++] = p; 716 } 717 argv[i] = NULL; 718 pid = prog_open(argv, &fd, CurEnv); 719 if (pid < 0) 720 f = NULL; 721 else 722 f = fdopen(fd, "r"); 723 } 724 else 725 { 726 pid = -1; 727 sff = SFF_REGONLY; 728 if (!bitset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail)) 729 sff |= SFF_SAFEDIRPATH; 730 if (!bitset(DBS_LINKEDCLASSFILEINWRITABLEDIR, DontBlameSendmail)) 731 sff |= SFF_NOWLINK; 732 if (safe) 733 sff |= SFF_OPENASROOT; 734 if (DontLockReadFiles) 735 sff |= SFF_NOLOCK; 736 f = safefopen(filename, O_RDONLY, 0, sff); 737 } 738 if (f == NULL) 739 { 740 if (!optional) 741 syserr("fileclass: cannot open %s", filename); 742 return; 743 } 744 745 while (fgets(buf, sizeof buf, f) != NULL) 746 { 747 register char *p; 748 # if SCANF 749 char wordbuf[MAXLINE + 1]; 750 # endif 751 752 if (buf[0] == '#') 753 continue; 754 # if SCANF 755 if (sscanf(buf, fmt, wordbuf) != 1) 756 continue; 757 p = wordbuf; 758 # else /* SCANF */ 759 p = buf; 760 # endif /* SCANF */ 761 762 /* 763 ** Break up the match into words. 764 */ 765 766 while (*p != '\0') 767 { 768 register char *q; 769 770 /* strip leading spaces */ 771 while (isascii(*p) && isspace(*p)) 772 p++; 773 if (*p == '\0') 774 break; 775 776 /* find the end of the word */ 777 q = p; 778 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 779 p++; 780 if (*p != '\0') 781 *p++ = '\0'; 782 783 /* enter the word in the symbol table */ 784 setclass(class, q); 785 } 786 } 787 788 (void) fclose(f); 789 if (pid > 0) 790 (void) waitfor(pid); 791 } 792 /* 793 ** MAKEMAILER -- define a new mailer. 794 ** 795 ** Parameters: 796 ** line -- description of mailer. This is in labeled 797 ** fields. The fields are: 798 ** A -- the argv for this mailer 799 ** C -- the character set for MIME conversions 800 ** D -- the directory to run in 801 ** E -- the eol string 802 ** F -- the flags associated with the mailer 803 ** L -- the maximum line length 804 ** M -- the maximum message size 805 ** N -- the niceness at which to run 806 ** P -- the path to the mailer 807 ** R -- the recipient rewriting set 808 ** S -- the sender rewriting set 809 ** T -- the mailer type (for DSNs) 810 ** U -- the uid to run as 811 ** The first word is the canonical name of the mailer. 812 ** 813 ** Returns: 814 ** none. 815 ** 816 ** Side Effects: 817 ** enters the mailer into the mailer table. 818 */ 819 820 void 821 makemailer(line) 822 char *line; 823 { 824 register char *p; 825 register struct mailer *m; 826 register STAB *s; 827 int i; 828 char fcode; 829 auto char *endp; 830 extern int NextMailer; 831 extern char **makeargv __P((char *)); 832 extern char *munchstring __P((char *, char **, int)); 833 834 /* allocate a mailer and set up defaults */ 835 m = (struct mailer *) xalloc(sizeof *m); 836 bzero((char *) m, sizeof *m); 837 838 /* collect the mailer name */ 839 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 840 continue; 841 if (*p != '\0') 842 *p++ = '\0'; 843 if (line[0] == '\0') 844 syserr("name required for mailer"); 845 m->m_name = newstr(line); 846 847 /* now scan through and assign info from the fields */ 848 while (*p != '\0') 849 { 850 auto char *delimptr; 851 852 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 853 p++; 854 855 /* p now points to field code */ 856 fcode = *p; 857 while (*p != '\0' && *p != '=' && *p != ',') 858 p++; 859 if (*p++ != '=') 860 { 861 syserr("mailer %s: `=' expected", m->m_name); 862 return; 863 } 864 while (isascii(*p) && isspace(*p)) 865 p++; 866 867 /* p now points to the field body */ 868 p = munchstring(p, &delimptr, ','); 869 870 /* install the field into the mailer struct */ 871 switch (fcode) 872 { 873 case 'P': /* pathname */ 874 if (*p == '\0') 875 syserr("mailer %s: empty path name", m->m_name); 876 m->m_mailer = newstr(p); 877 break; 878 879 case 'F': /* flags */ 880 for (; *p != '\0'; p++) 881 if (!(isascii(*p) && isspace(*p))) 882 setbitn(*p, m->m_flags); 883 break; 884 885 case 'S': /* sender rewriting ruleset */ 886 case 'R': /* recipient rewriting ruleset */ 887 i = strtorwset(p, &endp, ST_ENTER); 888 if (i < 0) 889 return; 890 if (fcode == 'S') 891 m->m_sh_rwset = m->m_se_rwset = i; 892 else 893 m->m_rh_rwset = m->m_re_rwset = i; 894 895 p = endp; 896 if (*p++ == '/') 897 { 898 i = strtorwset(p, NULL, ST_ENTER); 899 if (i < 0) 900 return; 901 if (fcode == 'S') 902 m->m_sh_rwset = i; 903 else 904 m->m_rh_rwset = i; 905 } 906 break; 907 908 case 'E': /* end of line string */ 909 if (*p == '\0') 910 syserr("mailer %s: null end-of-line string", 911 m->m_name); 912 m->m_eol = newstr(p); 913 break; 914 915 case 'A': /* argument vector */ 916 if (*p == '\0') 917 syserr("mailer %s: null argument vector", 918 m->m_name); 919 m->m_argv = makeargv(p); 920 break; 921 922 case 'M': /* maximum message size */ 923 m->m_maxsize = atol(p); 924 break; 925 926 case 'L': /* maximum line length */ 927 m->m_linelimit = atoi(p); 928 if (m->m_linelimit < 0) 929 m->m_linelimit = 0; 930 break; 931 932 case 'N': /* run niceness */ 933 m->m_nice = atoi(p); 934 break; 935 936 case 'D': /* working directory */ 937 if (*p == '\0') 938 syserr("mailer %s: null working directory", 939 m->m_name); 940 m->m_execdir = newstr(p); 941 break; 942 943 case 'C': /* default charset */ 944 if (*p == '\0') 945 syserr("mailer %s: null charset", m->m_name); 946 m->m_defcharset = newstr(p); 947 break; 948 949 case 'T': /* MTA-Name/Address/Diagnostic types */ 950 /* extract MTA name type; default to "dns" */ 951 m->m_mtatype = newstr(p); 952 p = strchr(m->m_mtatype, '/'); 953 if (p != NULL) 954 { 955 *p++ = '\0'; 956 if (*p == '\0') 957 p = NULL; 958 } 959 if (*m->m_mtatype == '\0') 960 m->m_mtatype = "dns"; 961 962 /* extract address type; default to "rfc822" */ 963 m->m_addrtype = p; 964 if (p != NULL) 965 p = strchr(p, '/'); 966 if (p != NULL) 967 { 968 *p++ = '\0'; 969 if (*p == '\0') 970 p = NULL; 971 } 972 if (m->m_addrtype == NULL || *m->m_addrtype == '\0') 973 m->m_addrtype = "rfc822"; 974 975 /* extract diagnostic type; default to "smtp" */ 976 m->m_diagtype = p; 977 if (m->m_diagtype == NULL || *m->m_diagtype == '\0') 978 m->m_diagtype = "smtp"; 979 break; 980 981 case 'U': /* user id */ 982 if (isascii(*p) && !isdigit(*p)) 983 { 984 char *q = p; 985 struct passwd *pw; 986 987 while (*p != '\0' && isascii(*p) && 988 (isalnum(*p) || strchr("-_", *p) != NULL)) 989 p++; 990 while (isascii(*p) && isspace(*p)) 991 *p++ = '\0'; 992 if (*p != '\0') 993 *p++ = '\0'; 994 if (*q == '\0') 995 syserr("mailer %s: null user name", 996 m->m_name); 997 pw = sm_getpwnam(q); 998 if (pw == NULL) 999 syserr("readcf: mailer U= flag: unknown user %s", q); 1000 else 1001 { 1002 m->m_uid = pw->pw_uid; 1003 m->m_gid = pw->pw_gid; 1004 } 1005 } 1006 else 1007 { 1008 auto char *q; 1009 1010 m->m_uid = strtol(p, &q, 0); 1011 p = q; 1012 while (isascii(*p) && isspace(*p)) 1013 p++; 1014 if (*p != '\0') 1015 p++; 1016 } 1017 while (isascii(*p) && isspace(*p)) 1018 p++; 1019 if (*p == '\0') 1020 break; 1021 if (isascii(*p) && !isdigit(*p)) 1022 { 1023 char *q = p; 1024 struct group *gr; 1025 1026 while (isascii(*p) && isalnum(*p)) 1027 p++; 1028 *p++ = '\0'; 1029 if (*q == '\0') 1030 syserr("mailer %s: null group name", 1031 m->m_name); 1032 gr = getgrnam(q); 1033 if (gr == NULL) 1034 syserr("readcf: mailer U= flag: unknown group %s", q); 1035 else 1036 m->m_gid = gr->gr_gid; 1037 } 1038 else 1039 { 1040 m->m_gid = strtol(p, NULL, 0); 1041 } 1042 break; 1043 } 1044 1045 p = delimptr; 1046 } 1047 1048 /* do some rationality checking */ 1049 if (m->m_argv == NULL) 1050 { 1051 syserr("M%s: A= argument required", m->m_name); 1052 return; 1053 } 1054 if (m->m_mailer == NULL) 1055 { 1056 syserr("M%s: P= argument required", m->m_name); 1057 return; 1058 } 1059 1060 if (NextMailer >= MAXMAILERS) 1061 { 1062 syserr("too many mailers defined (%d max)", MAXMAILERS); 1063 return; 1064 } 1065 1066 /* do some heuristic cleanup for back compatibility */ 1067 if (bitnset(M_LIMITS, m->m_flags)) 1068 { 1069 if (m->m_linelimit == 0) 1070 m->m_linelimit = SMTPLINELIM; 1071 if (ConfigLevel < 2) 1072 setbitn(M_7BITS, m->m_flags); 1073 } 1074 1075 if (strcmp(m->m_mailer, "[IPC]") == 0 || 1076 strcmp(m->m_mailer, "[TCP]") == 0) 1077 { 1078 if (m->m_mtatype == NULL) 1079 m->m_mtatype = "dns"; 1080 if (m->m_addrtype == NULL) 1081 m->m_addrtype = "rfc822"; 1082 if (m->m_diagtype == NULL) 1083 m->m_diagtype = "smtp"; 1084 } 1085 1086 if (strcmp(m->m_mailer, "[FILE]") == 0) 1087 { 1088 /* Use the second argument for filename */ 1089 if (m->m_argv[0] == NULL || m->m_argv[1] == NULL || 1090 m->m_argv[2] != NULL) 1091 { 1092 syserr("M%s: too %s parameters for [FILE] mailer", 1093 m->m_name, 1094 (m->m_argv[0] == NULL || 1095 m->m_argv[1] == NULL) ? "few" : "many"); 1096 } 1097 else if (strcmp(m->m_argv[0], "FILE") != 0) 1098 { 1099 syserr("M%s: first argument in [FILE] mailer must be FILE", 1100 m->m_name); 1101 } 1102 } 1103 1104 if (m->m_eol == NULL) 1105 { 1106 char **pp; 1107 1108 /* default for SMTP is \r\n; use \n for local delivery */ 1109 for (pp = m->m_argv; *pp != NULL; pp++) 1110 { 1111 char *p; 1112 1113 for (p = *pp; *p != '\0'; ) 1114 { 1115 if ((*p++ & 0377) == MACROEXPAND && *p == 'u') 1116 break; 1117 } 1118 if (*p != '\0') 1119 break; 1120 } 1121 if (*pp == NULL) 1122 m->m_eol = "\r\n"; 1123 else 1124 m->m_eol = "\n"; 1125 } 1126 1127 /* enter the mailer into the symbol table */ 1128 s = stab(m->m_name, ST_MAILER, ST_ENTER); 1129 if (s->s_mailer != NULL) 1130 { 1131 i = s->s_mailer->m_mno; 1132 free(s->s_mailer); 1133 } 1134 else 1135 { 1136 i = NextMailer++; 1137 } 1138 Mailer[i] = s->s_mailer = m; 1139 m->m_mno = i; 1140 } 1141 /* 1142 ** MUNCHSTRING -- translate a string into internal form. 1143 ** 1144 ** Parameters: 1145 ** p -- the string to munch. 1146 ** delimptr -- if non-NULL, set to the pointer of the 1147 ** field delimiter character. 1148 ** delim -- the delimiter for the field. 1149 ** 1150 ** Returns: 1151 ** the munched string. 1152 */ 1153 1154 char * 1155 munchstring(p, delimptr, delim) 1156 register char *p; 1157 char **delimptr; 1158 int delim; 1159 { 1160 register char *q; 1161 bool backslash = FALSE; 1162 bool quotemode = FALSE; 1163 static char buf[MAXLINE]; 1164 1165 for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++) 1166 { 1167 if (backslash) 1168 { 1169 /* everything is roughly literal */ 1170 backslash = FALSE; 1171 switch (*p) 1172 { 1173 case 'r': /* carriage return */ 1174 *q++ = '\r'; 1175 continue; 1176 1177 case 'n': /* newline */ 1178 *q++ = '\n'; 1179 continue; 1180 1181 case 'f': /* form feed */ 1182 *q++ = '\f'; 1183 continue; 1184 1185 case 'b': /* backspace */ 1186 *q++ = '\b'; 1187 continue; 1188 } 1189 *q++ = *p; 1190 } 1191 else 1192 { 1193 if (*p == '\\') 1194 backslash = TRUE; 1195 else if (*p == '"') 1196 quotemode = !quotemode; 1197 else if (quotemode || *p != delim) 1198 *q++ = *p; 1199 else 1200 break; 1201 } 1202 } 1203 1204 if (delimptr != NULL) 1205 *delimptr = p; 1206 *q++ = '\0'; 1207 return (buf); 1208 } 1209 /* 1210 ** MAKEARGV -- break up a string into words 1211 ** 1212 ** Parameters: 1213 ** p -- the string to break up. 1214 ** 1215 ** Returns: 1216 ** a char **argv (dynamically allocated) 1217 ** 1218 ** Side Effects: 1219 ** munges p. 1220 */ 1221 1222 char ** 1223 makeargv(p) 1224 register char *p; 1225 { 1226 char *q; 1227 int i; 1228 char **avp; 1229 char *argv[MAXPV + 1]; 1230 1231 /* take apart the words */ 1232 i = 0; 1233 while (*p != '\0' && i < MAXPV) 1234 { 1235 q = p; 1236 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1237 p++; 1238 while (isascii(*p) && isspace(*p)) 1239 *p++ = '\0'; 1240 argv[i++] = newstr(q); 1241 } 1242 argv[i++] = NULL; 1243 1244 /* now make a copy of the argv */ 1245 avp = (char **) xalloc(sizeof *avp * i); 1246 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 1247 1248 return (avp); 1249 } 1250 /* 1251 ** PRINTRULES -- print rewrite rules (for debugging) 1252 ** 1253 ** Parameters: 1254 ** none. 1255 ** 1256 ** Returns: 1257 ** none. 1258 ** 1259 ** Side Effects: 1260 ** prints rewrite rules. 1261 */ 1262 1263 void 1264 printrules() 1265 { 1266 register struct rewrite *rwp; 1267 register int ruleset; 1268 1269 for (ruleset = 0; ruleset < 10; ruleset++) 1270 { 1271 if (RewriteRules[ruleset] == NULL) 1272 continue; 1273 printf("\n----Rule Set %d:", ruleset); 1274 1275 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 1276 { 1277 printf("\nLHS:"); 1278 printav(rwp->r_lhs); 1279 printf("RHS:"); 1280 printav(rwp->r_rhs); 1281 } 1282 } 1283 } 1284 /* 1285 ** PRINTMAILER -- print mailer structure (for debugging) 1286 ** 1287 ** Parameters: 1288 ** m -- the mailer to print 1289 ** 1290 ** Returns: 1291 ** none. 1292 */ 1293 1294 void 1295 printmailer(m) 1296 register MAILER *m; 1297 { 1298 int j; 1299 1300 printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=", 1301 m->m_mno, m->m_name, 1302 m->m_mailer, m->m_se_rwset, m->m_sh_rwset, 1303 m->m_re_rwset, m->m_rh_rwset, m->m_maxsize, 1304 (int) m->m_uid, (int) m->m_gid); 1305 for (j = '\0'; j <= '\177'; j++) 1306 if (bitnset(j, m->m_flags)) 1307 (void) putchar(j); 1308 printf(" L=%d E=", m->m_linelimit); 1309 xputs(m->m_eol); 1310 if (m->m_defcharset != NULL) 1311 printf(" C=%s", m->m_defcharset); 1312 printf(" T=%s/%s/%s", 1313 m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype, 1314 m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype, 1315 m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype); 1316 if (m->m_argv != NULL) 1317 { 1318 char **a = m->m_argv; 1319 1320 printf(" A="); 1321 while (*a != NULL) 1322 { 1323 if (a != m->m_argv) 1324 printf(" "); 1325 xputs(*a++); 1326 } 1327 } 1328 printf("\n"); 1329 } 1330 /* 1331 ** SETOPTION -- set global processing option 1332 ** 1333 ** Parameters: 1334 ** opt -- option name. 1335 ** val -- option value (as a text string). 1336 ** safe -- set if this came from a configuration file. 1337 ** Some options (if set from the command line) will 1338 ** reset the user id to avoid security problems. 1339 ** sticky -- if set, don't let other setoptions override 1340 ** this value. 1341 ** e -- the main envelope. 1342 ** 1343 ** Returns: 1344 ** none. 1345 ** 1346 ** Side Effects: 1347 ** Sets options as implied by the arguments. 1348 */ 1349 1350 static BITMAP StickyOpt; /* set if option is stuck */ 1351 extern void settimeout __P((char *, char *)); 1352 1353 1354 #if NAMED_BIND 1355 1356 struct resolverflags 1357 { 1358 char *rf_name; /* name of the flag */ 1359 long rf_bits; /* bits to set/clear */ 1360 } ResolverFlags[] = 1361 { 1362 { "debug", RES_DEBUG }, 1363 { "aaonly", RES_AAONLY }, 1364 { "usevc", RES_USEVC }, 1365 { "primary", RES_PRIMARY }, 1366 { "igntc", RES_IGNTC }, 1367 { "recurse", RES_RECURSE }, 1368 { "defnames", RES_DEFNAMES }, 1369 { "stayopen", RES_STAYOPEN }, 1370 { "dnsrch", RES_DNSRCH }, 1371 { "true", 0 }, /* avoid error on old syntax */ 1372 { NULL, 0 } 1373 }; 1374 1375 #endif 1376 1377 struct optioninfo 1378 { 1379 char *o_name; /* long name of option */ 1380 u_char o_code; /* short name of option */ 1381 bool o_safe; /* safe for random people to use */ 1382 } OptionTab[] = 1383 { 1384 { "SevenBitInput", '7', TRUE }, 1385 #if MIME8TO7 1386 { "EightBitMode", '8', TRUE }, 1387 #endif 1388 { "AliasFile", 'A', FALSE }, 1389 { "AliasWait", 'a', FALSE }, 1390 { "BlankSub", 'B', FALSE }, 1391 { "MinFreeBlocks", 'b', TRUE }, 1392 { "CheckpointInterval", 'C', TRUE }, 1393 { "HoldExpensive", 'c', FALSE }, 1394 { "AutoRebuildAliases", 'D', FALSE }, 1395 { "DeliveryMode", 'd', TRUE }, 1396 { "ErrorHeader", 'E', FALSE }, 1397 { "ErrorMode", 'e', TRUE }, 1398 { "TempFileMode", 'F', FALSE }, 1399 { "SaveFromLine", 'f', FALSE }, 1400 { "MatchGECOS", 'G', FALSE }, 1401 { "HelpFile", 'H', FALSE }, 1402 { "MaxHopCount", 'h', FALSE }, 1403 { "ResolverOptions", 'I', FALSE }, 1404 { "IgnoreDots", 'i', TRUE }, 1405 { "ForwardPath", 'J', FALSE }, 1406 { "SendMimeErrors", 'j', TRUE }, 1407 { "ConnectionCacheSize", 'k', FALSE }, 1408 { "ConnectionCacheTimeout", 'K', FALSE }, 1409 { "UseErrorsTo", 'l', FALSE }, 1410 { "LogLevel", 'L', TRUE }, 1411 { "MeToo", 'm', TRUE }, 1412 { "CheckAliases", 'n', FALSE }, 1413 { "OldStyleHeaders", 'o', TRUE }, 1414 { "DaemonPortOptions", 'O', FALSE }, 1415 { "PrivacyOptions", 'p', TRUE }, 1416 { "PostmasterCopy", 'P', FALSE }, 1417 { "QueueFactor", 'q', FALSE }, 1418 { "QueueDirectory", 'Q', FALSE }, 1419 { "DontPruneRoutes", 'R', FALSE }, 1420 { "Timeout", 'r', FALSE }, 1421 { "StatusFile", 'S', FALSE }, 1422 { "SuperSafe", 's', TRUE }, 1423 { "QueueTimeout", 'T', FALSE }, 1424 { "TimeZoneSpec", 't', FALSE }, 1425 { "UserDatabaseSpec", 'U', FALSE }, 1426 { "DefaultUser", 'u', FALSE }, 1427 { "FallbackMXhost", 'V', FALSE }, 1428 { "Verbose", 'v', TRUE }, 1429 { "TryNullMXList", 'w', FALSE }, 1430 { "QueueLA", 'x', FALSE }, 1431 { "RefuseLA", 'X', FALSE }, 1432 { "RecipientFactor", 'y', FALSE }, 1433 { "ForkEachJob", 'Y', FALSE }, 1434 { "ClassFactor", 'z', FALSE }, 1435 { "RetryFactor", 'Z', FALSE }, 1436 #define O_QUEUESORTORD 0x81 1437 { "QueueSortOrder", O_QUEUESORTORD, TRUE }, 1438 #define O_HOSTSFILE 0x82 1439 { "HostsFile", O_HOSTSFILE, FALSE }, 1440 #define O_MQA 0x83 1441 { "MinQueueAge", O_MQA, TRUE }, 1442 #define O_DEFCHARSET 0x85 1443 { "DefaultCharSet", O_DEFCHARSET, TRUE }, 1444 #define O_SSFILE 0x86 1445 { "ServiceSwitchFile", O_SSFILE, FALSE }, 1446 #define O_DIALDELAY 0x87 1447 { "DialDelay", O_DIALDELAY, TRUE }, 1448 #define O_NORCPTACTION 0x88 1449 { "NoRecipientAction", O_NORCPTACTION, TRUE }, 1450 #define O_SAFEFILEENV 0x89 1451 { "SafeFileEnvironment", O_SAFEFILEENV, FALSE }, 1452 #define O_MAXMSGSIZE 0x8a 1453 { "MaxMessageSize", O_MAXMSGSIZE, FALSE }, 1454 #define O_COLONOKINADDR 0x8b 1455 { "ColonOkInAddr", O_COLONOKINADDR, TRUE }, 1456 #define O_MAXQUEUERUN 0x8c 1457 { "MaxQueueRunSize", O_MAXQUEUERUN, TRUE }, 1458 #define O_MAXCHILDREN 0x8d 1459 { "MaxDaemonChildren", O_MAXCHILDREN, FALSE }, 1460 #define O_KEEPCNAMES 0x8e 1461 { "DontExpandCnames", O_KEEPCNAMES, FALSE }, 1462 #define O_MUSTQUOTE 0x8f 1463 { "MustQuoteChars", O_MUSTQUOTE, FALSE }, 1464 #define O_SMTPGREETING 0x90 1465 { "SmtpGreetingMessage", O_SMTPGREETING, FALSE }, 1466 #define O_UNIXFROM 0x91 1467 { "UnixFromLine", O_UNIXFROM, FALSE }, 1468 #define O_OPCHARS 0x92 1469 { "OperatorChars", O_OPCHARS, FALSE }, 1470 #define O_DONTINITGRPS 0x93 1471 { "DontInitGroups", O_DONTINITGRPS, FALSE }, 1472 #define O_SLFH 0x94 1473 { "SingleLineFromHeader", O_SLFH, TRUE }, 1474 #define O_ABH 0x95 1475 { "AllowBogusHELO", O_ABH, TRUE }, 1476 #define O_CONNTHROT 0x97 1477 { "ConnectionRateThrottle", O_CONNTHROT, FALSE }, 1478 #define O_UGW 0x99 1479 { "UnsafeGroupWrites", O_UGW, FALSE }, 1480 #define O_DBLBOUNCE 0x9a 1481 { "DoubleBounceAddress", O_DBLBOUNCE, FALSE }, 1482 #define O_HSDIR 0x9b 1483 { "HostStatusDirectory", O_HSDIR, FALSE }, 1484 #define O_SINGTHREAD 0x9c 1485 { "SingleThreadDelivery", O_SINGTHREAD, FALSE }, 1486 #define O_RUNASUSER 0x9d 1487 { "RunAsUser", O_RUNASUSER, FALSE }, 1488 #if _FFR_DSN_RRT_OPTION 1489 #define O_DSN_RRT 0x9e 1490 { "RrtImpliesDsn", O_DSN_RRT, FALSE }, 1491 #endif 1492 #if _FFR_PIDFILE_OPTION 1493 #define O_PIDFILE 0x9f 1494 { "PidFile", O_PIDFILE, FALSE }, 1495 #endif 1496 #define O_DONTBLAMESENDMAIL 0xa0 1497 { "DontBlameSendmail", O_DONTBLAMESENDMAIL, FALSE }, 1498 #define O_DPI 0xa1 1499 { "DontProbeInterfaces", O_DPI, FALSE }, 1500 #define O_MAXRCPT 0xa2 1501 { "MaxRecipientsPerMessage", O_MAXRCPT, FALSE }, 1502 #if _FFR_DEADLETTERDROP_OPTION 1503 #define O_DEADLETTER 0xa3 1504 { "DeadLetterDrop", O_DEADLETTER, FALSE }, 1505 #endif 1506 #if _FFR_DONTLOCKFILESFORREAD_OPTION 1507 #define O_DONTLOCK 0xa4 1508 { "DontLockFilesForRead", O_DONTLOCK, FALSE }, 1509 #endif 1510 #if _FFR_MAXALIASRECURSION_OPTION 1511 #define O_MAXALIASRCSN 0xa5 1512 { "MaxAliasRecursion", O_MAXALIASRCSN, FALSE }, 1513 #endif 1514 #if _FFR_CONNECTONLYTO_OPTION 1515 #define O_CNCTONLYTO 0xa6 1516 { "ConnectOnlyTo", O_CNCTONLYTO, FALSE }, 1517 #endif 1518 #if _FFR_TRUSTED_FILE_OWNER 1519 #define O_TRUSTFILEOWN 0xa7 1520 { "TrustedFileOwner", O_TRUSTFILEOWN, FALSE }, 1521 #endif 1522 1523 { NULL, '\0', FALSE } 1524 }; 1525 1526 1527 1528 void 1529 setoption(opt, val, safe, sticky, e) 1530 int opt; 1531 char *val; 1532 bool safe; 1533 bool sticky; 1534 register ENVELOPE *e; 1535 { 1536 register char *p; 1537 register struct optioninfo *o; 1538 char *subopt; 1539 int mid; 1540 bool can_setuid = RunAsUid == 0; 1541 auto char *ep; 1542 char buf[50]; 1543 extern bool atobool __P((char *)); 1544 extern time_t convtime __P((char *, char)); 1545 extern int QueueLA; 1546 extern int RefuseLA; 1547 extern bool Warn_Q_option; 1548 extern void setalias __P((char *)); 1549 extern int atooct __P((char *)); 1550 extern void setdefuser __P((void)); 1551 extern void setdaemonoptions __P((char *)); 1552 1553 errno = 0; 1554 if (opt == ' ') 1555 { 1556 /* full word options */ 1557 struct optioninfo *sel; 1558 1559 p = strchr(val, '='); 1560 if (p == NULL) 1561 p = &val[strlen(val)]; 1562 while (*--p == ' ') 1563 continue; 1564 while (*++p == ' ') 1565 *p = '\0'; 1566 if (p == val) 1567 { 1568 syserr("readcf: null option name"); 1569 return; 1570 } 1571 if (*p == '=') 1572 *p++ = '\0'; 1573 while (*p == ' ') 1574 p++; 1575 subopt = strchr(val, '.'); 1576 if (subopt != NULL) 1577 *subopt++ = '\0'; 1578 sel = NULL; 1579 for (o = OptionTab; o->o_name != NULL; o++) 1580 { 1581 if (strncasecmp(o->o_name, val, strlen(val)) != 0) 1582 continue; 1583 if (strlen(o->o_name) == strlen(val)) 1584 { 1585 /* completely specified -- this must be it */ 1586 sel = NULL; 1587 break; 1588 } 1589 if (sel != NULL) 1590 break; 1591 sel = o; 1592 } 1593 if (sel != NULL && o->o_name == NULL) 1594 o = sel; 1595 else if (o->o_name == NULL) 1596 { 1597 syserr("readcf: unknown option name %s", val); 1598 return; 1599 } 1600 else if (sel != NULL) 1601 { 1602 syserr("readcf: ambiguous option name %s (matches %s and %s)", 1603 val, sel->o_name, o->o_name); 1604 return; 1605 } 1606 if (strlen(val) != strlen(o->o_name)) 1607 { 1608 int oldVerbose = Verbose; 1609 1610 Verbose = 1; 1611 message("Option %s used as abbreviation for %s", 1612 val, o->o_name); 1613 Verbose = oldVerbose; 1614 } 1615 opt = o->o_code; 1616 val = p; 1617 } 1618 else 1619 { 1620 for (o = OptionTab; o->o_name != NULL; o++) 1621 { 1622 if (o->o_code == opt) 1623 break; 1624 } 1625 subopt = NULL; 1626 } 1627 1628 if (tTd(37, 1)) 1629 { 1630 printf(isascii(opt) && isprint(opt) ? 1631 "setoption %s (%c).%s=" : 1632 "setoption %s (0x%x).%s=", 1633 o->o_name == NULL ? "<unknown>" : o->o_name, 1634 opt, 1635 subopt == NULL ? "" : subopt); 1636 xputs(val); 1637 } 1638 1639 /* 1640 ** See if this option is preset for us. 1641 */ 1642 1643 if (!sticky && bitnset(opt, StickyOpt)) 1644 { 1645 if (tTd(37, 1)) 1646 printf(" (ignored)\n"); 1647 return; 1648 } 1649 1650 /* 1651 ** Check to see if this option can be specified by this user. 1652 */ 1653 1654 if (!safe && RealUid == 0) 1655 safe = TRUE; 1656 if (!safe && !o->o_safe) 1657 { 1658 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1659 { 1660 if (tTd(37, 1)) 1661 printf(" (unsafe)"); 1662 (void) drop_privileges(TRUE); 1663 } 1664 } 1665 if (tTd(37, 1)) 1666 printf("\n"); 1667 1668 switch (opt & 0xff) 1669 { 1670 case '7': /* force seven-bit input */ 1671 SevenBitInput = atobool(val); 1672 break; 1673 1674 #if MIME8TO7 1675 case '8': /* handling of 8-bit input */ 1676 switch (*val) 1677 { 1678 case 'm': /* convert 8-bit, convert MIME */ 1679 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1680 break; 1681 1682 case 'p': /* pass 8 bit, convert MIME */ 1683 MimeMode = MM_CVTMIME|MM_PASS8BIT; 1684 break; 1685 1686 case 's': /* strict adherence */ 1687 MimeMode = MM_CVTMIME; 1688 break; 1689 1690 #if 0 1691 case 'r': /* reject 8-bit, don't convert MIME */ 1692 MimeMode = 0; 1693 break; 1694 1695 case 'j': /* "just send 8" */ 1696 MimeMode = MM_PASS8BIT; 1697 break; 1698 1699 case 'a': /* encode 8 bit if available */ 1700 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1701 break; 1702 1703 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1704 MimeMode = MM_MIME8BIT; 1705 break; 1706 #endif 1707 1708 default: 1709 syserr("Unknown 8-bit mode %c", *val); 1710 exit(EX_USAGE); 1711 } 1712 break; 1713 #endif 1714 1715 case 'A': /* set default alias file */ 1716 if (val[0] == '\0') 1717 setalias("aliases"); 1718 else 1719 setalias(val); 1720 break; 1721 1722 case 'a': /* look N minutes for "@:@" in alias file */ 1723 if (val[0] == '\0') 1724 SafeAlias = 5 * 60; /* five minutes */ 1725 else 1726 SafeAlias = convtime(val, 'm'); 1727 break; 1728 1729 case 'B': /* substitution for blank character */ 1730 SpaceSub = val[0]; 1731 if (SpaceSub == '\0') 1732 SpaceSub = ' '; 1733 break; 1734 1735 case 'b': /* min blocks free on queue fs/max msg size */ 1736 p = strchr(val, '/'); 1737 if (p != NULL) 1738 { 1739 *p++ = '\0'; 1740 MaxMessageSize = atol(p); 1741 } 1742 MinBlocksFree = atol(val); 1743 break; 1744 1745 case 'c': /* don't connect to "expensive" mailers */ 1746 NoConnect = atobool(val); 1747 break; 1748 1749 case 'C': /* checkpoint every N addresses */ 1750 CheckpointInterval = atoi(val); 1751 break; 1752 1753 case 'd': /* delivery mode */ 1754 switch (*val) 1755 { 1756 case '\0': 1757 e->e_sendmode = SM_DELIVER; 1758 break; 1759 1760 case SM_QUEUE: /* queue only */ 1761 case SM_DEFER: /* queue only and defer map lookups */ 1762 #if !QUEUE 1763 syserr("need QUEUE to set -odqueue or -oddefer"); 1764 #endif /* QUEUE */ 1765 /* fall through..... */ 1766 1767 case SM_DELIVER: /* do everything */ 1768 case SM_FORK: /* fork after verification */ 1769 e->e_sendmode = *val; 1770 break; 1771 1772 default: 1773 syserr("Unknown delivery mode %c", *val); 1774 exit(EX_USAGE); 1775 } 1776 buf[0] = (char)e->e_sendmode; 1777 buf[1] = '\0'; 1778 define(macid("{deliveryMode}", NULL), newstr(buf), e); 1779 break; 1780 1781 case 'D': /* rebuild alias database as needed */ 1782 AutoRebuild = atobool(val); 1783 break; 1784 1785 case 'E': /* error message header/header file */ 1786 if (*val != '\0') 1787 ErrMsgFile = newstr(val); 1788 break; 1789 1790 case 'e': /* set error processing mode */ 1791 switch (*val) 1792 { 1793 case EM_QUIET: /* be silent about it */ 1794 case EM_MAIL: /* mail back */ 1795 case EM_BERKNET: /* do berknet error processing */ 1796 case EM_WRITE: /* write back (or mail) */ 1797 case EM_PRINT: /* print errors normally (default) */ 1798 e->e_errormode = *val; 1799 break; 1800 } 1801 break; 1802 1803 case 'F': /* file mode */ 1804 FileMode = atooct(val) & 0777; 1805 break; 1806 1807 case 'f': /* save Unix-style From lines on front */ 1808 SaveFrom = atobool(val); 1809 break; 1810 1811 case 'G': /* match recipients against GECOS field */ 1812 MatchGecos = atobool(val); 1813 break; 1814 1815 case 'g': /* default gid */ 1816 g_opt: 1817 if (isascii(*val) && isdigit(*val)) 1818 DefGid = atoi(val); 1819 else 1820 { 1821 register struct group *gr; 1822 1823 DefGid = -1; 1824 gr = getgrnam(val); 1825 if (gr == NULL) 1826 syserr("readcf: option %c: unknown group %s", 1827 opt, val); 1828 else 1829 DefGid = gr->gr_gid; 1830 } 1831 break; 1832 1833 case 'H': /* help file */ 1834 if (val[0] == '\0') 1835 HelpFile = "sendmail.hf"; 1836 else 1837 HelpFile = newstr(val); 1838 break; 1839 1840 case 'h': /* maximum hop count */ 1841 MaxHopCount = atoi(val); 1842 break; 1843 1844 case 'I': /* use internet domain name server */ 1845 #if NAMED_BIND 1846 for (p = val; *p != 0; ) 1847 { 1848 bool clearmode; 1849 char *q; 1850 struct resolverflags *rfp; 1851 1852 while (*p == ' ') 1853 p++; 1854 if (*p == '\0') 1855 break; 1856 clearmode = FALSE; 1857 if (*p == '-') 1858 clearmode = TRUE; 1859 else if (*p != '+') 1860 p--; 1861 p++; 1862 q = p; 1863 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1864 p++; 1865 if (*p != '\0') 1866 *p++ = '\0'; 1867 if (strcasecmp(q, "HasWildcardMX") == 0) 1868 { 1869 HasWildcardMX = !clearmode; 1870 continue; 1871 } 1872 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1873 { 1874 if (strcasecmp(q, rfp->rf_name) == 0) 1875 break; 1876 } 1877 if (rfp->rf_name == NULL) 1878 syserr("readcf: I option value %s unrecognized", q); 1879 else if (clearmode) 1880 _res.options &= ~rfp->rf_bits; 1881 else 1882 _res.options |= rfp->rf_bits; 1883 } 1884 if (tTd(8, 2)) 1885 printf("_res.options = %x, HasWildcardMX = %d\n", 1886 (u_int) _res.options, HasWildcardMX); 1887 #else 1888 usrerr("name server (I option) specified but BIND not compiled in"); 1889 #endif 1890 break; 1891 1892 case 'i': /* ignore dot lines in message */ 1893 IgnrDot = atobool(val); 1894 break; 1895 1896 case 'j': /* send errors in MIME (RFC 1341) format */ 1897 SendMIMEErrors = atobool(val); 1898 break; 1899 1900 case 'J': /* .forward search path */ 1901 ForwardPath = newstr(val); 1902 break; 1903 1904 case 'k': /* connection cache size */ 1905 MaxMciCache = atoi(val); 1906 if (MaxMciCache < 0) 1907 MaxMciCache = 0; 1908 break; 1909 1910 case 'K': /* connection cache timeout */ 1911 MciCacheTimeout = convtime(val, 'm'); 1912 break; 1913 1914 case 'l': /* use Errors-To: header */ 1915 UseErrorsTo = atobool(val); 1916 break; 1917 1918 case 'L': /* log level */ 1919 if (safe || LogLevel < atoi(val)) 1920 LogLevel = atoi(val); 1921 break; 1922 1923 case 'M': /* define macro */ 1924 mid = macid(val, &ep); 1925 p = newstr(ep); 1926 if (!safe) 1927 cleanstrcpy(p, p, MAXNAME); 1928 define(mid, p, CurEnv); 1929 sticky = FALSE; 1930 break; 1931 1932 case 'm': /* send to me too */ 1933 MeToo = atobool(val); 1934 break; 1935 1936 case 'n': /* validate RHS in newaliases */ 1937 CheckAliases = atobool(val); 1938 break; 1939 1940 /* 'N' available -- was "net name" */ 1941 1942 case 'O': /* daemon options */ 1943 #if DAEMON 1944 setdaemonoptions(val); 1945 #else 1946 syserr("DaemonPortOptions (O option) set but DAEMON not compiled in"); 1947 #endif 1948 break; 1949 1950 case 'o': /* assume old style headers */ 1951 if (atobool(val)) 1952 CurEnv->e_flags |= EF_OLDSTYLE; 1953 else 1954 CurEnv->e_flags &= ~EF_OLDSTYLE; 1955 break; 1956 1957 case 'p': /* select privacy level */ 1958 p = val; 1959 for (;;) 1960 { 1961 register struct prival *pv; 1962 extern struct prival PrivacyValues[]; 1963 1964 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1965 p++; 1966 if (*p == '\0') 1967 break; 1968 val = p; 1969 while (isascii(*p) && isalnum(*p)) 1970 p++; 1971 if (*p != '\0') 1972 *p++ = '\0'; 1973 1974 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1975 { 1976 if (strcasecmp(val, pv->pv_name) == 0) 1977 break; 1978 } 1979 if (pv->pv_name == NULL) 1980 syserr("readcf: Op line: %s unrecognized", val); 1981 PrivacyFlags |= pv->pv_flag; 1982 } 1983 sticky = FALSE; 1984 break; 1985 1986 case 'P': /* postmaster copy address for returned mail */ 1987 PostMasterCopy = newstr(val); 1988 break; 1989 1990 case 'q': /* slope of queue only function */ 1991 QueueFactor = atoi(val); 1992 break; 1993 1994 case 'Q': /* queue directory */ 1995 if (val[0] == '\0') 1996 QueueDir = "mqueue"; 1997 else 1998 QueueDir = newstr(val); 1999 if (RealUid != 0 && !safe) 2000 Warn_Q_option = TRUE; 2001 break; 2002 2003 case 'R': /* don't prune routes */ 2004 DontPruneRoutes = atobool(val); 2005 break; 2006 2007 case 'r': /* read timeout */ 2008 if (subopt == NULL) 2009 inittimeouts(val); 2010 else 2011 settimeout(subopt, val); 2012 break; 2013 2014 case 'S': /* status file */ 2015 if (val[0] == '\0') 2016 StatFile = "sendmail.st"; 2017 else 2018 StatFile = newstr(val); 2019 break; 2020 2021 case 's': /* be super safe, even if expensive */ 2022 SuperSafe = atobool(val); 2023 break; 2024 2025 case 'T': /* queue timeout */ 2026 p = strchr(val, '/'); 2027 if (p != NULL) 2028 { 2029 *p++ = '\0'; 2030 settimeout("queuewarn", p); 2031 } 2032 settimeout("queuereturn", val); 2033 break; 2034 2035 case 't': /* time zone name */ 2036 TimeZoneSpec = newstr(val); 2037 break; 2038 2039 case 'U': /* location of user database */ 2040 UdbSpec = newstr(val); 2041 break; 2042 2043 case 'u': /* set default uid */ 2044 for (p = val; *p != '\0'; p++) 2045 { 2046 if (*p == '.' || *p == '/' || *p == ':') 2047 { 2048 *p++ = '\0'; 2049 break; 2050 } 2051 } 2052 if (isascii(*val) && isdigit(*val)) 2053 { 2054 DefUid = atoi(val); 2055 setdefuser(); 2056 } 2057 else 2058 { 2059 register struct passwd *pw; 2060 2061 DefUid = -1; 2062 pw = sm_getpwnam(val); 2063 if (pw == NULL) 2064 syserr("readcf: option u: unknown user %s", val); 2065 else 2066 { 2067 DefUid = pw->pw_uid; 2068 DefGid = pw->pw_gid; 2069 DefUser = newstr(pw->pw_name); 2070 } 2071 } 2072 2073 #ifdef UID_MAX 2074 if (DefUid > UID_MAX) 2075 { 2076 syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored", 2077 DefUid, UID_MAX); 2078 } 2079 #endif 2080 2081 /* handle the group if it is there */ 2082 if (*p == '\0') 2083 break; 2084 val = p; 2085 goto g_opt; 2086 2087 case 'V': /* fallback MX host */ 2088 if (val[0] != '\0') 2089 FallBackMX = newstr(val); 2090 break; 2091 2092 case 'v': /* run in verbose mode */ 2093 Verbose = atobool(val) ? 1 : 0; 2094 break; 2095 2096 case 'w': /* if we are best MX, try host directly */ 2097 TryNullMXList = atobool(val); 2098 break; 2099 2100 /* 'W' available -- was wizard password */ 2101 2102 case 'x': /* load avg at which to auto-queue msgs */ 2103 QueueLA = atoi(val); 2104 break; 2105 2106 case 'X': /* load avg at which to auto-reject connections */ 2107 RefuseLA = atoi(val); 2108 break; 2109 2110 case 'y': /* work recipient factor */ 2111 WkRecipFact = atoi(val); 2112 break; 2113 2114 case 'Y': /* fork jobs during queue runs */ 2115 ForkQueueRuns = atobool(val); 2116 break; 2117 2118 case 'z': /* work message class factor */ 2119 WkClassFact = atoi(val); 2120 break; 2121 2122 case 'Z': /* work time factor */ 2123 WkTimeFact = atoi(val); 2124 break; 2125 2126 case O_QUEUESORTORD: /* queue sorting order */ 2127 switch (*val) 2128 { 2129 case 'h': /* Host first */ 2130 case 'H': 2131 QueueSortOrder = QS_BYHOST; 2132 break; 2133 2134 case 'p': /* Priority order */ 2135 case 'P': 2136 QueueSortOrder = QS_BYPRIORITY; 2137 break; 2138 2139 case 't': /* Submission time */ 2140 case 'T': 2141 QueueSortOrder = QS_BYTIME; 2142 break; 2143 2144 default: 2145 syserr("Invalid queue sort order \"%s\"", val); 2146 } 2147 break; 2148 2149 case O_HOSTSFILE: /* pathname of /etc/hosts file */ 2150 HostsFile = newstr(val); 2151 break; 2152 2153 case O_MQA: /* minimum queue age between deliveries */ 2154 MinQueueAge = convtime(val, 'm'); 2155 break; 2156 2157 case O_DEFCHARSET: /* default character set for mimefying */ 2158 DefaultCharSet = newstr(denlstring(val, TRUE, TRUE)); 2159 break; 2160 2161 case O_SSFILE: /* service switch file */ 2162 ServiceSwitchFile = newstr(val); 2163 break; 2164 2165 case O_DIALDELAY: /* delay for dial-on-demand operation */ 2166 DialDelay = convtime(val, 's'); 2167 break; 2168 2169 case O_NORCPTACTION: /* what to do if no recipient */ 2170 if (strcasecmp(val, "none") == 0) 2171 NoRecipientAction = NRA_NO_ACTION; 2172 else if (strcasecmp(val, "add-to") == 0) 2173 NoRecipientAction = NRA_ADD_TO; 2174 else if (strcasecmp(val, "add-apparently-to") == 0) 2175 NoRecipientAction = NRA_ADD_APPARENTLY_TO; 2176 else if (strcasecmp(val, "add-bcc") == 0) 2177 NoRecipientAction = NRA_ADD_BCC; 2178 else if (strcasecmp(val, "add-to-undisclosed") == 0) 2179 NoRecipientAction = NRA_ADD_TO_UNDISCLOSED; 2180 else 2181 syserr("Invalid NoRecipientAction: %s", val); 2182 break; 2183 2184 case O_SAFEFILEENV: /* chroot() environ for writing to files */ 2185 SafeFileEnv = newstr(val); 2186 break; 2187 2188 case O_MAXMSGSIZE: /* maximum message size */ 2189 MaxMessageSize = atol(val); 2190 break; 2191 2192 case O_COLONOKINADDR: /* old style handling of colon addresses */ 2193 ColonOkInAddr = atobool(val); 2194 break; 2195 2196 case O_MAXQUEUERUN: /* max # of jobs in a single queue run */ 2197 MaxQueueRun = atol(val); 2198 break; 2199 2200 case O_MAXCHILDREN: /* max # of children of daemon */ 2201 MaxChildren = atoi(val); 2202 break; 2203 2204 case O_KEEPCNAMES: /* don't expand CNAME records */ 2205 DontExpandCnames = atobool(val); 2206 break; 2207 2208 case O_MUSTQUOTE: /* must quote these characters in phrases */ 2209 strcpy(buf, "@,;:\\()[]"); 2210 if (strlen(val) < (SIZE_T) sizeof buf - 10) 2211 strcat(buf, val); 2212 MustQuoteChars = newstr(buf); 2213 break; 2214 2215 case O_SMTPGREETING: /* SMTP greeting message (old $e macro) */ 2216 SmtpGreeting = newstr(munchstring(val, NULL, '\0')); 2217 break; 2218 2219 case O_UNIXFROM: /* UNIX From_ line (old $l macro) */ 2220 UnixFromLine = newstr(munchstring(val, NULL, '\0')); 2221 break; 2222 2223 case O_OPCHARS: /* operator characters (old $o macro) */ 2224 OperatorChars = newstr(munchstring(val, NULL, '\0')); 2225 break; 2226 2227 case O_DONTINITGRPS: /* don't call initgroups(3) */ 2228 DontInitGroups = atobool(val); 2229 break; 2230 2231 case O_SLFH: /* make sure from fits on one line */ 2232 SingleLineFromHeader = atobool(val); 2233 break; 2234 2235 case O_ABH: /* allow HELO commands with syntax errors */ 2236 AllowBogusHELO = atobool(val); 2237 break; 2238 2239 case O_CONNTHROT: /* connection rate throttle */ 2240 ConnRateThrottle = atoi(val); 2241 break; 2242 2243 case O_UGW: /* group writable files are unsafe */ 2244 if (!atobool(val)) 2245 DontBlameSendmail |= DBS_GROUPWRITABLEFORWARDFILESAFE|DBS_GROUPWRITABLEINCLUDEFILESAFE; 2246 break; 2247 2248 case O_DBLBOUNCE: /* address to which to send double bounces */ 2249 if (val[0] != '\0') 2250 DoubleBounceAddr = newstr(val); 2251 else 2252 syserr("readcf: option DoubleBounceAddress: value required"); 2253 break; 2254 2255 case O_HSDIR: /* persistent host status directory */ 2256 if (val[0] != '\0') 2257 HostStatDir = newstr(val); 2258 break; 2259 2260 case O_SINGTHREAD: /* single thread deliveries (requires hsdir) */ 2261 SingleThreadDelivery = atobool(val); 2262 break; 2263 2264 case O_RUNASUSER: /* run bulk of code as this user */ 2265 for (p = val; *p != '\0'; p++) 2266 { 2267 if (*p == '.' || *p == '/' || *p == ':') 2268 { 2269 *p++ = '\0'; 2270 break; 2271 } 2272 } 2273 if (isascii(*val) && isdigit(*val)) 2274 { 2275 if (can_setuid) 2276 RunAsUid = atoi(val); 2277 } 2278 else 2279 { 2280 register struct passwd *pw; 2281 2282 pw = sm_getpwnam(val); 2283 if (pw == NULL) 2284 syserr("readcf: option RunAsUser: unknown user %s", val); 2285 else if (can_setuid) 2286 { 2287 if (*p == '\0') 2288 RunAsUserName = newstr(val); 2289 RunAsUid = pw->pw_uid; 2290 RunAsGid = pw->pw_gid; 2291 } 2292 } 2293 #ifdef UID_MAX 2294 if (RunAsUid > UID_MAX) 2295 { 2296 syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored", 2297 RunAsUid, UID_MAX); 2298 } 2299 #endif 2300 if (*p != '\0') 2301 { 2302 if (isascii(*p) && isdigit(*p)) 2303 { 2304 if (can_setuid) 2305 RunAsGid = atoi(p); 2306 } 2307 else 2308 { 2309 register struct group *gr; 2310 2311 gr = getgrnam(p); 2312 if (gr == NULL) 2313 syserr("readcf: option RunAsUser: unknown group %s", 2314 p); 2315 else if (can_setuid) 2316 RunAsGid = gr->gr_gid; 2317 } 2318 } 2319 if (tTd(47, 5)) 2320 printf("readcf: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid); 2321 break; 2322 2323 #if _FFR_DSN_RRT_OPTION 2324 case O_DSN_RRT: 2325 RrtImpliesDsn = atobool(val); 2326 break; 2327 #endif 2328 2329 #if _FFR_PIDFILE_OPTION 2330 case O_PIDFILE: 2331 free(PidFile); 2332 PidFile = newstr(val); 2333 break; 2334 #endif 2335 2336 case O_DONTBLAMESENDMAIL: 2337 p = val; 2338 for (;;) 2339 { 2340 register struct dbsval *dbs; 2341 extern struct dbsval DontBlameSendmailValues[]; 2342 2343 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 2344 p++; 2345 if (*p == '\0') 2346 break; 2347 val = p; 2348 while (isascii(*p) && isalnum(*p)) 2349 p++; 2350 if (*p != '\0') 2351 *p++ = '\0'; 2352 2353 for (dbs = DontBlameSendmailValues; 2354 dbs->dbs_name != NULL; dbs++) 2355 { 2356 if (strcasecmp(val, dbs->dbs_name) == 0) 2357 break; 2358 } 2359 if (dbs->dbs_name == NULL) 2360 syserr("readcf: DontBlameSendmail option: %s unrecognized", val); 2361 else if (dbs->dbs_flag == DBS_SAFE) 2362 DontBlameSendmail = DBS_SAFE; 2363 else 2364 DontBlameSendmail |= dbs->dbs_flag; 2365 } 2366 sticky = FALSE; 2367 break; 2368 2369 case O_DPI: 2370 DontProbeInterfaces = atobool(val); 2371 break; 2372 2373 case O_MAXRCPT: 2374 MaxRcptPerMsg = atoi(val); 2375 break; 2376 2377 #if _FFR_DEADLETTERDROP_OPTION 2378 case O_DEADLETTER: 2379 if (DeadLetterDrop != NULL) 2380 free(DeadLetterDrop); 2381 DeadLetterDrop = newstr(val); 2382 break; 2383 #endif 2384 2385 #if _FFR_DONTLOCKFILESFORREAD_OPTION 2386 case O_DONTLOCK: 2387 DontLockReadFiles = atobool(val); 2388 break; 2389 #endif 2390 2391 #if _FFR_MAXALIASRECURSION_OPTION 2392 case O_MAXALIASRCSN: 2393 MaxAliasRecursion = atoi(val); 2394 break; 2395 #endif 2396 2397 #if _FFR_CONNECTONLYTO_OPTION 2398 case O_CNCTONLYTO: 2399 /* XXX should probably use gethostbyname */ 2400 ConnectOnlyTo = inet_addr(val); 2401 break; 2402 #endif 2403 2404 #if _FFR_TRUSTED_FILE_OWNER 2405 case O_TRUSTFILEOWN: 2406 if (isascii(*val) && isdigit(*val)) 2407 TrustedFileUid = atoi(val); 2408 else 2409 { 2410 register struct passwd *pw; 2411 2412 TrustedFileUid = 0; 2413 pw = sm_getpwnam(val); 2414 if (pw == NULL) 2415 syserr("readcf: option TrustedFileOwner: unknown user %s", val); 2416 else 2417 TrustedFileUid = pw->pw_uid; 2418 } 2419 2420 #ifdef UID_MAX 2421 if (TrustedFileUid > UID_MAX) 2422 { 2423 syserr("readcf: option TrustedFileOwner: uid value (%ld) > UID_MAX (%ld)", 2424 TrustedFileUid, UID_MAX); 2425 TrustedFileUid = 0; 2426 } 2427 #endif 2428 break; 2429 #endif 2430 2431 default: 2432 if (tTd(37, 1)) 2433 { 2434 if (isascii(opt) && isprint(opt)) 2435 printf("Warning: option %c unknown\n", opt); 2436 else 2437 printf("Warning: option 0x%x unknown\n", opt); 2438 } 2439 break; 2440 } 2441 if (sticky) 2442 setbitn(opt, StickyOpt); 2443 } 2444 /* 2445 ** SETCLASS -- set a string into a class 2446 ** 2447 ** Parameters: 2448 ** class -- the class to put the string in. 2449 ** str -- the string to enter 2450 ** 2451 ** Returns: 2452 ** none. 2453 ** 2454 ** Side Effects: 2455 ** puts the word into the symbol table. 2456 */ 2457 2458 void 2459 setclass(class, str) 2460 int class; 2461 char *str; 2462 { 2463 register STAB *s; 2464 2465 if (tTd(37, 8)) 2466 printf("setclass(%s, %s)\n", macname(class), str); 2467 s = stab(str, ST_CLASS, ST_ENTER); 2468 setbitn(class, s->s_class); 2469 } 2470 /* 2471 ** MAKEMAPENTRY -- create a map entry 2472 ** 2473 ** Parameters: 2474 ** line -- the config file line 2475 ** 2476 ** Returns: 2477 ** A pointer to the map that has been created. 2478 ** NULL if there was a syntax error. 2479 ** 2480 ** Side Effects: 2481 ** Enters the map into the dictionary. 2482 */ 2483 2484 MAP * 2485 makemapentry(line) 2486 char *line; 2487 { 2488 register char *p; 2489 char *mapname; 2490 char *classname; 2491 register STAB *s; 2492 STAB *class; 2493 2494 for (p = line; isascii(*p) && isspace(*p); p++) 2495 continue; 2496 if (!(isascii(*p) && isalnum(*p))) 2497 { 2498 syserr("readcf: config K line: no map name"); 2499 return NULL; 2500 } 2501 2502 mapname = p; 2503 while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.') 2504 continue; 2505 if (*p != '\0') 2506 *p++ = '\0'; 2507 while (isascii(*p) && isspace(*p)) 2508 p++; 2509 if (!(isascii(*p) && isalnum(*p))) 2510 { 2511 syserr("readcf: config K line, map %s: no map class", mapname); 2512 return NULL; 2513 } 2514 classname = p; 2515 while (isascii(*++p) && isalnum(*p)) 2516 continue; 2517 if (*p != '\0') 2518 *p++ = '\0'; 2519 while (isascii(*p) && isspace(*p)) 2520 p++; 2521 2522 /* look up the class */ 2523 class = stab(classname, ST_MAPCLASS, ST_FIND); 2524 if (class == NULL) 2525 { 2526 syserr("readcf: map %s: class %s not available", mapname, classname); 2527 return NULL; 2528 } 2529 2530 /* enter the map */ 2531 s = stab(mapname, ST_MAP, ST_ENTER); 2532 s->s_map.map_class = &class->s_mapclass; 2533 s->s_map.map_mname = newstr(mapname); 2534 2535 if (class->s_mapclass.map_parse(&s->s_map, p)) 2536 s->s_map.map_mflags |= MF_VALID; 2537 2538 if (tTd(37, 5)) 2539 { 2540 printf("map %s, class %s, flags %lx, file %s,\n", 2541 s->s_map.map_mname, s->s_map.map_class->map_cname, 2542 s->s_map.map_mflags, 2543 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 2544 printf("\tapp %s, domain %s, rebuild %s\n", 2545 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 2546 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 2547 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 2548 } 2549 2550 return &s->s_map; 2551 } 2552 /* 2553 ** STRTORWSET -- convert string to rewriting set number 2554 ** 2555 ** Parameters: 2556 ** p -- the pointer to the string to decode. 2557 ** endp -- if set, store the trailing delimiter here. 2558 ** stabmode -- ST_ENTER to create this entry, ST_FIND if 2559 ** it must already exist. 2560 ** 2561 ** Returns: 2562 ** The appropriate ruleset number. 2563 ** -1 if it is not valid (error already printed) 2564 */ 2565 2566 int 2567 strtorwset(p, endp, stabmode) 2568 char *p; 2569 char **endp; 2570 int stabmode; 2571 { 2572 int ruleset; 2573 static int nextruleset = MAXRWSETS; 2574 2575 while (isascii(*p) && isspace(*p)) 2576 p++; 2577 if (!isascii(*p)) 2578 { 2579 syserr("invalid ruleset name: \"%.20s\"", p); 2580 return -1; 2581 } 2582 if (isdigit(*p)) 2583 { 2584 ruleset = strtol(p, endp, 10); 2585 if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 2586 { 2587 syserr("bad ruleset %d (%d max)", 2588 ruleset, MAXRWSETS / 2); 2589 ruleset = -1; 2590 } 2591 } 2592 else 2593 { 2594 STAB *s; 2595 char delim; 2596 char *q; 2597 2598 q = p; 2599 while (*p != '\0' && isascii(*p) && 2600 (isalnum(*p) || *p == '_')) 2601 p++; 2602 if (q == p || !(isascii(*q) && isalpha(*q))) 2603 { 2604 /* no valid characters */ 2605 syserr("invalid ruleset name: \"%.20s\"", q); 2606 return -1; 2607 } 2608 while (isascii(*p) && isspace(*p)) 2609 *p++ = '\0'; 2610 delim = *p; 2611 if (delim != '\0') 2612 *p = '\0'; 2613 s = stab(q, ST_RULESET, stabmode); 2614 if (delim != '\0') 2615 *p = delim; 2616 2617 if (s == NULL) 2618 return -1; 2619 2620 if (stabmode == ST_ENTER && delim == '=') 2621 { 2622 while (isascii(*++p) && isspace(*p)) 2623 continue; 2624 if (!(isascii(*p) && isdigit(*p))) 2625 { 2626 syserr("bad ruleset definition \"%s\" (number required after `=')", q); 2627 ruleset = -1; 2628 } 2629 else 2630 { 2631 ruleset = strtol(p, endp, 10); 2632 if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 2633 { 2634 syserr("bad ruleset number %d in \"%s\" (%d max)", 2635 ruleset, q, MAXRWSETS / 2); 2636 ruleset = -1; 2637 } 2638 } 2639 } 2640 else 2641 { 2642 if (endp != NULL) 2643 *endp = p; 2644 if (s->s_ruleset > 0) 2645 ruleset = s->s_ruleset; 2646 else if ((ruleset = --nextruleset) < MAXRWSETS / 2) 2647 { 2648 syserr("%s: too many named rulesets (%d max)", 2649 q, MAXRWSETS / 2); 2650 ruleset = -1; 2651 } 2652 } 2653 if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset) 2654 { 2655 syserr("%s: ruleset changed value (old %d, new %d)", 2656 q, s->s_ruleset, ruleset); 2657 ruleset = s->s_ruleset; 2658 } 2659 else if (ruleset > 0) 2660 { 2661 s->s_ruleset = ruleset; 2662 } 2663 } 2664 return ruleset; 2665 } 2666 /* 2667 ** INITTIMEOUTS -- parse and set timeout values 2668 ** 2669 ** Parameters: 2670 ** val -- a pointer to the values. If NULL, do initial 2671 ** settings. 2672 ** 2673 ** Returns: 2674 ** none. 2675 ** 2676 ** Side Effects: 2677 ** Initializes the TimeOuts structure 2678 */ 2679 2680 #define SECONDS 2681 #define MINUTES * 60 2682 #define HOUR * 3600 2683 2684 void 2685 inittimeouts(val) 2686 register char *val; 2687 { 2688 register char *p; 2689 extern time_t convtime __P((char *, char)); 2690 2691 if (tTd(37, 2)) 2692 printf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val); 2693 if (val == NULL) 2694 { 2695 TimeOuts.to_connect = (time_t) 0 SECONDS; 2696 TimeOuts.to_initial = (time_t) 5 MINUTES; 2697 TimeOuts.to_helo = (time_t) 5 MINUTES; 2698 TimeOuts.to_mail = (time_t) 10 MINUTES; 2699 TimeOuts.to_rcpt = (time_t) 1 HOUR; 2700 TimeOuts.to_datainit = (time_t) 5 MINUTES; 2701 TimeOuts.to_datablock = (time_t) 1 HOUR; 2702 TimeOuts.to_datafinal = (time_t) 1 HOUR; 2703 TimeOuts.to_rset = (time_t) 5 MINUTES; 2704 TimeOuts.to_quit = (time_t) 2 MINUTES; 2705 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 2706 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 2707 #if IDENTPROTO 2708 TimeOuts.to_ident = (time_t) 30 SECONDS; 2709 #else 2710 TimeOuts.to_ident = (time_t) 0 SECONDS; 2711 #endif 2712 TimeOuts.to_fileopen = (time_t) 60 SECONDS; 2713 if (tTd(37, 5)) 2714 { 2715 printf("Timeouts:\n"); 2716 printf(" connect = %ld\n", (long)TimeOuts.to_connect); 2717 printf(" initial = %ld\n", (long)TimeOuts.to_initial); 2718 printf(" helo = %ld\n", (long)TimeOuts.to_helo); 2719 printf(" mail = %ld\n", (long)TimeOuts.to_mail); 2720 printf(" rcpt = %ld\n", (long)TimeOuts.to_rcpt); 2721 printf(" datainit = %ld\n", (long)TimeOuts.to_datainit); 2722 printf(" datablock = %ld\n", (long)TimeOuts.to_datablock); 2723 printf(" datafinal = %ld\n", (long)TimeOuts.to_datafinal); 2724 printf(" rset = %ld\n", (long)TimeOuts.to_rset); 2725 printf(" quit = %ld\n", (long)TimeOuts.to_quit); 2726 printf(" nextcommand = %ld\n", (long)TimeOuts.to_nextcommand); 2727 printf(" miscshort = %ld\n", (long)TimeOuts.to_miscshort); 2728 printf(" ident = %ld\n", (long)TimeOuts.to_ident); 2729 printf(" fileopen = %ld\n", (long)TimeOuts.to_fileopen); 2730 } 2731 return; 2732 } 2733 2734 for (;; val = p) 2735 { 2736 while (isascii(*val) && isspace(*val)) 2737 val++; 2738 if (*val == '\0') 2739 break; 2740 for (p = val; *p != '\0' && *p != ','; p++) 2741 continue; 2742 if (*p != '\0') 2743 *p++ = '\0'; 2744 2745 if (isascii(*val) && isdigit(*val)) 2746 { 2747 /* old syntax -- set everything */ 2748 TimeOuts.to_mail = convtime(val, 'm'); 2749 TimeOuts.to_rcpt = TimeOuts.to_mail; 2750 TimeOuts.to_datainit = TimeOuts.to_mail; 2751 TimeOuts.to_datablock = TimeOuts.to_mail; 2752 TimeOuts.to_datafinal = TimeOuts.to_mail; 2753 TimeOuts.to_nextcommand = TimeOuts.to_mail; 2754 continue; 2755 } 2756 else 2757 { 2758 register char *q = strchr(val, ':'); 2759 2760 if (q == NULL && (q = strchr(val, '=')) == NULL) 2761 { 2762 /* syntax error */ 2763 continue; 2764 } 2765 *q++ = '\0'; 2766 settimeout(val, q); 2767 } 2768 } 2769 } 2770 /* 2771 ** SETTIMEOUT -- set an individual timeout 2772 ** 2773 ** Parameters: 2774 ** name -- the name of the timeout. 2775 ** val -- the value of the timeout. 2776 ** 2777 ** Returns: 2778 ** none. 2779 */ 2780 2781 void 2782 settimeout(name, val) 2783 char *name; 2784 char *val; 2785 { 2786 register char *p; 2787 time_t to; 2788 extern time_t convtime __P((char *, char)); 2789 2790 if (tTd(37, 2)) 2791 printf("settimeout(%s = %s)\n", name, val); 2792 2793 to = convtime(val, 'm'); 2794 p = strchr(name, '.'); 2795 if (p != NULL) 2796 *p++ = '\0'; 2797 2798 if (strcasecmp(name, "initial") == 0) 2799 TimeOuts.to_initial = to; 2800 else if (strcasecmp(name, "mail") == 0) 2801 TimeOuts.to_mail = to; 2802 else if (strcasecmp(name, "rcpt") == 0) 2803 TimeOuts.to_rcpt = to; 2804 else if (strcasecmp(name, "datainit") == 0) 2805 TimeOuts.to_datainit = to; 2806 else if (strcasecmp(name, "datablock") == 0) 2807 TimeOuts.to_datablock = to; 2808 else if (strcasecmp(name, "datafinal") == 0) 2809 TimeOuts.to_datafinal = to; 2810 else if (strcasecmp(name, "command") == 0) 2811 TimeOuts.to_nextcommand = to; 2812 else if (strcasecmp(name, "rset") == 0) 2813 TimeOuts.to_rset = to; 2814 else if (strcasecmp(name, "helo") == 0) 2815 TimeOuts.to_helo = to; 2816 else if (strcasecmp(name, "quit") == 0) 2817 TimeOuts.to_quit = to; 2818 else if (strcasecmp(name, "misc") == 0) 2819 TimeOuts.to_miscshort = to; 2820 else if (strcasecmp(name, "ident") == 0) 2821 TimeOuts.to_ident = to; 2822 else if (strcasecmp(name, "fileopen") == 0) 2823 TimeOuts.to_fileopen = to; 2824 else if (strcasecmp(name, "connect") == 0) 2825 TimeOuts.to_connect = to; 2826 else if (strcasecmp(name, "iconnect") == 0) 2827 TimeOuts.to_iconnect = to; 2828 else if (strcasecmp(name, "queuewarn") == 0) 2829 { 2830 to = convtime(val, 'h'); 2831 if (p == NULL || strcmp(p, "*") == 0) 2832 { 2833 TimeOuts.to_q_warning[TOC_NORMAL] = to; 2834 TimeOuts.to_q_warning[TOC_URGENT] = to; 2835 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 2836 } 2837 else if (strcasecmp(p, "normal") == 0) 2838 TimeOuts.to_q_warning[TOC_NORMAL] = to; 2839 else if (strcasecmp(p, "urgent") == 0) 2840 TimeOuts.to_q_warning[TOC_URGENT] = to; 2841 else if (strcasecmp(p, "non-urgent") == 0) 2842 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 2843 else 2844 syserr("settimeout: invalid queuewarn subtimeout %s", p); 2845 } 2846 else if (strcasecmp(name, "queuereturn") == 0) 2847 { 2848 to = convtime(val, 'd'); 2849 if (p == NULL || strcmp(p, "*") == 0) 2850 { 2851 TimeOuts.to_q_return[TOC_NORMAL] = to; 2852 TimeOuts.to_q_return[TOC_URGENT] = to; 2853 TimeOuts.to_q_return[TOC_NONURGENT] = to; 2854 } 2855 else if (strcasecmp(p, "normal") == 0) 2856 TimeOuts.to_q_return[TOC_NORMAL] = to; 2857 else if (strcasecmp(p, "urgent") == 0) 2858 TimeOuts.to_q_return[TOC_URGENT] = to; 2859 else if (strcasecmp(p, "non-urgent") == 0) 2860 TimeOuts.to_q_return[TOC_NONURGENT] = to; 2861 else 2862 syserr("settimeout: invalid queuereturn subtimeout %s", p); 2863 } 2864 else if (strcasecmp(name, "hoststatus") == 0) 2865 MciInfoTimeout = convtime(val, 'm'); 2866 else 2867 syserr("settimeout: invalid timeout %s", name); 2868 } 2869