1 /* 2 * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1988, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14 #ifndef lint 15 static char id[] = "@(#)$Id: readcf.c,v 8.382.4.27 2000/09/28 01:31:16 gshapiro Exp $"; 16 #endif /* ! lint */ 17 18 #include <sendmail.h> 19 20 21 #if NETINET || NETINET6 22 # include <arpa/inet.h> 23 #endif /* NETINET || NETINET6 */ 24 25 #define SECONDS 26 #define MINUTES * 60 27 #define HOUR * 3600 28 #define HOURS HOUR 29 30 static void fileclass __P((int, char *, char *, bool, bool)); 31 static char **makeargv __P((char *)); 32 static void settimeout __P((char *, char *, bool)); 33 static void toomany __P((int, int)); 34 35 /* 36 ** READCF -- read configuration file. 37 ** 38 ** This routine reads the configuration file and builds the internal 39 ** form. 40 ** 41 ** The file is formatted as a sequence of lines, each taken 42 ** atomically. The first character of each line describes how 43 ** the line is to be interpreted. The lines are: 44 ** Dxval Define macro x to have value val. 45 ** Cxword Put word into class x. 46 ** Fxfile [fmt] Read file for lines to put into 47 ** class x. Use scanf string 'fmt' 48 ** or "%s" if not present. Fmt should 49 ** only produce one string-valued result. 50 ** Hname: value Define header with field-name 'name' 51 ** and value as specified; this will be 52 ** macro expanded immediately before 53 ** use. 54 ** Sn Use rewriting set n. 55 ** Rlhs rhs Rewrite addresses that match lhs to 56 ** be rhs. 57 ** Mn arg=val... Define mailer. n is the internal name. 58 ** Args specify mailer parameters. 59 ** Oxvalue Set option x to value. 60 ** Pname=value Set precedence name to value. 61 ** Vversioncode[/vendorcode] 62 ** Version level/vendor name of 63 ** configuration syntax. 64 ** Kmapname mapclass arguments.... 65 ** Define keyed lookup of a given class. 66 ** Arguments are class dependent. 67 ** Eenvar=value Set the environment value to the given value. 68 ** 69 ** Parameters: 70 ** cfname -- configuration file name. 71 ** safe -- TRUE if this is the system config file; 72 ** FALSE otherwise. 73 ** e -- the main envelope. 74 ** 75 ** Returns: 76 ** none. 77 ** 78 ** Side Effects: 79 ** Builds several internal tables. 80 */ 81 82 void 83 readcf(cfname, safe, e) 84 char *cfname; 85 bool safe; 86 register ENVELOPE *e; 87 { 88 FILE *cf; 89 int ruleset = -1; 90 char *q; 91 struct rewrite *rwp = NULL; 92 char *bp; 93 auto char *ep; 94 int nfuzzy; 95 char *file; 96 bool optional; 97 int mid; 98 register char *p; 99 long sff = SFF_OPENASROOT; 100 struct stat statb; 101 char buf[MAXLINE]; 102 char exbuf[MAXLINE]; 103 char pvpbuf[MAXLINE + MAXATOM]; 104 static char *null_list[1] = { NULL }; 105 extern u_char TokTypeNoC[]; 106 107 FileName = cfname; 108 LineNumber = 0; 109 110 if (DontLockReadFiles) 111 sff |= SFF_NOLOCK; 112 cf = safefopen(cfname, O_RDONLY, 0444, sff); 113 if (cf == NULL) 114 { 115 syserr("cannot open"); 116 finis(FALSE, EX_OSFILE); 117 } 118 119 if (fstat(fileno(cf), &statb) < 0) 120 { 121 syserr("cannot fstat"); 122 finis(FALSE, EX_OSFILE); 123 } 124 125 if (!S_ISREG(statb.st_mode)) 126 { 127 syserr("not a plain file"); 128 finis(FALSE, EX_OSFILE); 129 } 130 131 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 132 { 133 if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS) 134 fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 135 FileName); 136 if (LogLevel > 0) 137 sm_syslog(LOG_CRIT, NOQID, 138 "%s: WARNING: dangerous write permissions", 139 FileName); 140 } 141 142 #ifdef XLA 143 xla_zero(); 144 #endif /* XLA */ 145 146 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 147 { 148 if (bp[0] == '#') 149 { 150 if (bp != buf) 151 free(bp); 152 continue; 153 } 154 155 /* do macro expansion mappings */ 156 translate_dollars(bp); 157 158 /* interpret this line */ 159 errno = 0; 160 switch (bp[0]) 161 { 162 case '\0': 163 case '#': /* comment */ 164 break; 165 166 case 'R': /* rewriting rule */ 167 if (ruleset < 0) 168 { 169 syserr("missing valid ruleset for \"%s\"", bp); 170 break; 171 } 172 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 173 continue; 174 175 if (*p == '\0') 176 { 177 syserr("invalid rewrite line \"%s\" (tab expected)", bp); 178 break; 179 } 180 181 /* allocate space for the rule header */ 182 if (rwp == NULL) 183 { 184 RewriteRules[ruleset] = rwp = 185 (struct rewrite *) xalloc(sizeof *rwp); 186 } 187 else 188 { 189 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 190 rwp = rwp->r_next; 191 } 192 rwp->r_next = NULL; 193 194 /* expand and save the LHS */ 195 *p = '\0'; 196 expand(&bp[1], exbuf, sizeof exbuf, e); 197 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, 198 sizeof pvpbuf, NULL, 199 ConfigLevel >= 9 ? TokTypeNoC : NULL); 200 nfuzzy = 0; 201 if (rwp->r_lhs != NULL) 202 { 203 register char **ap; 204 205 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 206 207 /* count the number of fuzzy matches in LHS */ 208 for (ap = rwp->r_lhs; *ap != NULL; ap++) 209 { 210 char *botch; 211 212 botch = NULL; 213 switch (**ap & 0377) 214 { 215 case MATCHZANY: 216 case MATCHANY: 217 case MATCHONE: 218 case MATCHCLASS: 219 case MATCHNCLASS: 220 nfuzzy++; 221 break; 222 223 case MATCHREPL: 224 botch = "$0-$9"; 225 break; 226 227 case CANONUSER: 228 botch = "$:"; 229 break; 230 231 case CALLSUBR: 232 botch = "$>"; 233 break; 234 235 case CONDIF: 236 botch = "$?"; 237 break; 238 239 case CONDFI: 240 botch = "$."; 241 break; 242 243 case HOSTBEGIN: 244 botch = "$["; 245 break; 246 247 case HOSTEND: 248 botch = "$]"; 249 break; 250 251 case LOOKUPBEGIN: 252 botch = "$("; 253 break; 254 255 case LOOKUPEND: 256 botch = "$)"; 257 break; 258 } 259 if (botch != NULL) 260 syserr("Inappropriate use of %s on LHS", 261 botch); 262 } 263 rwp->r_line = LineNumber; 264 } 265 else 266 { 267 syserr("R line: null LHS"); 268 rwp->r_lhs = null_list; 269 } 270 271 /* expand and save the RHS */ 272 while (*++p == '\t') 273 continue; 274 q = p; 275 while (*p != '\0' && *p != '\t') 276 p++; 277 *p = '\0'; 278 expand(q, exbuf, sizeof exbuf, e); 279 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, 280 sizeof pvpbuf, NULL, 281 ConfigLevel >= 9 ? TokTypeNoC : NULL); 282 if (rwp->r_rhs != NULL) 283 { 284 register char **ap; 285 286 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 287 288 /* check no out-of-bounds replacements */ 289 nfuzzy += '0'; 290 for (ap = rwp->r_rhs; *ap != NULL; ap++) 291 { 292 char *botch; 293 294 botch = NULL; 295 switch (**ap & 0377) 296 { 297 case MATCHREPL: 298 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 299 { 300 syserr("replacement $%c out of bounds", 301 (*ap)[1]); 302 } 303 break; 304 305 case MATCHZANY: 306 botch = "$*"; 307 break; 308 309 case MATCHANY: 310 botch = "$+"; 311 break; 312 313 case MATCHONE: 314 botch = "$-"; 315 break; 316 317 case MATCHCLASS: 318 botch = "$="; 319 break; 320 321 case MATCHNCLASS: 322 botch = "$~"; 323 break; 324 } 325 if (botch != NULL) 326 syserr("Inappropriate use of %s on RHS", 327 botch); 328 } 329 } 330 else 331 { 332 syserr("R line: null RHS"); 333 rwp->r_rhs = null_list; 334 } 335 break; 336 337 case 'S': /* select rewriting set */ 338 expand(&bp[1], exbuf, sizeof exbuf, e); 339 ruleset = strtorwset(exbuf, NULL, ST_ENTER); 340 if (ruleset < 0) 341 break; 342 343 rwp = RewriteRules[ruleset]; 344 if (rwp != NULL) 345 { 346 if (OpMode == MD_TEST) 347 printf("WARNING: Ruleset %s has multiple definitions\n", 348 &bp[1]); 349 if (tTd(37, 1)) 350 dprintf("WARNING: Ruleset %s has multiple definitions\n", 351 &bp[1]); 352 while (rwp->r_next != NULL) 353 rwp = rwp->r_next; 354 } 355 break; 356 357 case 'D': /* macro definition */ 358 mid = macid(&bp[1], &ep); 359 p = munchstring(ep, NULL, '\0'); 360 define(mid, newstr(p), e); 361 break; 362 363 case 'H': /* required header line */ 364 (void) chompheader(&bp[1], CHHDR_DEF, NULL, e); 365 break; 366 367 case 'C': /* word class */ 368 case 'T': /* trusted user (set class `t') */ 369 if (bp[0] == 'C') 370 { 371 mid = macid(&bp[1], &ep); 372 expand(ep, exbuf, sizeof exbuf, e); 373 p = exbuf; 374 } 375 else 376 { 377 mid = 't'; 378 p = &bp[1]; 379 } 380 while (*p != '\0') 381 { 382 register char *wd; 383 char delim; 384 385 while (*p != '\0' && isascii(*p) && isspace(*p)) 386 p++; 387 wd = p; 388 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 389 p++; 390 delim = *p; 391 *p = '\0'; 392 if (wd[0] != '\0') 393 setclass(mid, wd); 394 *p = delim; 395 } 396 break; 397 398 case 'F': /* word class from file */ 399 mid = macid(&bp[1], &ep); 400 for (p = ep; isascii(*p) && isspace(*p); ) 401 p++; 402 if (p[0] == '-' && p[1] == 'o') 403 { 404 optional = TRUE; 405 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 406 p++; 407 while (isascii(*p) && isspace(*p)) 408 p++; 409 } 410 else 411 optional = FALSE; 412 413 file = p; 414 q = p; 415 while (*q != '\0' && !(isascii(*q) && isspace(*q))) 416 q++; 417 if (*file == '|') 418 p = "%s"; 419 else 420 { 421 p = q; 422 if (*p == '\0') 423 p = "%s"; 424 else 425 { 426 *p = '\0'; 427 while (isascii(*++p) && isspace(*p)) 428 continue; 429 } 430 } 431 fileclass(mid, file, p, safe, optional); 432 break; 433 434 #ifdef XLA 435 case 'L': /* extended load average description */ 436 xla_init(&bp[1]); 437 break; 438 #endif /* XLA */ 439 440 #if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) 441 case 'L': /* lookup macro */ 442 case 'G': /* lookup class */ 443 /* reserved for Sun -- NIS+ database lookup */ 444 if (VendorCode != VENDOR_SUN) 445 goto badline; 446 sun_lg_config_line(bp, e); 447 break; 448 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */ 449 450 case 'M': /* define mailer */ 451 makemailer(&bp[1]); 452 break; 453 454 case 'O': /* set option */ 455 setoption(bp[1], &bp[2], safe, FALSE, e); 456 break; 457 458 case 'P': /* set precedence */ 459 if (NumPriorities >= MAXPRIORITIES) 460 { 461 toomany('P', MAXPRIORITIES); 462 break; 463 } 464 for (p = &bp[1]; *p != '\0' && *p != '='; p++) 465 continue; 466 if (*p == '\0') 467 goto badline; 468 *p = '\0'; 469 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 470 Priorities[NumPriorities].pri_val = atoi(++p); 471 NumPriorities++; 472 break; 473 474 case 'V': /* configuration syntax version */ 475 for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 476 continue; 477 if (!isascii(*p) || !isdigit(*p)) 478 { 479 syserr("invalid argument to V line: \"%.20s\"", 480 &bp[1]); 481 break; 482 } 483 ConfigLevel = strtol(p, &ep, 10); 484 485 /* 486 ** Do heuristic tweaking for back compatibility. 487 */ 488 489 if (ConfigLevel >= 5) 490 { 491 /* level 5 configs have short name in $w */ 492 p = macvalue('w', e); 493 if (p != NULL && (p = strchr(p, '.')) != NULL) 494 *p = '\0'; 495 define('w', macvalue('w', e), e); 496 } 497 if (ConfigLevel >= 6) 498 { 499 ColonOkInAddr = FALSE; 500 } 501 502 /* 503 ** Look for vendor code. 504 */ 505 506 if (*ep++ == '/') 507 { 508 /* extract vendor code */ 509 for (p = ep; isascii(*p) && isalpha(*p); ) 510 p++; 511 *p = '\0'; 512 513 if (!setvendor(ep)) 514 syserr("invalid V line vendor code: \"%s\"", 515 ep); 516 } 517 break; 518 519 case 'K': 520 expand(&bp[1], exbuf, sizeof exbuf, e); 521 (void) makemapentry(exbuf); 522 break; 523 524 case 'E': 525 p = strchr(bp, '='); 526 if (p != NULL) 527 *p++ = '\0'; 528 setuserenv(&bp[1], p); 529 break; 530 531 #if _FFR_MILTER 532 case 'X': /* mail filter */ 533 milter_setup(&bp[1]); 534 break; 535 #endif /* _FFR_MILTER */ 536 537 default: 538 badline: 539 syserr("unknown configuration line \"%s\"", bp); 540 } 541 if (bp != buf) 542 free(bp); 543 } 544 if (ferror(cf)) 545 { 546 syserr("I/O read error"); 547 finis(FALSE, EX_OSFILE); 548 } 549 (void) fclose(cf); 550 FileName = NULL; 551 552 /* initialize host maps from local service tables */ 553 inithostmaps(); 554 555 /* initialize daemon (if not defined yet) */ 556 initdaemon(); 557 558 /* determine if we need to do special name-server frotz */ 559 { 560 int nmaps; 561 char *maptype[MAXMAPSTACK]; 562 short mapreturn[MAXMAPACTIONS]; 563 564 nmaps = switch_map_find("hosts", maptype, mapreturn); 565 UseNameServer = FALSE; 566 if (nmaps > 0 && nmaps <= MAXMAPSTACK) 567 { 568 register int mapno; 569 570 for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++) 571 { 572 if (strcmp(maptype[mapno], "dns") == 0) 573 UseNameServer = TRUE; 574 } 575 } 576 577 #ifdef HESIOD 578 nmaps = switch_map_find("passwd", maptype, mapreturn); 579 UseHesiod = FALSE; 580 if (nmaps > 0 && nmaps <= MAXMAPSTACK) 581 { 582 register int mapno; 583 584 for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++) 585 { 586 if (strcmp(maptype[mapno], "hesiod") == 0) 587 UseHesiod = TRUE; 588 } 589 } 590 #endif /* HESIOD */ 591 } 592 } 593 /* 594 ** TRANSLATE_DOLLARS -- convert $x into internal form 595 ** 596 ** Actually does all appropriate pre-processing of a config line 597 ** to turn it into internal form. 598 ** 599 ** Parameters: 600 ** bp -- the buffer to translate. 601 ** 602 ** Returns: 603 ** None. The buffer is translated in place. Since the 604 ** translations always make the buffer shorter, this is 605 ** safe without a size parameter. 606 */ 607 608 void 609 translate_dollars(bp) 610 char *bp; 611 { 612 register char *p; 613 auto char *ep; 614 615 for (p = bp; *p != '\0'; p++) 616 { 617 if (*p == '#' && p > bp && ConfigLevel >= 3) 618 { 619 /* this is an on-line comment */ 620 register char *e; 621 622 switch (*--p & 0377) 623 { 624 case MACROEXPAND: 625 /* it's from $# -- let it go through */ 626 p++; 627 break; 628 629 case '\\': 630 /* it's backslash escaped */ 631 (void) strlcpy(p, p + 1, strlen(p)); 632 break; 633 634 default: 635 /* delete leading white space */ 636 while (isascii(*p) && isspace(*p) && 637 *p != '\n' && p > bp) 638 p--; 639 if ((e = strchr(++p, '\n')) != NULL) 640 (void) strlcpy(p, e, strlen(p)); 641 else 642 *p-- = '\0'; 643 break; 644 } 645 continue; 646 } 647 648 if (*p != '$' || p[1] == '\0') 649 continue; 650 651 if (p[1] == '$') 652 { 653 /* actual dollar sign.... */ 654 (void) strlcpy(p, p + 1, strlen(p)); 655 continue; 656 } 657 658 /* convert to macro expansion character */ 659 *p++ = MACROEXPAND; 660 661 /* special handling for $=, $~, $&, and $? */ 662 if (*p == '=' || *p == '~' || *p == '&' || *p == '?') 663 p++; 664 665 /* convert macro name to code */ 666 *p = macid(p, &ep); 667 if (ep != p + 1) 668 (void) strlcpy(p + 1, ep, strlen(p + 1)); 669 } 670 671 /* strip trailing white space from the line */ 672 while (--p > bp && isascii(*p) && isspace(*p)) 673 *p = '\0'; 674 } 675 /* 676 ** TOOMANY -- signal too many of some option 677 ** 678 ** Parameters: 679 ** id -- the id of the error line 680 ** maxcnt -- the maximum possible values 681 ** 682 ** Returns: 683 ** none. 684 ** 685 ** Side Effects: 686 ** gives a syserr. 687 */ 688 689 static void 690 toomany(id, maxcnt) 691 int id; 692 int maxcnt; 693 { 694 syserr("too many %c lines, %d max", id, maxcnt); 695 } 696 /* 697 ** FILECLASS -- read members of a class from a file 698 ** 699 ** Parameters: 700 ** class -- class to define. 701 ** filename -- name of file to read. 702 ** fmt -- scanf string to use for match. 703 ** safe -- if set, this is a safe read. 704 ** optional -- if set, it is not an error for the file to 705 ** not exist. 706 ** 707 ** Returns: 708 ** none 709 ** 710 ** Side Effects: 711 ** 712 ** puts all lines in filename that match a scanf into 713 ** the named class. 714 */ 715 716 static void 717 fileclass(class, filename, fmt, safe, optional) 718 int class; 719 char *filename; 720 char *fmt; 721 bool safe; 722 bool optional; 723 { 724 FILE *f; 725 long sff; 726 pid_t pid; 727 register char *p; 728 char buf[MAXLINE]; 729 730 if (tTd(37, 2)) 731 dprintf("fileclass(%s, fmt=%s)\n", filename, fmt); 732 733 if (filename[0] == '|') 734 { 735 auto int fd; 736 int i; 737 char *argv[MAXPV + 1]; 738 739 i = 0; 740 for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t")) 741 { 742 if (i >= MAXPV) 743 break; 744 argv[i++] = p; 745 } 746 argv[i] = NULL; 747 pid = prog_open(argv, &fd, CurEnv); 748 if (pid < 0) 749 f = NULL; 750 else 751 f = fdopen(fd, "r"); 752 } 753 else 754 { 755 pid = -1; 756 sff = SFF_REGONLY; 757 if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail)) 758 sff |= SFF_SAFEDIRPATH; 759 if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR, 760 DontBlameSendmail)) 761 sff |= SFF_NOWLINK; 762 if (safe) 763 sff |= SFF_OPENASROOT; 764 if (DontLockReadFiles) 765 sff |= SFF_NOLOCK; 766 f = safefopen(filename, O_RDONLY, 0, sff); 767 } 768 if (f == NULL) 769 { 770 if (!optional) 771 syserr("fileclass: cannot open '%s'", filename); 772 return; 773 } 774 775 while (fgets(buf, sizeof buf, f) != NULL) 776 { 777 #if SCANF 778 char wordbuf[MAXLINE + 1]; 779 #endif /* SCANF */ 780 781 if (buf[0] == '#') 782 continue; 783 #if SCANF 784 if (sscanf(buf, fmt, wordbuf) != 1) 785 continue; 786 p = wordbuf; 787 #else /* SCANF */ 788 p = buf; 789 #endif /* SCANF */ 790 791 /* 792 ** Break up the match into words. 793 */ 794 795 while (*p != '\0') 796 { 797 register char *q; 798 799 /* strip leading spaces */ 800 while (isascii(*p) && isspace(*p)) 801 p++; 802 if (*p == '\0') 803 break; 804 805 /* find the end of the word */ 806 q = p; 807 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 808 p++; 809 if (*p != '\0') 810 *p++ = '\0'; 811 812 /* enter the word in the symbol table */ 813 setclass(class, q); 814 } 815 } 816 817 (void) fclose(f); 818 if (pid > 0) 819 (void) waitfor(pid); 820 } 821 /* 822 ** MAKEMAILER -- define a new mailer. 823 ** 824 ** Parameters: 825 ** line -- description of mailer. This is in labeled 826 ** fields. The fields are: 827 ** A -- the argv for this mailer 828 ** C -- the character set for MIME conversions 829 ** D -- the directory to run in 830 ** E -- the eol string 831 ** F -- the flags associated with the mailer 832 ** L -- the maximum line length 833 ** M -- the maximum message size 834 ** N -- the niceness at which to run 835 ** P -- the path to the mailer 836 ** R -- the recipient rewriting set 837 ** S -- the sender rewriting set 838 ** T -- the mailer type (for DSNs) 839 ** U -- the uid to run as 840 ** W -- the time to wait at the end 841 ** The first word is the canonical name of the mailer. 842 ** 843 ** Returns: 844 ** none. 845 ** 846 ** Side Effects: 847 ** enters the mailer into the mailer table. 848 */ 849 850 void 851 makemailer(line) 852 char *line; 853 { 854 register char *p; 855 register struct mailer *m; 856 register STAB *s; 857 int i; 858 char fcode; 859 auto char *endp; 860 extern int NextMailer; 861 862 /* allocate a mailer and set up defaults */ 863 m = (struct mailer *) xalloc(sizeof *m); 864 memset((char *) m, '\0', sizeof *m); 865 866 /* collect the mailer name */ 867 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 868 continue; 869 if (*p != '\0') 870 *p++ = '\0'; 871 if (line[0] == '\0') 872 syserr("name required for mailer"); 873 m->m_name = newstr(line); 874 875 /* now scan through and assign info from the fields */ 876 while (*p != '\0') 877 { 878 auto char *delimptr; 879 880 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 881 p++; 882 883 /* p now points to field code */ 884 fcode = *p; 885 while (*p != '\0' && *p != '=' && *p != ',') 886 p++; 887 if (*p++ != '=') 888 { 889 syserr("mailer %s: `=' expected", m->m_name); 890 return; 891 } 892 while (isascii(*p) && isspace(*p)) 893 p++; 894 895 /* p now points to the field body */ 896 p = munchstring(p, &delimptr, ','); 897 898 /* install the field into the mailer struct */ 899 switch (fcode) 900 { 901 case 'P': /* pathname */ 902 if (*p == '\0') 903 syserr("mailer %s: empty path name", m->m_name); 904 m->m_mailer = newstr(p); 905 break; 906 907 case 'F': /* flags */ 908 for (; *p != '\0'; p++) 909 if (!(isascii(*p) && isspace(*p))) 910 setbitn(*p, m->m_flags); 911 break; 912 913 case 'S': /* sender rewriting ruleset */ 914 case 'R': /* recipient rewriting ruleset */ 915 i = strtorwset(p, &endp, ST_ENTER); 916 if (i < 0) 917 return; 918 if (fcode == 'S') 919 m->m_sh_rwset = m->m_se_rwset = i; 920 else 921 m->m_rh_rwset = m->m_re_rwset = i; 922 923 p = endp; 924 if (*p++ == '/') 925 { 926 i = strtorwset(p, NULL, ST_ENTER); 927 if (i < 0) 928 return; 929 if (fcode == 'S') 930 m->m_sh_rwset = i; 931 else 932 m->m_rh_rwset = i; 933 } 934 break; 935 936 case 'E': /* end of line string */ 937 if (*p == '\0') 938 syserr("mailer %s: null end-of-line string", 939 m->m_name); 940 m->m_eol = newstr(p); 941 break; 942 943 case 'A': /* argument vector */ 944 if (*p == '\0') 945 syserr("mailer %s: null argument vector", 946 m->m_name); 947 m->m_argv = makeargv(p); 948 break; 949 950 case 'M': /* maximum message size */ 951 m->m_maxsize = atol(p); 952 break; 953 954 case 'm': /* maximum messages per connection */ 955 m->m_maxdeliveries = atoi(p); 956 break; 957 958 #if _FFR_DYNAMIC_TOBUF 959 case 'r': /* max recipient per envelope */ 960 m->m_maxrcpt = atoi(p); 961 break; 962 #endif /* _FFR_DYNAMIC_TOBUF */ 963 964 case 'L': /* maximum line length */ 965 m->m_linelimit = atoi(p); 966 if (m->m_linelimit < 0) 967 m->m_linelimit = 0; 968 break; 969 970 case 'N': /* run niceness */ 971 m->m_nice = atoi(p); 972 break; 973 974 case 'D': /* working directory */ 975 if (*p == '\0') 976 syserr("mailer %s: null working directory", 977 m->m_name); 978 m->m_execdir = newstr(p); 979 break; 980 981 case 'C': /* default charset */ 982 if (*p == '\0') 983 syserr("mailer %s: null charset", m->m_name); 984 m->m_defcharset = newstr(p); 985 break; 986 987 case 'T': /* MTA-Name/Address/Diagnostic types */ 988 /* extract MTA name type; default to "dns" */ 989 m->m_mtatype = newstr(p); 990 p = strchr(m->m_mtatype, '/'); 991 if (p != NULL) 992 { 993 *p++ = '\0'; 994 if (*p == '\0') 995 p = NULL; 996 } 997 if (*m->m_mtatype == '\0') 998 m->m_mtatype = "dns"; 999 1000 /* extract address type; default to "rfc822" */ 1001 m->m_addrtype = p; 1002 if (p != NULL) 1003 p = strchr(p, '/'); 1004 if (p != NULL) 1005 { 1006 *p++ = '\0'; 1007 if (*p == '\0') 1008 p = NULL; 1009 } 1010 if (m->m_addrtype == NULL || *m->m_addrtype == '\0') 1011 m->m_addrtype = "rfc822"; 1012 1013 /* extract diagnostic type; default to "smtp" */ 1014 m->m_diagtype = p; 1015 if (m->m_diagtype == NULL || *m->m_diagtype == '\0') 1016 m->m_diagtype = "smtp"; 1017 break; 1018 1019 case 'U': /* user id */ 1020 if (isascii(*p) && !isdigit(*p)) 1021 { 1022 char *q = p; 1023 struct passwd *pw; 1024 1025 while (*p != '\0' && isascii(*p) && 1026 (isalnum(*p) || strchr("-_", *p) != NULL)) 1027 p++; 1028 while (isascii(*p) && isspace(*p)) 1029 *p++ = '\0'; 1030 if (*p != '\0') 1031 *p++ = '\0'; 1032 if (*q == '\0') 1033 syserr("mailer %s: null user name", 1034 m->m_name); 1035 pw = sm_getpwnam(q); 1036 if (pw == NULL) 1037 syserr("readcf: mailer U= flag: unknown user %s", q); 1038 else 1039 { 1040 m->m_uid = pw->pw_uid; 1041 m->m_gid = pw->pw_gid; 1042 } 1043 } 1044 else 1045 { 1046 auto char *q; 1047 1048 m->m_uid = strtol(p, &q, 0); 1049 p = q; 1050 while (isascii(*p) && isspace(*p)) 1051 p++; 1052 if (*p != '\0') 1053 p++; 1054 } 1055 while (isascii(*p) && isspace(*p)) 1056 p++; 1057 if (*p == '\0') 1058 break; 1059 if (isascii(*p) && !isdigit(*p)) 1060 { 1061 char *q = p; 1062 struct group *gr; 1063 1064 while (isascii(*p) && isalnum(*p)) 1065 p++; 1066 *p++ = '\0'; 1067 if (*q == '\0') 1068 syserr("mailer %s: null group name", 1069 m->m_name); 1070 gr = getgrnam(q); 1071 if (gr == NULL) 1072 syserr("readcf: mailer U= flag: unknown group %s", q); 1073 else 1074 m->m_gid = gr->gr_gid; 1075 } 1076 else 1077 { 1078 m->m_gid = strtol(p, NULL, 0); 1079 } 1080 break; 1081 1082 case 'W': /* wait timeout */ 1083 m->m_wait = convtime(p, 's'); 1084 break; 1085 1086 case '/': /* new root directory */ 1087 if (*p == '\0') 1088 syserr("mailer %s: null root directory", 1089 m->m_name); 1090 else 1091 m->m_rootdir = newstr(p); 1092 break; 1093 1094 default: 1095 syserr("M%s: unknown mailer equate %c=", 1096 m->m_name, fcode); 1097 break; 1098 } 1099 1100 p = delimptr; 1101 } 1102 1103 /* do some rationality checking */ 1104 if (m->m_argv == NULL) 1105 { 1106 syserr("M%s: A= argument required", m->m_name); 1107 return; 1108 } 1109 if (m->m_mailer == NULL) 1110 { 1111 syserr("M%s: P= argument required", m->m_name); 1112 return; 1113 } 1114 1115 if (NextMailer >= MAXMAILERS) 1116 { 1117 syserr("too many mailers defined (%d max)", MAXMAILERS); 1118 return; 1119 } 1120 1121 #if _FFR_DYNAMIC_TOBUF 1122 if (m->m_maxrcpt <= 0) 1123 m->m_maxrcpt = DEFAULT_MAX_RCPT; 1124 #endif /* _FFR_DYNAMIC_TOBUF */ 1125 1126 /* do some heuristic cleanup for back compatibility */ 1127 if (bitnset(M_LIMITS, m->m_flags)) 1128 { 1129 if (m->m_linelimit == 0) 1130 m->m_linelimit = SMTPLINELIM; 1131 if (ConfigLevel < 2) 1132 setbitn(M_7BITS, m->m_flags); 1133 } 1134 1135 if (strcmp(m->m_mailer, "[TCP]") == 0) 1136 { 1137 #if _FFR_REMOVE_TCP_MAILER_PATH 1138 syserr("M%s: P=[TCP] is deprecated, use P=[IPC] instead\n", 1139 m->m_name); 1140 #else /* _FFR_REMOVE_TCP_MAILER_PATH */ 1141 printf("M%s: Warning: P=[TCP] is deprecated, use P=[IPC] instead\n", 1142 m->m_name); 1143 #endif /* _FFR_REMOVE_TCP_MAILER_PATH */ 1144 } 1145 1146 if (strcmp(m->m_mailer, "[IPC]") == 0 1147 #if !_FFR_REMOVE_TCP_MAILER_PATH 1148 || strcmp(m->m_mailer, "[TCP]") == 0 1149 #endif /* !_FFR_REMOVE_TCP_MAILER_PATH */ 1150 ) 1151 { 1152 /* Use the second argument for host or path to socket */ 1153 if (m->m_argv[0] == NULL || m->m_argv[1] == NULL || 1154 m->m_argv[1][0] == '\0') 1155 { 1156 syserr("M%s: too few parameters for %s mailer", 1157 m->m_name, m->m_mailer); 1158 } 1159 if (strcmp(m->m_argv[0], "TCP") != 0 1160 #if NETUNIX 1161 && strcmp(m->m_argv[0], "FILE") != 0 1162 #endif /* NETUNIX */ 1163 #if !_FFR_DEPRECATE_IPC_MAILER_ARG 1164 && strcmp(m->m_argv[0], "IPC") != 0 1165 #endif /* !_FFR_DEPRECATE_IPC_MAILER_ARG */ 1166 ) 1167 { 1168 printf("M%s: Warning: first argument in %s mailer must be %s\n", 1169 m->m_name, m->m_mailer, 1170 #if NETUNIX 1171 "TCP or FILE" 1172 #else /* NETUNIX */ 1173 "TCP" 1174 #endif /* NETUNIX */ 1175 ); 1176 } 1177 1178 } 1179 else if (strcmp(m->m_mailer, "[FILE]") == 0) 1180 { 1181 /* Use the second argument for filename */ 1182 if (m->m_argv[0] == NULL || m->m_argv[1] == NULL || 1183 m->m_argv[2] != NULL) 1184 { 1185 syserr("M%s: too %s parameters for [FILE] mailer", 1186 m->m_name, 1187 (m->m_argv[0] == NULL || 1188 m->m_argv[1] == NULL) ? "few" : "many"); 1189 } 1190 else if (strcmp(m->m_argv[0], "FILE") != 0) 1191 { 1192 syserr("M%s: first argument in [FILE] mailer must be FILE", 1193 m->m_name); 1194 } 1195 } 1196 1197 if (strcmp(m->m_mailer, "[IPC]") == 0 || 1198 strcmp(m->m_mailer, "[TCP]") == 0) 1199 { 1200 if (m->m_mtatype == NULL) 1201 m->m_mtatype = "dns"; 1202 if (m->m_addrtype == NULL) 1203 m->m_addrtype = "rfc822"; 1204 if (m->m_diagtype == NULL) 1205 { 1206 if (m->m_argv[0] != NULL && 1207 strcmp(m->m_argv[0], "FILE") == 0) 1208 m->m_diagtype = "x-unix"; 1209 else 1210 m->m_diagtype = "smtp"; 1211 } 1212 } 1213 1214 if (m->m_eol == NULL) 1215 { 1216 char **pp; 1217 1218 /* default for SMTP is \r\n; use \n for local delivery */ 1219 for (pp = m->m_argv; *pp != NULL; pp++) 1220 { 1221 for (p = *pp; *p != '\0'; ) 1222 { 1223 if ((*p++ & 0377) == MACROEXPAND && *p == 'u') 1224 break; 1225 } 1226 if (*p != '\0') 1227 break; 1228 } 1229 if (*pp == NULL) 1230 m->m_eol = "\r\n"; 1231 else 1232 m->m_eol = "\n"; 1233 } 1234 1235 /* enter the mailer into the symbol table */ 1236 s = stab(m->m_name, ST_MAILER, ST_ENTER); 1237 if (s->s_mailer != NULL) 1238 { 1239 i = s->s_mailer->m_mno; 1240 free(s->s_mailer); 1241 } 1242 else 1243 { 1244 i = NextMailer++; 1245 } 1246 Mailer[i] = s->s_mailer = m; 1247 m->m_mno = i; 1248 } 1249 /* 1250 ** MUNCHSTRING -- translate a string into internal form. 1251 ** 1252 ** Parameters: 1253 ** p -- the string to munch. 1254 ** delimptr -- if non-NULL, set to the pointer of the 1255 ** field delimiter character. 1256 ** delim -- the delimiter for the field. 1257 ** 1258 ** Returns: 1259 ** the munched string. 1260 ** 1261 ** Side Effects: 1262 ** the munched string is a local static buffer. 1263 ** it must be copied before the function is called again. 1264 */ 1265 1266 char * 1267 munchstring(p, delimptr, delim) 1268 register char *p; 1269 char **delimptr; 1270 int delim; 1271 { 1272 register char *q; 1273 bool backslash = FALSE; 1274 bool quotemode = FALSE; 1275 static char buf[MAXLINE]; 1276 1277 for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++) 1278 { 1279 if (backslash) 1280 { 1281 /* everything is roughly literal */ 1282 backslash = FALSE; 1283 switch (*p) 1284 { 1285 case 'r': /* carriage return */ 1286 *q++ = '\r'; 1287 continue; 1288 1289 case 'n': /* newline */ 1290 *q++ = '\n'; 1291 continue; 1292 1293 case 'f': /* form feed */ 1294 *q++ = '\f'; 1295 continue; 1296 1297 case 'b': /* backspace */ 1298 *q++ = '\b'; 1299 continue; 1300 } 1301 *q++ = *p; 1302 } 1303 else 1304 { 1305 if (*p == '\\') 1306 backslash = TRUE; 1307 else if (*p == '"') 1308 quotemode = !quotemode; 1309 else if (quotemode || *p != delim) 1310 *q++ = *p; 1311 else 1312 break; 1313 } 1314 } 1315 1316 if (delimptr != NULL) 1317 *delimptr = p; 1318 *q++ = '\0'; 1319 return buf; 1320 } 1321 /* 1322 ** MAKEARGV -- break up a string into words 1323 ** 1324 ** Parameters: 1325 ** p -- the string to break up. 1326 ** 1327 ** Returns: 1328 ** a char **argv (dynamically allocated) 1329 ** 1330 ** Side Effects: 1331 ** munges p. 1332 */ 1333 1334 static char ** 1335 makeargv(p) 1336 register char *p; 1337 { 1338 char *q; 1339 int i; 1340 char **avp; 1341 char *argv[MAXPV + 1]; 1342 1343 /* take apart the words */ 1344 i = 0; 1345 while (*p != '\0' && i < MAXPV) 1346 { 1347 q = p; 1348 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1349 p++; 1350 while (isascii(*p) && isspace(*p)) 1351 *p++ = '\0'; 1352 argv[i++] = newstr(q); 1353 } 1354 argv[i++] = NULL; 1355 1356 /* now make a copy of the argv */ 1357 avp = (char **) xalloc(sizeof *avp * i); 1358 memmove((char *) avp, (char *) argv, sizeof *avp * i); 1359 1360 return avp; 1361 } 1362 /* 1363 ** PRINTRULES -- print rewrite rules (for debugging) 1364 ** 1365 ** Parameters: 1366 ** none. 1367 ** 1368 ** Returns: 1369 ** none. 1370 ** 1371 ** Side Effects: 1372 ** prints rewrite rules. 1373 */ 1374 1375 void 1376 printrules() 1377 { 1378 register struct rewrite *rwp; 1379 register int ruleset; 1380 1381 for (ruleset = 0; ruleset < 10; ruleset++) 1382 { 1383 if (RewriteRules[ruleset] == NULL) 1384 continue; 1385 printf("\n----Rule Set %d:", ruleset); 1386 1387 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 1388 { 1389 printf("\nLHS:"); 1390 printav(rwp->r_lhs); 1391 printf("RHS:"); 1392 printav(rwp->r_rhs); 1393 } 1394 } 1395 } 1396 /* 1397 ** PRINTMAILER -- print mailer structure (for debugging) 1398 ** 1399 ** Parameters: 1400 ** m -- the mailer to print 1401 ** 1402 ** Returns: 1403 ** none. 1404 */ 1405 1406 void 1407 printmailer(m) 1408 register MAILER *m; 1409 { 1410 int j; 1411 1412 printf("mailer %d (%s): P=%s S=", m->m_mno, m->m_name, m->m_mailer); 1413 if (RuleSetNames[m->m_se_rwset] == NULL) 1414 printf("%d/", m->m_se_rwset); 1415 else 1416 printf("%s/", RuleSetNames[m->m_se_rwset]); 1417 if (RuleSetNames[m->m_sh_rwset] == NULL) 1418 printf("%d R=", m->m_sh_rwset); 1419 else 1420 printf("%s R=", RuleSetNames[m->m_sh_rwset]); 1421 if (RuleSetNames[m->m_re_rwset] == NULL) 1422 printf("%d/", m->m_re_rwset); 1423 else 1424 printf("%s/", RuleSetNames[m->m_re_rwset]); 1425 if (RuleSetNames[m->m_rh_rwset] == NULL) 1426 printf("%d ", m->m_rh_rwset); 1427 else 1428 printf("%s ", RuleSetNames[m->m_rh_rwset]); 1429 printf("M=%ld U=%d:%d F=", m->m_maxsize, 1430 (int) m->m_uid, (int) m->m_gid); 1431 for (j = '\0'; j <= '\177'; j++) 1432 if (bitnset(j, m->m_flags)) 1433 (void) putchar(j); 1434 printf(" L=%d E=", m->m_linelimit); 1435 xputs(m->m_eol); 1436 if (m->m_defcharset != NULL) 1437 printf(" C=%s", m->m_defcharset); 1438 printf(" T=%s/%s/%s", 1439 m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype, 1440 m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype, 1441 m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype); 1442 #if _FFR_DYNAMIC_TOBUF 1443 printf(" r=%d", m->m_maxrcpt); 1444 #endif /* _FFR_DYNAMIC_TOBUF */ 1445 if (m->m_argv != NULL) 1446 { 1447 char **a = m->m_argv; 1448 1449 printf(" A="); 1450 while (*a != NULL) 1451 { 1452 if (a != m->m_argv) 1453 printf(" "); 1454 xputs(*a++); 1455 } 1456 } 1457 printf("\n"); 1458 } 1459 /* 1460 ** SETOPTION -- set global processing option 1461 ** 1462 ** Parameters: 1463 ** opt -- option name. 1464 ** val -- option value (as a text string). 1465 ** safe -- set if this came from a configuration file. 1466 ** Some options (if set from the command line) will 1467 ** reset the user id to avoid security problems. 1468 ** sticky -- if set, don't let other setoptions override 1469 ** this value. 1470 ** e -- the main envelope. 1471 ** 1472 ** Returns: 1473 ** none. 1474 ** 1475 ** Side Effects: 1476 ** Sets options as implied by the arguments. 1477 */ 1478 1479 static BITMAP256 StickyOpt; /* set if option is stuck */ 1480 1481 #if NAMED_BIND 1482 1483 static struct resolverflags 1484 { 1485 char *rf_name; /* name of the flag */ 1486 long rf_bits; /* bits to set/clear */ 1487 } ResolverFlags[] = 1488 { 1489 { "debug", RES_DEBUG }, 1490 { "aaonly", RES_AAONLY }, 1491 { "usevc", RES_USEVC }, 1492 { "primary", RES_PRIMARY }, 1493 { "igntc", RES_IGNTC }, 1494 { "recurse", RES_RECURSE }, 1495 { "defnames", RES_DEFNAMES }, 1496 { "stayopen", RES_STAYOPEN }, 1497 { "dnsrch", RES_DNSRCH }, 1498 { "true", 0 }, /* avoid error on old syntax */ 1499 { NULL, 0 } 1500 }; 1501 1502 #endif /* NAMED_BIND */ 1503 1504 #define OI_NONE 0 /* no special treatment */ 1505 #define OI_SAFE 0x0001 /* safe for random people to use */ 1506 #define OI_SUBOPT 0x0002 /* option has suboptions */ 1507 1508 static struct optioninfo 1509 { 1510 char *o_name; /* long name of option */ 1511 u_char o_code; /* short name of option */ 1512 u_short o_flags; /* option flags */ 1513 } OptionTab[] = 1514 { 1515 #if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) 1516 { "RemoteMode", '>', OI_NONE }, 1517 #endif /* defined(SUN_EXTENSIONS) && defined(REMOTE_MODE) */ 1518 { "SevenBitInput", '7', OI_SAFE }, 1519 #if MIME8TO7 1520 { "EightBitMode", '8', OI_SAFE }, 1521 #endif /* MIME8TO7 */ 1522 { "AliasFile", 'A', OI_NONE }, 1523 { "AliasWait", 'a', OI_NONE }, 1524 { "BlankSub", 'B', OI_NONE }, 1525 { "MinFreeBlocks", 'b', OI_SAFE }, 1526 { "CheckpointInterval", 'C', OI_SAFE }, 1527 { "HoldExpensive", 'c', OI_NONE }, 1528 #if !_FFR_REMOVE_AUTOREBUILD 1529 { "AutoRebuildAliases", 'D', OI_NONE }, 1530 #endif /* !_FFR_REMOVE_AUTOREBUILD */ 1531 { "DeliveryMode", 'd', OI_SAFE }, 1532 { "ErrorHeader", 'E', OI_NONE }, 1533 { "ErrorMode", 'e', OI_SAFE }, 1534 { "TempFileMode", 'F', OI_NONE }, 1535 { "SaveFromLine", 'f', OI_NONE }, 1536 { "MatchGECOS", 'G', OI_NONE }, 1537 { "HelpFile", 'H', OI_NONE }, 1538 { "MaxHopCount", 'h', OI_NONE }, 1539 { "ResolverOptions", 'I', OI_NONE }, 1540 { "IgnoreDots", 'i', OI_SAFE }, 1541 { "ForwardPath", 'J', OI_NONE }, 1542 { "SendMimeErrors", 'j', OI_SAFE }, 1543 { "ConnectionCacheSize", 'k', OI_NONE }, 1544 { "ConnectionCacheTimeout", 'K', OI_NONE }, 1545 { "UseErrorsTo", 'l', OI_NONE }, 1546 { "LogLevel", 'L', OI_SAFE }, 1547 { "MeToo", 'm', OI_SAFE }, 1548 { "CheckAliases", 'n', OI_NONE }, 1549 { "OldStyleHeaders", 'o', OI_SAFE }, 1550 { "DaemonPortOptions", 'O', OI_NONE }, 1551 { "PrivacyOptions", 'p', OI_SAFE }, 1552 { "PostmasterCopy", 'P', OI_NONE }, 1553 { "QueueFactor", 'q', OI_NONE }, 1554 { "QueueDirectory", 'Q', OI_NONE }, 1555 { "DontPruneRoutes", 'R', OI_NONE }, 1556 { "Timeout", 'r', OI_SUBOPT }, 1557 { "StatusFile", 'S', OI_NONE }, 1558 { "SuperSafe", 's', OI_SAFE }, 1559 { "QueueTimeout", 'T', OI_NONE }, 1560 { "TimeZoneSpec", 't', OI_NONE }, 1561 { "UserDatabaseSpec", 'U', OI_NONE }, 1562 { "DefaultUser", 'u', OI_NONE }, 1563 { "FallbackMXhost", 'V', OI_NONE }, 1564 { "Verbose", 'v', OI_SAFE }, 1565 { "TryNullMXList", 'w', OI_NONE }, 1566 { "QueueLA", 'x', OI_NONE }, 1567 { "RefuseLA", 'X', OI_NONE }, 1568 { "RecipientFactor", 'y', OI_NONE }, 1569 { "ForkEachJob", 'Y', OI_NONE }, 1570 { "ClassFactor", 'z', OI_NONE }, 1571 { "RetryFactor", 'Z', OI_NONE }, 1572 #define O_QUEUESORTORD 0x81 1573 { "QueueSortOrder", O_QUEUESORTORD, OI_SAFE }, 1574 #define O_HOSTSFILE 0x82 1575 { "HostsFile", O_HOSTSFILE, OI_NONE }, 1576 #define O_MQA 0x83 1577 { "MinQueueAge", O_MQA, OI_SAFE }, 1578 #define O_DEFCHARSET 0x85 1579 { "DefaultCharSet", O_DEFCHARSET, OI_SAFE }, 1580 #define O_SSFILE 0x86 1581 { "ServiceSwitchFile", O_SSFILE, OI_NONE }, 1582 #define O_DIALDELAY 0x87 1583 { "DialDelay", O_DIALDELAY, OI_SAFE }, 1584 #define O_NORCPTACTION 0x88 1585 { "NoRecipientAction", O_NORCPTACTION, OI_SAFE }, 1586 #define O_SAFEFILEENV 0x89 1587 { "SafeFileEnvironment", O_SAFEFILEENV, OI_NONE }, 1588 #define O_MAXMSGSIZE 0x8a 1589 { "MaxMessageSize", O_MAXMSGSIZE, OI_NONE }, 1590 #define O_COLONOKINADDR 0x8b 1591 { "ColonOkInAddr", O_COLONOKINADDR, OI_SAFE }, 1592 #define O_MAXQUEUERUN 0x8c 1593 { "MaxQueueRunSize", O_MAXQUEUERUN, OI_SAFE }, 1594 #define O_MAXCHILDREN 0x8d 1595 { "MaxDaemonChildren", O_MAXCHILDREN, OI_NONE }, 1596 #define O_KEEPCNAMES 0x8e 1597 { "DontExpandCnames", O_KEEPCNAMES, OI_NONE }, 1598 #define O_MUSTQUOTE 0x8f 1599 { "MustQuoteChars", O_MUSTQUOTE, OI_NONE }, 1600 #define O_SMTPGREETING 0x90 1601 { "SmtpGreetingMessage", O_SMTPGREETING, OI_NONE }, 1602 #define O_UNIXFROM 0x91 1603 { "UnixFromLine", O_UNIXFROM, OI_NONE }, 1604 #define O_OPCHARS 0x92 1605 { "OperatorChars", O_OPCHARS, OI_NONE }, 1606 #define O_DONTINITGRPS 0x93 1607 { "DontInitGroups", O_DONTINITGRPS, OI_NONE }, 1608 #define O_SLFH 0x94 1609 { "SingleLineFromHeader", O_SLFH, OI_SAFE }, 1610 #define O_ABH 0x95 1611 { "AllowBogusHELO", O_ABH, OI_SAFE }, 1612 #define O_CONNTHROT 0x97 1613 { "ConnectionRateThrottle", O_CONNTHROT, OI_NONE }, 1614 #define O_UGW 0x99 1615 { "UnsafeGroupWrites", O_UGW, OI_NONE }, 1616 #define O_DBLBOUNCE 0x9a 1617 { "DoubleBounceAddress", O_DBLBOUNCE, OI_NONE }, 1618 #define O_HSDIR 0x9b 1619 { "HostStatusDirectory", O_HSDIR, OI_NONE }, 1620 #define O_SINGTHREAD 0x9c 1621 { "SingleThreadDelivery", O_SINGTHREAD, OI_NONE }, 1622 #define O_RUNASUSER 0x9d 1623 { "RunAsUser", O_RUNASUSER, OI_NONE }, 1624 #define O_DSN_RRT 0x9e 1625 { "RrtImpliesDsn", O_DSN_RRT, OI_NONE }, 1626 #define O_PIDFILE 0x9f 1627 { "PidFile", O_PIDFILE, OI_NONE }, 1628 #define O_DONTBLAMESENDMAIL 0xa0 1629 { "DontBlameSendmail", O_DONTBLAMESENDMAIL, OI_NONE }, 1630 #define O_DPI 0xa1 1631 { "DontProbeInterfaces", O_DPI, OI_NONE }, 1632 #define O_MAXRCPT 0xa2 1633 { "MaxRecipientsPerMessage", O_MAXRCPT, OI_SAFE }, 1634 #define O_DEADLETTER 0xa3 1635 { "DeadLetterDrop", O_DEADLETTER, OI_NONE }, 1636 #if _FFR_DONTLOCKFILESFORREAD_OPTION 1637 # define O_DONTLOCK 0xa4 1638 { "DontLockFilesForRead", O_DONTLOCK, OI_NONE }, 1639 #endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */ 1640 #define O_MAXALIASRCSN 0xa5 1641 { "MaxAliasRecursion", O_MAXALIASRCSN, OI_NONE }, 1642 #define O_CNCTONLYTO 0xa6 1643 { "ConnectOnlyTo", O_CNCTONLYTO, OI_NONE }, 1644 #define O_TRUSTUSER 0xa7 1645 { "TrustedUser", O_TRUSTUSER, OI_NONE }, 1646 #define O_MAXMIMEHDRLEN 0xa8 1647 { "MaxMimeHeaderLength", O_MAXMIMEHDRLEN, OI_NONE }, 1648 #define O_CONTROLSOCKET 0xa9 1649 { "ControlSocketName", O_CONTROLSOCKET, OI_NONE }, 1650 #define O_MAXHDRSLEN 0xaa 1651 { "MaxHeadersLength", O_MAXHDRSLEN, OI_NONE }, 1652 #if _FFR_MAX_FORWARD_ENTRIES 1653 # define O_MAXFORWARD 0xab 1654 { "MaxForwardEntries", O_MAXFORWARD, OI_NONE }, 1655 #endif /* _FFR_MAX_FORWARD_ENTRIES */ 1656 #define O_PROCTITLEPREFIX 0xac 1657 { "ProcessTitlePrefix", O_PROCTITLEPREFIX, OI_NONE }, 1658 #define O_SASLINFO 0xad 1659 #if _FFR_ALLOW_SASLINFO 1660 { "DefaultAuthInfo", O_SASLINFO, OI_SAFE }, 1661 #else /* _FFR_ALLOW_SASLINFO */ 1662 { "DefaultAuthInfo", O_SASLINFO, OI_NONE }, 1663 #endif /* _FFR_ALLOW_SASLINFO */ 1664 #define O_SASLMECH 0xae 1665 { "AuthMechanisms", O_SASLMECH, OI_NONE }, 1666 #define O_CLIENTPORT 0xaf 1667 { "ClientPortOptions", O_CLIENTPORT, OI_NONE }, 1668 #define O_DF_BUFSIZE 0xb0 1669 { "DataFileBufferSize", O_DF_BUFSIZE, OI_NONE }, 1670 #define O_XF_BUFSIZE 0xb1 1671 { "XscriptFileBufferSize", O_XF_BUFSIZE, OI_NONE }, 1672 # define O_LDAPDEFAULTSPEC 0xb2 1673 { "LDAPDefaultSpec", O_LDAPDEFAULTSPEC, OI_NONE }, 1674 #if _FFR_QUEUEDELAY 1675 #define O_QUEUEDELAY 0xb3 1676 { "QueueDelay", O_QUEUEDELAY, OI_NONE }, 1677 #endif /* _FFR_QUEUEDELAY */ 1678 # define O_SRVCERTFILE 0xb4 1679 { "ServerCertFile", O_SRVCERTFILE, OI_NONE }, 1680 # define O_SRVKEYFILE 0xb5 1681 { "Serverkeyfile", O_SRVKEYFILE, OI_NONE }, 1682 # define O_CLTCERTFILE 0xb6 1683 { "ClientCertFile", O_CLTCERTFILE, OI_NONE }, 1684 # define O_CLTKEYFILE 0xb7 1685 { "Clientkeyfile", O_CLTKEYFILE, OI_NONE }, 1686 # define O_CACERTFILE 0xb8 1687 { "CACERTFile", O_CACERTFILE, OI_NONE }, 1688 # define O_CACERTPATH 0xb9 1689 { "CACERTPath", O_CACERTPATH, OI_NONE }, 1690 # define O_DHPARAMS 0xba 1691 { "DHParameters", O_DHPARAMS, OI_NONE }, 1692 #if _FFR_MILTER 1693 #define O_INPUTMILTER 0xbb 1694 { "InputMailFilters", O_INPUTMILTER, OI_NONE }, 1695 #define O_MILTER 0xbc 1696 { "Milter", O_MILTER, OI_SUBOPT }, 1697 #endif /* _FFR_MILTER */ 1698 #define O_SASLOPTS 0xbd 1699 { "AuthOptions", O_SASLOPTS, OI_NONE }, 1700 #if _FFR_QUEUE_FILE_MODE 1701 #define O_QUEUE_FILE_MODE 0xbe 1702 { "QueueFileMode", O_QUEUE_FILE_MODE, OI_NONE }, 1703 #endif /* _FFR_QUEUE_FILE_MODE */ 1704 # if _FFR_TLS_1 1705 # define O_DHPARAMS5 0xbf 1706 { "DHParameters512", O_DHPARAMS5, OI_NONE }, 1707 # define O_CIPHERLIST 0xc0 1708 { "CipherList", O_CIPHERLIST, OI_NONE }, 1709 # endif /* _FFR_TLS_1 */ 1710 # define O_RANDFILE 0xc1 1711 { "RandFile", O_RANDFILE, OI_NONE }, 1712 { NULL, '\0', OI_NONE } 1713 }; 1714 1715 void 1716 setoption(opt, val, safe, sticky, e) 1717 int opt; 1718 char *val; 1719 bool safe; 1720 bool sticky; 1721 register ENVELOPE *e; 1722 { 1723 register char *p; 1724 register struct optioninfo *o; 1725 char *subopt; 1726 int mid; 1727 bool can_setuid = RunAsUid == 0; 1728 auto char *ep; 1729 char buf[50]; 1730 extern bool Warn_Q_option; 1731 #if _FFR_ALLOW_SASLINFO 1732 extern int SubmitMode; 1733 #endif /* _FFR_ALLOW_SASLINFO */ 1734 1735 errno = 0; 1736 if (opt == ' ') 1737 { 1738 /* full word options */ 1739 struct optioninfo *sel; 1740 1741 p = strchr(val, '='); 1742 if (p == NULL) 1743 p = &val[strlen(val)]; 1744 while (*--p == ' ') 1745 continue; 1746 while (*++p == ' ') 1747 *p = '\0'; 1748 if (p == val) 1749 { 1750 syserr("readcf: null option name"); 1751 return; 1752 } 1753 if (*p == '=') 1754 *p++ = '\0'; 1755 while (*p == ' ') 1756 p++; 1757 subopt = strchr(val, '.'); 1758 if (subopt != NULL) 1759 *subopt++ = '\0'; 1760 sel = NULL; 1761 for (o = OptionTab; o->o_name != NULL; o++) 1762 { 1763 if (strncasecmp(o->o_name, val, strlen(val)) != 0) 1764 continue; 1765 if (strlen(o->o_name) == strlen(val)) 1766 { 1767 /* completely specified -- this must be it */ 1768 sel = NULL; 1769 break; 1770 } 1771 if (sel != NULL) 1772 break; 1773 sel = o; 1774 } 1775 if (sel != NULL && o->o_name == NULL) 1776 o = sel; 1777 else if (o->o_name == NULL) 1778 { 1779 syserr("readcf: unknown option name %s", val); 1780 return; 1781 } 1782 else if (sel != NULL) 1783 { 1784 syserr("readcf: ambiguous option name %s (matches %s and %s)", 1785 val, sel->o_name, o->o_name); 1786 return; 1787 } 1788 if (strlen(val) != strlen(o->o_name)) 1789 { 1790 int oldVerbose = Verbose; 1791 1792 Verbose = 1; 1793 message("Option %s used as abbreviation for %s", 1794 val, o->o_name); 1795 Verbose = oldVerbose; 1796 } 1797 opt = o->o_code; 1798 val = p; 1799 } 1800 else 1801 { 1802 for (o = OptionTab; o->o_name != NULL; o++) 1803 { 1804 if (o->o_code == opt) 1805 break; 1806 } 1807 subopt = NULL; 1808 } 1809 1810 if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags)) 1811 { 1812 if (tTd(37, 1)) 1813 dprintf("setoption: %s does not support suboptions, ignoring .%s\n", 1814 o->o_name == NULL ? "<unknown>" : o->o_name, 1815 subopt); 1816 subopt = NULL; 1817 } 1818 1819 if (tTd(37, 1)) 1820 { 1821 dprintf(isascii(opt) && isprint(opt) ? 1822 "setoption %s (%c)%s%s=" : 1823 "setoption %s (0x%x)%s%s=", 1824 o->o_name == NULL ? "<unknown>" : o->o_name, 1825 opt, 1826 subopt == NULL ? "" : ".", 1827 subopt == NULL ? "" : subopt); 1828 xputs(val); 1829 } 1830 1831 /* 1832 ** See if this option is preset for us. 1833 */ 1834 1835 if (!sticky && bitnset(opt, StickyOpt)) 1836 { 1837 if (tTd(37, 1)) 1838 dprintf(" (ignored)\n"); 1839 return; 1840 } 1841 1842 /* 1843 ** Check to see if this option can be specified by this user. 1844 */ 1845 1846 if (!safe && RealUid == 0) 1847 safe = TRUE; 1848 if (!safe && !bitset(OI_SAFE, o->o_flags)) 1849 { 1850 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1851 { 1852 int dp; 1853 1854 if (tTd(37, 1)) 1855 dprintf(" (unsafe)"); 1856 dp = drop_privileges(TRUE); 1857 setstat(dp); 1858 } 1859 } 1860 if (tTd(37, 1)) 1861 dprintf("\n"); 1862 1863 switch (opt & 0xff) 1864 { 1865 case '7': /* force seven-bit input */ 1866 SevenBitInput = atobool(val); 1867 break; 1868 1869 #if MIME8TO7 1870 case '8': /* handling of 8-bit input */ 1871 switch (*val) 1872 { 1873 case 'm': /* convert 8-bit, convert MIME */ 1874 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1875 break; 1876 1877 case 'p': /* pass 8 bit, convert MIME */ 1878 MimeMode = MM_CVTMIME|MM_PASS8BIT; 1879 break; 1880 1881 case 's': /* strict adherence */ 1882 MimeMode = MM_CVTMIME; 1883 break; 1884 1885 # if 0 1886 case 'r': /* reject 8-bit, don't convert MIME */ 1887 MimeMode = 0; 1888 break; 1889 1890 case 'j': /* "just send 8" */ 1891 MimeMode = MM_PASS8BIT; 1892 break; 1893 1894 case 'a': /* encode 8 bit if available */ 1895 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1896 break; 1897 1898 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1899 MimeMode = MM_MIME8BIT; 1900 break; 1901 # endif /* 0 */ 1902 1903 default: 1904 syserr("Unknown 8-bit mode %c", *val); 1905 finis(FALSE, EX_USAGE); 1906 } 1907 break; 1908 #endif /* MIME8TO7 */ 1909 1910 case 'A': /* set default alias file */ 1911 if (val[0] == '\0') 1912 setalias("aliases"); 1913 else 1914 setalias(val); 1915 break; 1916 1917 case 'a': /* look N minutes for "@:@" in alias file */ 1918 if (val[0] == '\0') 1919 SafeAlias = 5 * 60; /* five minutes */ 1920 else 1921 SafeAlias = convtime(val, 'm'); 1922 break; 1923 1924 case 'B': /* substitution for blank character */ 1925 SpaceSub = val[0]; 1926 if (SpaceSub == '\0') 1927 SpaceSub = ' '; 1928 break; 1929 1930 case 'b': /* min blocks free on queue fs/max msg size */ 1931 p = strchr(val, '/'); 1932 if (p != NULL) 1933 { 1934 *p++ = '\0'; 1935 MaxMessageSize = atol(p); 1936 } 1937 MinBlocksFree = atol(val); 1938 break; 1939 1940 case 'c': /* don't connect to "expensive" mailers */ 1941 NoConnect = atobool(val); 1942 break; 1943 1944 case 'C': /* checkpoint every N addresses */ 1945 CheckpointInterval = atoi(val); 1946 break; 1947 1948 case 'd': /* delivery mode */ 1949 switch (*val) 1950 { 1951 case '\0': 1952 set_delivery_mode(SM_DELIVER, e); 1953 break; 1954 1955 case SM_QUEUE: /* queue only */ 1956 case SM_DEFER: /* queue only and defer map lookups */ 1957 #if !QUEUE 1958 syserr("need QUEUE to set -odqueue or -oddefer"); 1959 #endif /* !QUEUE */ 1960 /* FALLTHROUGH */ 1961 1962 case SM_DELIVER: /* do everything */ 1963 case SM_FORK: /* fork after verification */ 1964 set_delivery_mode(*val, e); 1965 break; 1966 1967 default: 1968 syserr("Unknown delivery mode %c", *val); 1969 finis(FALSE, EX_USAGE); 1970 } 1971 break; 1972 1973 #if !_FFR_REMOVE_AUTOREBUILD 1974 case 'D': /* rebuild alias database as needed */ 1975 AutoRebuild = atobool(val); 1976 break; 1977 #endif /* !_FFR_REMOVE_AUTOREBUILD */ 1978 1979 case 'E': /* error message header/header file */ 1980 if (*val != '\0') 1981 ErrMsgFile = newstr(val); 1982 break; 1983 1984 case 'e': /* set error processing mode */ 1985 switch (*val) 1986 { 1987 case EM_QUIET: /* be silent about it */ 1988 case EM_MAIL: /* mail back */ 1989 case EM_BERKNET: /* do berknet error processing */ 1990 case EM_WRITE: /* write back (or mail) */ 1991 case EM_PRINT: /* print errors normally (default) */ 1992 e->e_errormode = *val; 1993 break; 1994 } 1995 break; 1996 1997 case 'F': /* file mode */ 1998 FileMode = atooct(val) & 0777; 1999 break; 2000 2001 case 'f': /* save Unix-style From lines on front */ 2002 SaveFrom = atobool(val); 2003 break; 2004 2005 case 'G': /* match recipients against GECOS field */ 2006 MatchGecos = atobool(val); 2007 break; 2008 2009 case 'g': /* default gid */ 2010 g_opt: 2011 if (isascii(*val) && isdigit(*val)) 2012 DefGid = atoi(val); 2013 else 2014 { 2015 register struct group *gr; 2016 2017 DefGid = -1; 2018 gr = getgrnam(val); 2019 if (gr == NULL) 2020 syserr("readcf: option %c: unknown group %s", 2021 opt, val); 2022 else 2023 DefGid = gr->gr_gid; 2024 } 2025 break; 2026 2027 case 'H': /* help file */ 2028 if (val[0] == '\0') 2029 HelpFile = "helpfile"; 2030 else 2031 HelpFile = newstr(val); 2032 break; 2033 2034 case 'h': /* maximum hop count */ 2035 MaxHopCount = atoi(val); 2036 break; 2037 2038 case 'I': /* use internet domain name server */ 2039 #if NAMED_BIND 2040 for (p = val; *p != 0; ) 2041 { 2042 bool clearmode; 2043 char *q; 2044 struct resolverflags *rfp; 2045 2046 while (*p == ' ') 2047 p++; 2048 if (*p == '\0') 2049 break; 2050 clearmode = FALSE; 2051 if (*p == '-') 2052 clearmode = TRUE; 2053 else if (*p != '+') 2054 p--; 2055 p++; 2056 q = p; 2057 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 2058 p++; 2059 if (*p != '\0') 2060 *p++ = '\0'; 2061 if (strcasecmp(q, "HasWildcardMX") == 0) 2062 { 2063 HasWildcardMX = !clearmode; 2064 continue; 2065 } 2066 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 2067 { 2068 if (strcasecmp(q, rfp->rf_name) == 0) 2069 break; 2070 } 2071 if (rfp->rf_name == NULL) 2072 syserr("readcf: I option value %s unrecognized", q); 2073 else if (clearmode) 2074 _res.options &= ~rfp->rf_bits; 2075 else 2076 _res.options |= rfp->rf_bits; 2077 } 2078 if (tTd(8, 2)) 2079 dprintf("_res.options = %x, HasWildcardMX = %d\n", 2080 (u_int) _res.options, HasWildcardMX); 2081 #else /* NAMED_BIND */ 2082 usrerr("name server (I option) specified but BIND not compiled in"); 2083 #endif /* NAMED_BIND */ 2084 break; 2085 2086 case 'i': /* ignore dot lines in message */ 2087 IgnrDot = atobool(val); 2088 break; 2089 2090 case 'j': /* send errors in MIME (RFC 1341) format */ 2091 SendMIMEErrors = atobool(val); 2092 break; 2093 2094 case 'J': /* .forward search path */ 2095 ForwardPath = newstr(val); 2096 break; 2097 2098 case 'k': /* connection cache size */ 2099 MaxMciCache = atoi(val); 2100 if (MaxMciCache < 0) 2101 MaxMciCache = 0; 2102 break; 2103 2104 case 'K': /* connection cache timeout */ 2105 MciCacheTimeout = convtime(val, 'm'); 2106 break; 2107 2108 case 'l': /* use Errors-To: header */ 2109 UseErrorsTo = atobool(val); 2110 break; 2111 2112 case 'L': /* log level */ 2113 if (safe || LogLevel < atoi(val)) 2114 LogLevel = atoi(val); 2115 break; 2116 2117 case 'M': /* define macro */ 2118 mid = macid(val, &ep); 2119 p = newstr(ep); 2120 if (!safe) 2121 cleanstrcpy(p, p, MAXNAME); 2122 define(mid, p, CurEnv); 2123 sticky = FALSE; 2124 break; 2125 2126 case 'm': /* send to me too */ 2127 MeToo = atobool(val); 2128 break; 2129 2130 case 'n': /* validate RHS in newaliases */ 2131 CheckAliases = atobool(val); 2132 break; 2133 2134 /* 'N' available -- was "net name" */ 2135 2136 case 'O': /* daemon options */ 2137 #if DAEMON 2138 if (!setdaemonoptions(val)) 2139 { 2140 syserr("too many daemons defined (%d max)", MAXDAEMONS); 2141 } 2142 #else /* DAEMON */ 2143 syserr("DaemonPortOptions (O option) set but DAEMON not compiled in"); 2144 #endif /* DAEMON */ 2145 break; 2146 2147 case 'o': /* assume old style headers */ 2148 if (atobool(val)) 2149 CurEnv->e_flags |= EF_OLDSTYLE; 2150 else 2151 CurEnv->e_flags &= ~EF_OLDSTYLE; 2152 break; 2153 2154 case 'p': /* select privacy level */ 2155 p = val; 2156 for (;;) 2157 { 2158 register struct prival *pv; 2159 extern struct prival PrivacyValues[]; 2160 2161 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 2162 p++; 2163 if (*p == '\0') 2164 break; 2165 val = p; 2166 while (isascii(*p) && isalnum(*p)) 2167 p++; 2168 if (*p != '\0') 2169 *p++ = '\0'; 2170 2171 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 2172 { 2173 if (strcasecmp(val, pv->pv_name) == 0) 2174 break; 2175 } 2176 if (pv->pv_name == NULL) 2177 syserr("readcf: Op line: %s unrecognized", val); 2178 PrivacyFlags |= pv->pv_flag; 2179 } 2180 sticky = FALSE; 2181 break; 2182 2183 case 'P': /* postmaster copy address for returned mail */ 2184 PostMasterCopy = newstr(val); 2185 break; 2186 2187 case 'q': /* slope of queue only function */ 2188 QueueFactor = atoi(val); 2189 break; 2190 2191 case 'Q': /* queue directory */ 2192 if (val[0] == '\0') 2193 { 2194 QueueDir = "mqueue"; 2195 } 2196 else 2197 { 2198 QueueDir = newstr(val); 2199 } 2200 if (RealUid != 0 && !safe) 2201 Warn_Q_option = TRUE; 2202 break; 2203 2204 case 'R': /* don't prune routes */ 2205 DontPruneRoutes = atobool(val); 2206 break; 2207 2208 case 'r': /* read timeout */ 2209 if (subopt == NULL) 2210 inittimeouts(val, sticky); 2211 else 2212 settimeout(subopt, val, sticky); 2213 break; 2214 2215 case 'S': /* status file */ 2216 if (val[0] == '\0') 2217 StatFile = "statistics"; 2218 else 2219 StatFile = newstr(val); 2220 break; 2221 2222 case 's': /* be super safe, even if expensive */ 2223 SuperSafe = atobool(val); 2224 break; 2225 2226 case 'T': /* queue timeout */ 2227 p = strchr(val, '/'); 2228 if (p != NULL) 2229 { 2230 *p++ = '\0'; 2231 settimeout("queuewarn", p, sticky); 2232 } 2233 settimeout("queuereturn", val, sticky); 2234 break; 2235 2236 case 't': /* time zone name */ 2237 TimeZoneSpec = newstr(val); 2238 break; 2239 2240 case 'U': /* location of user database */ 2241 UdbSpec = newstr(val); 2242 break; 2243 2244 case 'u': /* set default uid */ 2245 for (p = val; *p != '\0'; p++) 2246 { 2247 if (*p == '.' || *p == '/' || *p == ':') 2248 { 2249 *p++ = '\0'; 2250 break; 2251 } 2252 } 2253 if (isascii(*val) && isdigit(*val)) 2254 { 2255 DefUid = atoi(val); 2256 setdefuser(); 2257 } 2258 else 2259 { 2260 register struct passwd *pw; 2261 2262 DefUid = -1; 2263 pw = sm_getpwnam(val); 2264 if (pw == NULL) 2265 syserr("readcf: option u: unknown user %s", val); 2266 else 2267 { 2268 DefUid = pw->pw_uid; 2269 DefGid = pw->pw_gid; 2270 DefUser = newstr(pw->pw_name); 2271 } 2272 } 2273 2274 #ifdef UID_MAX 2275 if (DefUid > UID_MAX) 2276 { 2277 syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored", 2278 DefUid, UID_MAX); 2279 } 2280 #endif /* UID_MAX */ 2281 2282 /* handle the group if it is there */ 2283 if (*p == '\0') 2284 break; 2285 val = p; 2286 goto g_opt; 2287 2288 case 'V': /* fallback MX host */ 2289 if (val[0] != '\0') 2290 FallBackMX = newstr(val); 2291 break; 2292 2293 case 'v': /* run in verbose mode */ 2294 Verbose = atobool(val) ? 1 : 0; 2295 break; 2296 2297 case 'w': /* if we are best MX, try host directly */ 2298 TryNullMXList = atobool(val); 2299 break; 2300 2301 /* 'W' available -- was wizard password */ 2302 2303 case 'x': /* load avg at which to auto-queue msgs */ 2304 QueueLA = atoi(val); 2305 break; 2306 2307 case 'X': /* load avg at which to auto-reject connections */ 2308 RefuseLA = atoi(val); 2309 break; 2310 2311 case 'y': /* work recipient factor */ 2312 WkRecipFact = atoi(val); 2313 break; 2314 2315 case 'Y': /* fork jobs during queue runs */ 2316 ForkQueueRuns = atobool(val); 2317 break; 2318 2319 case 'z': /* work message class factor */ 2320 WkClassFact = atoi(val); 2321 break; 2322 2323 case 'Z': /* work time factor */ 2324 WkTimeFact = atoi(val); 2325 break; 2326 2327 2328 case O_QUEUESORTORD: /* queue sorting order */ 2329 switch (*val) 2330 { 2331 case 'h': /* Host first */ 2332 case 'H': 2333 QueueSortOrder = QSO_BYHOST; 2334 break; 2335 2336 case 'p': /* Priority order */ 2337 case 'P': 2338 QueueSortOrder = QSO_BYPRIORITY; 2339 break; 2340 2341 case 't': /* Submission time */ 2342 case 'T': 2343 QueueSortOrder = QSO_BYTIME; 2344 break; 2345 2346 case 'f': /* File Name */ 2347 case 'F': 2348 QueueSortOrder = QSO_BYFILENAME; 2349 break; 2350 2351 default: 2352 syserr("Invalid queue sort order \"%s\"", val); 2353 } 2354 break; 2355 2356 #if _FFR_QUEUEDELAY 2357 case O_QUEUEDELAY: /* queue delay algorithm */ 2358 switch (*val) 2359 { 2360 case 'e': /* exponential */ 2361 case 'E': 2362 QueueAlg = QD_EXP; 2363 QueueInitDelay = 10 MINUTES; 2364 QueueMaxDelay = 2 HOURS; 2365 p = strchr(val, '/'); 2366 if (p != NULL) 2367 { 2368 char *q; 2369 2370 *p++ = '\0'; 2371 q = strchr(p, '/'); 2372 if (q != NULL) 2373 *q++ = '\0'; 2374 QueueInitDelay = convtime(p, 's'); 2375 if (q != NULL) 2376 { 2377 QueueMaxDelay = convtime(q, 's'); 2378 } 2379 } 2380 break; 2381 2382 case 'l': /* linear */ 2383 case 'L': 2384 QueueAlg = QD_LINEAR; 2385 break; 2386 2387 default: 2388 syserr("Invalid queue delay algorithm \"%s\"", val); 2389 } 2390 break; 2391 #endif /* _FFR_QUEUEDELAY */ 2392 2393 case O_HOSTSFILE: /* pathname of /etc/hosts file */ 2394 HostsFile = newstr(val); 2395 break; 2396 2397 case O_MQA: /* minimum queue age between deliveries */ 2398 MinQueueAge = convtime(val, 'm'); 2399 break; 2400 2401 case O_DEFCHARSET: /* default character set for mimefying */ 2402 DefaultCharSet = newstr(denlstring(val, TRUE, TRUE)); 2403 break; 2404 2405 case O_SSFILE: /* service switch file */ 2406 ServiceSwitchFile = newstr(val); 2407 break; 2408 2409 case O_DIALDELAY: /* delay for dial-on-demand operation */ 2410 DialDelay = convtime(val, 's'); 2411 break; 2412 2413 case O_NORCPTACTION: /* what to do if no recipient */ 2414 if (strcasecmp(val, "none") == 0) 2415 NoRecipientAction = NRA_NO_ACTION; 2416 else if (strcasecmp(val, "add-to") == 0) 2417 NoRecipientAction = NRA_ADD_TO; 2418 else if (strcasecmp(val, "add-apparently-to") == 0) 2419 NoRecipientAction = NRA_ADD_APPARENTLY_TO; 2420 else if (strcasecmp(val, "add-bcc") == 0) 2421 NoRecipientAction = NRA_ADD_BCC; 2422 else if (strcasecmp(val, "add-to-undisclosed") == 0) 2423 NoRecipientAction = NRA_ADD_TO_UNDISCLOSED; 2424 else 2425 syserr("Invalid NoRecipientAction: %s", val); 2426 break; 2427 2428 case O_SAFEFILEENV: /* chroot() environ for writing to files */ 2429 SafeFileEnv = newstr(val); 2430 break; 2431 2432 case O_MAXMSGSIZE: /* maximum message size */ 2433 MaxMessageSize = atol(val); 2434 break; 2435 2436 case O_COLONOKINADDR: /* old style handling of colon addresses */ 2437 ColonOkInAddr = atobool(val); 2438 break; 2439 2440 case O_MAXQUEUERUN: /* max # of jobs in a single queue run */ 2441 MaxQueueRun = atol(val); 2442 break; 2443 2444 case O_MAXCHILDREN: /* max # of children of daemon */ 2445 MaxChildren = atoi(val); 2446 break; 2447 2448 #if _FFR_MAX_FORWARD_ENTRIES 2449 case O_MAXFORWARD: /* max # of forward entries */ 2450 MaxForwardEntries = atoi(val); 2451 break; 2452 #endif /* _FFR_MAX_FORWARD_ENTRIES */ 2453 2454 case O_KEEPCNAMES: /* don't expand CNAME records */ 2455 DontExpandCnames = atobool(val); 2456 break; 2457 2458 case O_MUSTQUOTE: /* must quote these characters in phrases */ 2459 (void) strlcpy(buf, "@,;:\\()[]", sizeof buf); 2460 if (strlen(val) < (SIZE_T) sizeof buf - 10) 2461 (void) strlcat(buf, val, sizeof buf); 2462 else 2463 printf("Warning: MustQuoteChars too long, ignored.\n"); 2464 MustQuoteChars = newstr(buf); 2465 break; 2466 2467 case O_SMTPGREETING: /* SMTP greeting message (old $e macro) */ 2468 SmtpGreeting = newstr(munchstring(val, NULL, '\0')); 2469 break; 2470 2471 case O_UNIXFROM: /* UNIX From_ line (old $l macro) */ 2472 UnixFromLine = newstr(munchstring(val, NULL, '\0')); 2473 break; 2474 2475 case O_OPCHARS: /* operator characters (old $o macro) */ 2476 if (OperatorChars != NULL) 2477 printf("Warning: OperatorChars is being redefined.\n It should only be set before ruleset definitions.\n"); 2478 OperatorChars = newstr(munchstring(val, NULL, '\0')); 2479 break; 2480 2481 case O_DONTINITGRPS: /* don't call initgroups(3) */ 2482 DontInitGroups = atobool(val); 2483 break; 2484 2485 case O_SLFH: /* make sure from fits on one line */ 2486 SingleLineFromHeader = atobool(val); 2487 break; 2488 2489 case O_ABH: /* allow HELO commands with syntax errors */ 2490 AllowBogusHELO = atobool(val); 2491 break; 2492 2493 case O_CONNTHROT: /* connection rate throttle */ 2494 ConnRateThrottle = atoi(val); 2495 break; 2496 2497 case O_UGW: /* group writable files are unsafe */ 2498 if (!atobool(val)) 2499 { 2500 setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE, 2501 DontBlameSendmail); 2502 setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE, 2503 DontBlameSendmail); 2504 } 2505 break; 2506 2507 case O_DBLBOUNCE: /* address to which to send double bounces */ 2508 if (val[0] != '\0') 2509 DoubleBounceAddr = newstr(val); 2510 else 2511 syserr("readcf: option DoubleBounceAddress: value required"); 2512 break; 2513 2514 case O_HSDIR: /* persistent host status directory */ 2515 if (val[0] != '\0') 2516 HostStatDir = newstr(val); 2517 break; 2518 2519 case O_SINGTHREAD: /* single thread deliveries (requires hsdir) */ 2520 SingleThreadDelivery = atobool(val); 2521 break; 2522 2523 case O_RUNASUSER: /* run bulk of code as this user */ 2524 for (p = val; *p != '\0'; p++) 2525 { 2526 if (*p == '.' || *p == '/' || *p == ':') 2527 { 2528 *p++ = '\0'; 2529 break; 2530 } 2531 } 2532 if (isascii(*val) && isdigit(*val)) 2533 { 2534 if (can_setuid) 2535 RunAsUid = atoi(val); 2536 } 2537 else 2538 { 2539 register struct passwd *pw; 2540 2541 pw = sm_getpwnam(val); 2542 if (pw == NULL) 2543 syserr("readcf: option RunAsUser: unknown user %s", val); 2544 else if (can_setuid) 2545 { 2546 if (*p == '\0') 2547 RunAsUserName = newstr(val); 2548 RunAsUid = pw->pw_uid; 2549 RunAsGid = pw->pw_gid; 2550 } 2551 } 2552 #ifdef UID_MAX 2553 if (RunAsUid > UID_MAX) 2554 { 2555 syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored", 2556 RunAsUid, UID_MAX); 2557 } 2558 #endif /* UID_MAX */ 2559 if (*p != '\0') 2560 { 2561 if (isascii(*p) && isdigit(*p)) 2562 { 2563 if (can_setuid) 2564 RunAsGid = atoi(p); 2565 } 2566 else 2567 { 2568 register struct group *gr; 2569 2570 gr = getgrnam(p); 2571 if (gr == NULL) 2572 syserr("readcf: option RunAsUser: unknown group %s", 2573 p); 2574 else if (can_setuid) 2575 RunAsGid = gr->gr_gid; 2576 } 2577 } 2578 if (tTd(47, 5)) 2579 dprintf("readcf: RunAsUser = %d:%d\n", 2580 (int)RunAsUid, (int)RunAsGid); 2581 break; 2582 2583 case O_DSN_RRT: 2584 RrtImpliesDsn = atobool(val); 2585 break; 2586 2587 case O_PIDFILE: 2588 if (PidFile != NULL) 2589 free(PidFile); 2590 PidFile = newstr(val); 2591 break; 2592 2593 case O_DONTBLAMESENDMAIL: 2594 p = val; 2595 for (;;) 2596 { 2597 register struct dbsval *dbs; 2598 extern struct dbsval DontBlameSendmailValues[]; 2599 2600 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 2601 p++; 2602 if (*p == '\0') 2603 break; 2604 val = p; 2605 while (isascii(*p) && isalnum(*p)) 2606 p++; 2607 if (*p != '\0') 2608 *p++ = '\0'; 2609 2610 for (dbs = DontBlameSendmailValues; 2611 dbs->dbs_name != NULL; dbs++) 2612 { 2613 if (strcasecmp(val, dbs->dbs_name) == 0) 2614 break; 2615 } 2616 if (dbs->dbs_name == NULL) 2617 syserr("readcf: DontBlameSendmail option: %s unrecognized", val); 2618 else if (dbs->dbs_flag == DBS_SAFE) 2619 clrbitmap(DontBlameSendmail); 2620 else 2621 setbitn(dbs->dbs_flag, DontBlameSendmail); 2622 } 2623 sticky = FALSE; 2624 break; 2625 2626 case O_DPI: 2627 DontProbeInterfaces = atobool(val); 2628 break; 2629 2630 case O_MAXRCPT: 2631 MaxRcptPerMsg = atoi(val); 2632 break; 2633 2634 case O_DEADLETTER: 2635 if (DeadLetterDrop != NULL) 2636 free(DeadLetterDrop); 2637 DeadLetterDrop = newstr(val); 2638 break; 2639 2640 #if _FFR_DONTLOCKFILESFORREAD_OPTION 2641 case O_DONTLOCK: 2642 DontLockReadFiles = atobool(val); 2643 break; 2644 #endif /* _FFR_DONTLOCKFILESFORREAD_OPTION */ 2645 2646 case O_MAXALIASRCSN: 2647 MaxAliasRecursion = atoi(val); 2648 break; 2649 2650 case O_CNCTONLYTO: 2651 /* XXX should probably use gethostbyname */ 2652 #if NETINET || NETINET6 2653 # if NETINET6 2654 if (inet_addr(val) == INADDR_NONE) 2655 { 2656 ConnectOnlyTo.sa.sa_family = AF_INET6; 2657 if (inet_pton(AF_INET6, val, 2658 &ConnectOnlyTo.sin6.sin6_addr) != 1) 2659 syserr("readcf: option ConnectOnlyTo: invalid IP address %s", 2660 val); 2661 } 2662 else 2663 # endif /* NETINET6 */ 2664 { 2665 ConnectOnlyTo.sa.sa_family = AF_INET; 2666 ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val); 2667 } 2668 #endif /* NETINET || NETINET6 */ 2669 break; 2670 2671 case O_TRUSTUSER: 2672 #if HASFCHOWN 2673 if (isascii(*val) && isdigit(*val)) 2674 TrustedUid = atoi(val); 2675 else 2676 { 2677 register struct passwd *pw; 2678 2679 TrustedUid = 0; 2680 pw = sm_getpwnam(val); 2681 if (pw == NULL) 2682 syserr("readcf: option TrustedUser: unknown user %s", val); 2683 else 2684 TrustedUid = pw->pw_uid; 2685 } 2686 2687 # ifdef UID_MAX 2688 if (TrustedUid > UID_MAX) 2689 { 2690 syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)", 2691 TrustedUid, UID_MAX); 2692 TrustedUid = 0; 2693 } 2694 # endif /* UID_MAX */ 2695 #else /* HASFCHOWN */ 2696 syserr("readcf: option TrustedUser: can not be used on systems which do not support fchown()"); 2697 #endif /* HASFCHOWN */ 2698 break; 2699 2700 case O_MAXMIMEHDRLEN: 2701 p = strchr(val, '/'); 2702 if (p != NULL) 2703 *p++ = '\0'; 2704 MaxMimeHeaderLength = atoi(val); 2705 if (p != NULL && *p != '\0') 2706 MaxMimeFieldLength = atoi(p); 2707 else 2708 MaxMimeFieldLength = MaxMimeHeaderLength / 2; 2709 2710 if (MaxMimeHeaderLength < 0) 2711 MaxMimeHeaderLength = 0; 2712 else if (MaxMimeHeaderLength < 128) 2713 printf("Warning: MaxMimeHeaderLength: header length limit set lower than 128\n"); 2714 2715 if (MaxMimeFieldLength < 0) 2716 MaxMimeFieldLength = 0; 2717 else if (MaxMimeFieldLength < 40) 2718 printf("Warning: MaxMimeHeaderLength: field length limit set lower than 40\n"); 2719 break; 2720 2721 case O_CONTROLSOCKET: 2722 if (ControlSocketName != NULL) 2723 free(ControlSocketName); 2724 ControlSocketName = newstr(val); 2725 break; 2726 2727 case O_MAXHDRSLEN: 2728 MaxHeadersLength = atoi(val); 2729 2730 if (MaxHeadersLength > 0 && 2731 MaxHeadersLength < (MAXHDRSLEN / 2)) 2732 printf("Warning: MaxHeadersLength: headers length limit set lower than %d\n", (MAXHDRSLEN / 2)); 2733 break; 2734 2735 case O_PROCTITLEPREFIX: 2736 if (ProcTitlePrefix != NULL) 2737 free(ProcTitlePrefix); 2738 ProcTitlePrefix = newstr(val); 2739 break; 2740 2741 #if SASL 2742 case O_SASLINFO: 2743 #if _FFR_ALLOW_SASLINFO 2744 /* 2745 ** Allow users to select their own authinfo file. 2746 ** However, this is not a "perfect" solution. 2747 ** If mail is queued, the authentication info 2748 ** will not be used in subsequent delivery attempts. 2749 ** If we really want to support this, then it has 2750 ** to be stored in the queue file. 2751 */ 2752 if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 && 2753 RunAsUid != RealUid) 2754 { 2755 errno = 0; 2756 syserr("Error: %s only allowed with -U\n", 2757 o->o_name == NULL ? "<unknown>" : o->o_name); 2758 ExitStat = EX_USAGE; 2759 break; 2760 } 2761 #endif /* _FFR_ALLOW_SASLINFO */ 2762 if (SASLInfo != NULL) 2763 free(SASLInfo); 2764 SASLInfo = newstr(val); 2765 break; 2766 2767 case O_SASLMECH: 2768 if (AuthMechanisms != NULL) 2769 free(AuthMechanisms); 2770 if (*val != '\0') 2771 AuthMechanisms = newstr(val); 2772 else 2773 AuthMechanisms = NULL; 2774 break; 2775 2776 case O_SASLOPTS: 2777 while (val != NULL && *val != '\0') 2778 { 2779 switch(*val) 2780 { 2781 case 'A': 2782 SASLOpts |= SASL_AUTH_AUTH; 2783 break; 2784 # if _FFR_SASL_OPTS 2785 case 'a': 2786 SASLOpts |= SASL_SEC_NOACTIVE; 2787 break; 2788 case 'c': 2789 SASLOpts |= SASL_SEC_PASS_CREDENTIALS; 2790 break; 2791 case 'd': 2792 SASLOpts |= SASL_SEC_NODICTIONARY; 2793 break; 2794 case 'f': 2795 SASLOpts |= SASL_SEC_FORWARD_SECRECY; 2796 break; 2797 case 'p': 2798 SASLOpts |= SASL_SEC_NOPLAINTEXT; 2799 break; 2800 case 'y': 2801 SASLOpts |= SASL_SEC_NOANONYMOUS; 2802 break; 2803 # endif /* _FFR_SASL_OPTS */ 2804 default: 2805 printf("Warning: Option: %s unknown parameter '%c'\n", 2806 o->o_name == NULL ? "<unknown>" 2807 : o->o_name, 2808 (isascii(*val) && isprint(*val)) ? *val 2809 : '?'); 2810 break; 2811 } 2812 ++val; 2813 val = strpbrk(val, ", \t"); 2814 if (val != NULL) 2815 ++val; 2816 } 2817 break; 2818 2819 #else /* SASL */ 2820 case O_SASLINFO: 2821 case O_SASLMECH: 2822 case O_SASLOPTS: 2823 printf("Warning: Option: %s requires SASL support (-DSASL)\n", 2824 o->o_name == NULL ? "<unknown>" : o->o_name); 2825 break; 2826 #endif /* SASL */ 2827 2828 #if STARTTLS 2829 case O_SRVCERTFILE: 2830 if (SrvCERTfile != NULL) 2831 free(SrvCERTfile); 2832 SrvCERTfile = newstr(val); 2833 break; 2834 2835 case O_SRVKEYFILE: 2836 if (Srvkeyfile != NULL) 2837 free(Srvkeyfile); 2838 Srvkeyfile = newstr(val); 2839 break; 2840 2841 case O_CLTCERTFILE: 2842 if (CltCERTfile != NULL) 2843 free(CltCERTfile); 2844 CltCERTfile = newstr(val); 2845 break; 2846 2847 case O_CLTKEYFILE: 2848 if (Cltkeyfile != NULL) 2849 free(Cltkeyfile); 2850 Cltkeyfile = newstr(val); 2851 break; 2852 2853 case O_CACERTFILE: 2854 if (CACERTfile != NULL) 2855 free(CACERTfile); 2856 CACERTfile = newstr(val); 2857 break; 2858 2859 case O_CACERTPATH: 2860 if (CACERTpath != NULL) 2861 free(CACERTpath); 2862 CACERTpath = newstr(val); 2863 break; 2864 2865 case O_DHPARAMS: 2866 if (DHParams != NULL) 2867 free(DHParams); 2868 DHParams = newstr(val); 2869 break; 2870 2871 # if _FFR_TLS_1 2872 case O_DHPARAMS5: 2873 if (DHParams5 != NULL) 2874 free(DHParams5); 2875 DHParams5 = newstr(val); 2876 break; 2877 2878 case O_CIPHERLIST: 2879 if (CipherList != NULL) 2880 free(CipherList); 2881 CipherList = newstr(val); 2882 break; 2883 # endif /* _FFR_TLS_1 */ 2884 2885 case O_RANDFILE: 2886 if (RandFile != NULL) 2887 free(RandFile); 2888 RandFile= newstr(val); 2889 break; 2890 2891 # else /* STARTTLS */ 2892 case O_SRVCERTFILE: 2893 case O_SRVKEYFILE: 2894 case O_CLTCERTFILE: 2895 case O_CLTKEYFILE: 2896 case O_CACERTFILE: 2897 case O_CACERTPATH: 2898 case O_DHPARAMS: 2899 # if _FFR_TLS_1 2900 case O_DHPARAMS5: 2901 case O_CIPHERLIST: 2902 # endif /* _FFR_TLS_1 */ 2903 case O_RANDFILE: 2904 printf("Warning: Option: %s requires TLS support\n", 2905 o->o_name == NULL ? "<unknown>" : o->o_name); 2906 break; 2907 2908 # endif /* STARTTLS */ 2909 2910 case O_CLIENTPORT: 2911 #if DAEMON 2912 setclientoptions(val); 2913 #else /* DAEMON */ 2914 syserr("ClientPortOptions (O option) set but DAEMON not compiled in"); 2915 #endif /* DAEMON */ 2916 break; 2917 2918 case O_DF_BUFSIZE: 2919 DataFileBufferSize = atoi(val); 2920 break; 2921 2922 case O_XF_BUFSIZE: 2923 XscriptFileBufferSize = atoi(val); 2924 break; 2925 2926 case O_LDAPDEFAULTSPEC: 2927 #ifdef LDAPMAP 2928 ldapmap_set_defaults(val); 2929 #else /* LDAPMAP */ 2930 printf("Warning: Option: %s requires LDAP support (-DLDAPMAP)\n", 2931 o->o_name == NULL ? "<unknown>" : o->o_name); 2932 #endif /* LDAPMAP */ 2933 break; 2934 2935 #if _FFR_MILTER 2936 case O_INPUTMILTER: 2937 InputFilterList = newstr(val); 2938 break; 2939 2940 case O_MILTER: 2941 milter_set_option(subopt, val, sticky); 2942 break; 2943 #endif /* _FFR_MILTER */ 2944 2945 #if _FFR_QUEUE_FILE_MODE 2946 case O_QUEUE_FILE_MODE: /* queue file mode */ 2947 QueueFileMode = atooct(val) & 0777; 2948 break; 2949 #endif /* _FFR_QUEUE_FILE_MODE */ 2950 2951 default: 2952 if (tTd(37, 1)) 2953 { 2954 if (isascii(opt) && isprint(opt)) 2955 dprintf("Warning: option %c unknown\n", opt); 2956 else 2957 dprintf("Warning: option 0x%x unknown\n", opt); 2958 } 2959 break; 2960 } 2961 2962 /* 2963 ** Options with suboptions are responsible for taking care 2964 ** of sticky-ness (e.g., that a command line setting is kept 2965 ** when reading in the sendmail.cf file). This has to be done 2966 ** when the suboptions are parsed since each suboption must be 2967 ** sticky, not the root option. 2968 */ 2969 2970 if (sticky && !bitset(OI_SUBOPT, o->o_flags)) 2971 setbitn(opt, StickyOpt); 2972 } 2973 /* 2974 ** SETCLASS -- set a string into a class 2975 ** 2976 ** Parameters: 2977 ** class -- the class to put the string in. 2978 ** str -- the string to enter 2979 ** 2980 ** Returns: 2981 ** none. 2982 ** 2983 ** Side Effects: 2984 ** puts the word into the symbol table. 2985 */ 2986 2987 void 2988 setclass(class, str) 2989 int class; 2990 char *str; 2991 { 2992 register STAB *s; 2993 2994 if ((*str & 0377) == MATCHCLASS) 2995 { 2996 int mid; 2997 2998 str++; 2999 mid = macid(str, NULL); 3000 if (mid == '\0') 3001 return; 3002 3003 if (tTd(37, 8)) 3004 dprintf("setclass(%s, $=%s)\n", 3005 macname(class), macname(mid)); 3006 copy_class(mid, class); 3007 } 3008 else 3009 { 3010 if (tTd(37, 8)) 3011 dprintf("setclass(%s, %s)\n", macname(class), str); 3012 3013 s = stab(str, ST_CLASS, ST_ENTER); 3014 setbitn(class, s->s_class); 3015 } 3016 } 3017 /* 3018 ** MAKEMAPENTRY -- create a map entry 3019 ** 3020 ** Parameters: 3021 ** line -- the config file line 3022 ** 3023 ** Returns: 3024 ** A pointer to the map that has been created. 3025 ** NULL if there was a syntax error. 3026 ** 3027 ** Side Effects: 3028 ** Enters the map into the dictionary. 3029 */ 3030 3031 MAP * 3032 makemapentry(line) 3033 char *line; 3034 { 3035 register char *p; 3036 char *mapname; 3037 char *classname; 3038 register STAB *s; 3039 STAB *class; 3040 3041 for (p = line; isascii(*p) && isspace(*p); p++) 3042 continue; 3043 if (!(isascii(*p) && isalnum(*p))) 3044 { 3045 syserr("readcf: config K line: no map name"); 3046 return NULL; 3047 } 3048 3049 mapname = p; 3050 while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.') 3051 continue; 3052 if (*p != '\0') 3053 *p++ = '\0'; 3054 while (isascii(*p) && isspace(*p)) 3055 p++; 3056 if (!(isascii(*p) && isalnum(*p))) 3057 { 3058 syserr("readcf: config K line, map %s: no map class", mapname); 3059 return NULL; 3060 } 3061 classname = p; 3062 while (isascii(*++p) && isalnum(*p)) 3063 continue; 3064 if (*p != '\0') 3065 *p++ = '\0'; 3066 while (isascii(*p) && isspace(*p)) 3067 p++; 3068 3069 /* look up the class */ 3070 class = stab(classname, ST_MAPCLASS, ST_FIND); 3071 if (class == NULL) 3072 { 3073 syserr("readcf: map %s: class %s not available", mapname, classname); 3074 return NULL; 3075 } 3076 3077 /* enter the map */ 3078 s = stab(mapname, ST_MAP, ST_ENTER); 3079 s->s_map.map_class = &class->s_mapclass; 3080 s->s_map.map_mname = newstr(mapname); 3081 3082 if (class->s_mapclass.map_parse(&s->s_map, p)) 3083 s->s_map.map_mflags |= MF_VALID; 3084 3085 if (tTd(37, 5)) 3086 { 3087 dprintf("map %s, class %s, flags %lx, file %s,\n", 3088 s->s_map.map_mname, s->s_map.map_class->map_cname, 3089 s->s_map.map_mflags, 3090 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 3091 dprintf("\tapp %s, domain %s, rebuild %s\n", 3092 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 3093 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 3094 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 3095 } 3096 3097 return &s->s_map; 3098 } 3099 /* 3100 ** STRTORWSET -- convert string to rewriting set number 3101 ** 3102 ** Parameters: 3103 ** p -- the pointer to the string to decode. 3104 ** endp -- if set, store the trailing delimiter here. 3105 ** stabmode -- ST_ENTER to create this entry, ST_FIND if 3106 ** it must already exist. 3107 ** 3108 ** Returns: 3109 ** The appropriate ruleset number. 3110 ** -1 if it is not valid (error already printed) 3111 */ 3112 3113 int 3114 strtorwset(p, endp, stabmode) 3115 char *p; 3116 char **endp; 3117 int stabmode; 3118 { 3119 int ruleset; 3120 static int nextruleset = MAXRWSETS; 3121 3122 while (isascii(*p) && isspace(*p)) 3123 p++; 3124 if (!isascii(*p)) 3125 { 3126 syserr("invalid ruleset name: \"%.20s\"", p); 3127 return -1; 3128 } 3129 if (isdigit(*p)) 3130 { 3131 ruleset = strtol(p, endp, 10); 3132 if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 3133 { 3134 syserr("bad ruleset %d (%d max)", 3135 ruleset, MAXRWSETS / 2); 3136 ruleset = -1; 3137 } 3138 } 3139 else 3140 { 3141 STAB *s; 3142 char delim; 3143 char *q = NULL; 3144 3145 q = p; 3146 while (*p != '\0' && isascii(*p) && 3147 (isalnum(*p) || *p == '_')) 3148 p++; 3149 if (q == p || !(isascii(*q) && isalpha(*q))) 3150 { 3151 /* no valid characters */ 3152 syserr("invalid ruleset name: \"%.20s\"", q); 3153 return -1; 3154 } 3155 while (isascii(*p) && isspace(*p)) 3156 *p++ = '\0'; 3157 delim = *p; 3158 if (delim != '\0') 3159 *p = '\0'; 3160 s = stab(q, ST_RULESET, stabmode); 3161 if (delim != '\0') 3162 *p = delim; 3163 3164 if (s == NULL) 3165 return -1; 3166 3167 if (stabmode == ST_ENTER && delim == '=') 3168 { 3169 while (isascii(*++p) && isspace(*p)) 3170 continue; 3171 if (!(isascii(*p) && isdigit(*p))) 3172 { 3173 syserr("bad ruleset definition \"%s\" (number required after `=')", q); 3174 ruleset = -1; 3175 } 3176 else 3177 { 3178 ruleset = strtol(p, endp, 10); 3179 if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 3180 { 3181 syserr("bad ruleset number %d in \"%s\" (%d max)", 3182 ruleset, q, MAXRWSETS / 2); 3183 ruleset = -1; 3184 } 3185 } 3186 } 3187 else 3188 { 3189 if (endp != NULL) 3190 *endp = p; 3191 if (s->s_ruleset >= 0) 3192 ruleset = s->s_ruleset; 3193 else if ((ruleset = --nextruleset) < MAXRWSETS / 2) 3194 { 3195 syserr("%s: too many named rulesets (%d max)", 3196 q, MAXRWSETS / 2); 3197 ruleset = -1; 3198 } 3199 } 3200 if (s->s_ruleset >= 0 && 3201 ruleset >= 0 && 3202 ruleset != s->s_ruleset) 3203 { 3204 syserr("%s: ruleset changed value (old %d, new %d)", 3205 q, s->s_ruleset, ruleset); 3206 ruleset = s->s_ruleset; 3207 } 3208 else if (ruleset >= 0) 3209 { 3210 s->s_ruleset = ruleset; 3211 } 3212 if (stabmode == ST_ENTER) 3213 { 3214 char *h = NULL; 3215 3216 if (RuleSetNames[ruleset] != NULL) 3217 free(RuleSetNames[ruleset]); 3218 if (delim != '\0' && (h = strchr(q, delim)) != NULL) 3219 *h = '\0'; 3220 RuleSetNames[ruleset] = newstr(q); 3221 if (delim == '/' && h != NULL) 3222 *h = delim; /* put back delim */ 3223 } 3224 } 3225 return ruleset; 3226 } 3227 /* 3228 ** SETTIMEOUT -- set an individual timeout 3229 ** 3230 ** Parameters: 3231 ** name -- the name of the timeout. 3232 ** val -- the value of the timeout. 3233 ** sticky -- if set, don't let other setoptions override 3234 ** this value. 3235 ** 3236 ** Returns: 3237 ** none. 3238 */ 3239 3240 /* set if Timeout sub-option is stuck */ 3241 static BITMAP256 StickyTimeoutOpt; 3242 3243 static struct timeoutinfo 3244 { 3245 char *to_name; /* long name of timeout */ 3246 u_char to_code; /* code for option */ 3247 } TimeOutTab[] = 3248 { 3249 #define TO_INITIAL 0x01 3250 { "initial", TO_INITIAL }, 3251 #define TO_MAIL 0x02 3252 { "mail", TO_MAIL }, 3253 #define TO_RCPT 0x03 3254 { "rcpt", TO_RCPT }, 3255 #define TO_DATAINIT 0x04 3256 { "datainit", TO_DATAINIT }, 3257 #define TO_DATABLOCK 0x05 3258 { "datablock", TO_DATABLOCK }, 3259 #define TO_DATAFINAL 0x06 3260 { "datafinal", TO_DATAFINAL }, 3261 #define TO_COMMAND 0x07 3262 { "command", TO_COMMAND }, 3263 #define TO_RSET 0x08 3264 { "rset", TO_RSET }, 3265 #define TO_HELO 0x09 3266 { "helo", TO_HELO }, 3267 #define TO_QUIT 0x0A 3268 { "quit", TO_QUIT }, 3269 #define TO_MISC 0x0B 3270 { "misc", TO_MISC }, 3271 #define TO_IDENT 0x0C 3272 { "ident", TO_IDENT }, 3273 #define TO_FILEOPEN 0x0D 3274 { "fileopen", TO_FILEOPEN }, 3275 #define TO_CONNECT 0x0E 3276 { "connect", TO_CONNECT }, 3277 #define TO_ICONNECT 0x0F 3278 { "iconnect", TO_ICONNECT }, 3279 #define TO_QUEUEWARN 0x10 3280 { "queuewarn", TO_QUEUEWARN }, 3281 { "queuewarn.*", TO_QUEUEWARN }, 3282 #define TO_QUEUEWARN_NORMAL 0x11 3283 { "queuewarn.normal", TO_QUEUEWARN_NORMAL }, 3284 #define TO_QUEUEWARN_URGENT 0x12 3285 { "queuewarn.urgent", TO_QUEUEWARN_URGENT }, 3286 #define TO_QUEUEWARN_NON_URGENT 0x13 3287 { "queuewarn.non-urgent", TO_QUEUEWARN_NON_URGENT }, 3288 #define TO_QUEUERETURN 0x14 3289 { "queuereturn", TO_QUEUERETURN }, 3290 { "queuereturn.*", TO_QUEUERETURN }, 3291 #define TO_QUEUERETURN_NORMAL 0x15 3292 { "queuereturn.normal", TO_QUEUERETURN_NORMAL }, 3293 #define TO_QUEUERETURN_URGENT 0x16 3294 { "queuereturn.urgent", TO_QUEUERETURN_URGENT }, 3295 #define TO_QUEUERETURN_NON_URGENT 0x17 3296 { "queuereturn.non-urgent", TO_QUEUERETURN_NON_URGENT }, 3297 #define TO_HOSTSTATUS 0x18 3298 { "hoststatus", TO_HOSTSTATUS }, 3299 #define TO_RESOLVER_RETRANS 0x19 3300 { "resolver.retrans", TO_RESOLVER_RETRANS }, 3301 #define TO_RESOLVER_RETRANS_NORMAL 0x1A 3302 { "resolver.retrans.normal", TO_RESOLVER_RETRANS_NORMAL }, 3303 #define TO_RESOLVER_RETRANS_FIRST 0x1B 3304 { "resolver.retrans.first", TO_RESOLVER_RETRANS_FIRST }, 3305 #define TO_RESOLVER_RETRY 0x1C 3306 { "resolver.retry", TO_RESOLVER_RETRY }, 3307 #define TO_RESOLVER_RETRY_NORMAL 0x1D 3308 { "resolver.retry.normal", TO_RESOLVER_RETRY_NORMAL }, 3309 #define TO_RESOLVER_RETRY_FIRST 0x1E 3310 { "resolver.retry.first", TO_RESOLVER_RETRY_FIRST }, 3311 #define TO_CONTROL 0x1F 3312 { "control", TO_CONTROL }, 3313 { NULL, 0 }, 3314 }; 3315 3316 3317 static void 3318 settimeout(name, val, sticky) 3319 char *name; 3320 char *val; 3321 bool sticky; 3322 { 3323 register struct timeoutinfo *to; 3324 int i; 3325 time_t toval; 3326 3327 if (tTd(37, 2)) 3328 dprintf("settimeout(%s = %s)", name, val); 3329 3330 for (to = TimeOutTab; to->to_name != NULL; to++) 3331 { 3332 if (strcasecmp(to->to_name, name) == 0) 3333 break; 3334 } 3335 3336 if (to->to_name == NULL) 3337 syserr("settimeout: invalid timeout %s", name); 3338 3339 /* 3340 ** See if this option is preset for us. 3341 */ 3342 3343 if (!sticky && bitnset(to->to_code, StickyTimeoutOpt)) 3344 { 3345 if (tTd(37, 2)) 3346 dprintf(" (ignored)\n"); 3347 return; 3348 } 3349 3350 if (tTd(37, 2)) 3351 dprintf("\n"); 3352 3353 toval = convtime(val, 'm'); 3354 3355 switch (to->to_code) 3356 { 3357 case TO_INITIAL: 3358 TimeOuts.to_initial = toval; 3359 break; 3360 3361 case TO_MAIL: 3362 TimeOuts.to_mail = toval; 3363 break; 3364 3365 case TO_RCPT: 3366 TimeOuts.to_rcpt = toval; 3367 break; 3368 3369 case TO_DATAINIT: 3370 TimeOuts.to_datainit = toval; 3371 break; 3372 3373 case TO_DATABLOCK: 3374 TimeOuts.to_datablock = toval; 3375 break; 3376 3377 case TO_DATAFINAL: 3378 TimeOuts.to_datafinal = toval; 3379 break; 3380 3381 case TO_COMMAND: 3382 TimeOuts.to_nextcommand = toval; 3383 break; 3384 3385 case TO_RSET: 3386 TimeOuts.to_rset = toval; 3387 break; 3388 3389 case TO_HELO: 3390 TimeOuts.to_helo = toval; 3391 break; 3392 3393 case TO_QUIT: 3394 TimeOuts.to_quit = toval; 3395 break; 3396 3397 case TO_MISC: 3398 TimeOuts.to_miscshort = toval; 3399 break; 3400 3401 case TO_IDENT: 3402 TimeOuts.to_ident = toval; 3403 break; 3404 3405 case TO_FILEOPEN: 3406 TimeOuts.to_fileopen = toval; 3407 break; 3408 3409 case TO_CONNECT: 3410 TimeOuts.to_connect = toval; 3411 break; 3412 3413 case TO_ICONNECT: 3414 TimeOuts.to_iconnect = toval; 3415 break; 3416 3417 case TO_QUEUEWARN: 3418 toval = convtime(val, 'h'); 3419 TimeOuts.to_q_warning[TOC_NORMAL] = toval; 3420 TimeOuts.to_q_warning[TOC_URGENT] = toval; 3421 TimeOuts.to_q_warning[TOC_NONURGENT] = toval; 3422 break; 3423 3424 case TO_QUEUEWARN_NORMAL: 3425 toval = convtime(val, 'h'); 3426 TimeOuts.to_q_warning[TOC_NORMAL] = toval; 3427 break; 3428 3429 case TO_QUEUEWARN_URGENT: 3430 toval = convtime(val, 'h'); 3431 TimeOuts.to_q_warning[TOC_URGENT] = toval; 3432 break; 3433 3434 case TO_QUEUEWARN_NON_URGENT: 3435 toval = convtime(val, 'h'); 3436 TimeOuts.to_q_warning[TOC_NONURGENT] = toval; 3437 break; 3438 3439 case TO_QUEUERETURN: 3440 toval = convtime(val, 'd'); 3441 TimeOuts.to_q_return[TOC_NORMAL] = toval; 3442 TimeOuts.to_q_return[TOC_URGENT] = toval; 3443 TimeOuts.to_q_return[TOC_NONURGENT] = toval; 3444 break; 3445 3446 case TO_QUEUERETURN_NORMAL: 3447 toval = convtime(val, 'd'); 3448 TimeOuts.to_q_return[TOC_NORMAL] = toval; 3449 break; 3450 3451 case TO_QUEUERETURN_URGENT: 3452 toval = convtime(val, 'd'); 3453 TimeOuts.to_q_return[TOC_URGENT] = toval; 3454 break; 3455 3456 case TO_QUEUERETURN_NON_URGENT: 3457 toval = convtime(val, 'd'); 3458 TimeOuts.to_q_return[TOC_NONURGENT] = toval; 3459 break; 3460 3461 3462 case TO_HOSTSTATUS: 3463 MciInfoTimeout = toval; 3464 break; 3465 3466 case TO_RESOLVER_RETRANS: 3467 toval = convtime(val, 's'); 3468 TimeOuts.res_retrans[RES_TO_DEFAULT] = toval; 3469 TimeOuts.res_retrans[RES_TO_FIRST] = toval; 3470 TimeOuts.res_retrans[RES_TO_NORMAL] = toval; 3471 break; 3472 3473 case TO_RESOLVER_RETRY: 3474 i = atoi(val); 3475 TimeOuts.res_retry[RES_TO_DEFAULT] = i; 3476 TimeOuts.res_retry[RES_TO_FIRST] = i; 3477 TimeOuts.res_retry[RES_TO_NORMAL] = i; 3478 break; 3479 3480 case TO_RESOLVER_RETRANS_NORMAL: 3481 TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's'); 3482 break; 3483 3484 case TO_RESOLVER_RETRY_NORMAL: 3485 TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val); 3486 break; 3487 3488 case TO_RESOLVER_RETRANS_FIRST: 3489 TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's'); 3490 break; 3491 3492 case TO_RESOLVER_RETRY_FIRST: 3493 TimeOuts.res_retry[RES_TO_FIRST] = atoi(val); 3494 break; 3495 3496 case TO_CONTROL: 3497 TimeOuts.to_control = toval; 3498 break; 3499 3500 default: 3501 syserr("settimeout: invalid timeout %s", name); 3502 break; 3503 } 3504 3505 if (sticky) 3506 setbitn(to->to_code, StickyTimeoutOpt); 3507 } 3508 /* 3509 ** INITTIMEOUTS -- parse and set timeout values 3510 ** 3511 ** Parameters: 3512 ** val -- a pointer to the values. If NULL, do initial 3513 ** settings. 3514 ** sticky -- if set, don't let other setoptions override 3515 ** this suboption value. 3516 ** 3517 ** Returns: 3518 ** none. 3519 ** 3520 ** Side Effects: 3521 ** Initializes the TimeOuts structure 3522 */ 3523 3524 void 3525 inittimeouts(val, sticky) 3526 register char *val; 3527 bool sticky; 3528 { 3529 register char *p; 3530 3531 if (tTd(37, 2)) 3532 dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val); 3533 if (val == NULL) 3534 { 3535 TimeOuts.to_connect = (time_t) 0 SECONDS; 3536 TimeOuts.to_initial = (time_t) 5 MINUTES; 3537 TimeOuts.to_helo = (time_t) 5 MINUTES; 3538 TimeOuts.to_mail = (time_t) 10 MINUTES; 3539 TimeOuts.to_rcpt = (time_t) 1 HOUR; 3540 TimeOuts.to_datainit = (time_t) 5 MINUTES; 3541 TimeOuts.to_datablock = (time_t) 1 HOUR; 3542 TimeOuts.to_datafinal = (time_t) 1 HOUR; 3543 TimeOuts.to_rset = (time_t) 5 MINUTES; 3544 TimeOuts.to_quit = (time_t) 2 MINUTES; 3545 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 3546 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 3547 #if IDENTPROTO 3548 TimeOuts.to_ident = (time_t) 5 SECONDS; 3549 #else /* IDENTPROTO */ 3550 TimeOuts.to_ident = (time_t) 0 SECONDS; 3551 #endif /* IDENTPROTO */ 3552 TimeOuts.to_fileopen = (time_t) 60 SECONDS; 3553 TimeOuts.to_control = (time_t) 2 MINUTES; 3554 if (tTd(37, 5)) 3555 { 3556 dprintf("Timeouts:\n"); 3557 dprintf(" connect = %ld\n", (long)TimeOuts.to_connect); 3558 dprintf(" initial = %ld\n", (long)TimeOuts.to_initial); 3559 dprintf(" helo = %ld\n", (long)TimeOuts.to_helo); 3560 dprintf(" mail = %ld\n", (long)TimeOuts.to_mail); 3561 dprintf(" rcpt = %ld\n", (long)TimeOuts.to_rcpt); 3562 dprintf(" datainit = %ld\n", (long)TimeOuts.to_datainit); 3563 dprintf(" datablock = %ld\n", (long)TimeOuts.to_datablock); 3564 dprintf(" datafinal = %ld\n", (long)TimeOuts.to_datafinal); 3565 dprintf(" rset = %ld\n", (long)TimeOuts.to_rset); 3566 dprintf(" quit = %ld\n", (long)TimeOuts.to_quit); 3567 dprintf(" nextcommand = %ld\n", (long)TimeOuts.to_nextcommand); 3568 dprintf(" miscshort = %ld\n", (long)TimeOuts.to_miscshort); 3569 dprintf(" ident = %ld\n", (long)TimeOuts.to_ident); 3570 dprintf(" fileopen = %ld\n", (long)TimeOuts.to_fileopen); 3571 dprintf(" control = %ld\n", (long)TimeOuts.to_control); 3572 } 3573 return; 3574 } 3575 3576 for (;; val = p) 3577 { 3578 while (isascii(*val) && isspace(*val)) 3579 val++; 3580 if (*val == '\0') 3581 break; 3582 for (p = val; *p != '\0' && *p != ','; p++) 3583 continue; 3584 if (*p != '\0') 3585 *p++ = '\0'; 3586 3587 if (isascii(*val) && isdigit(*val)) 3588 { 3589 /* old syntax -- set everything */ 3590 TimeOuts.to_mail = convtime(val, 'm'); 3591 TimeOuts.to_rcpt = TimeOuts.to_mail; 3592 TimeOuts.to_datainit = TimeOuts.to_mail; 3593 TimeOuts.to_datablock = TimeOuts.to_mail; 3594 TimeOuts.to_datafinal = TimeOuts.to_mail; 3595 TimeOuts.to_nextcommand = TimeOuts.to_mail; 3596 if (sticky) 3597 { 3598 setbitn(TO_MAIL, StickyTimeoutOpt); 3599 setbitn(TO_RCPT, StickyTimeoutOpt); 3600 setbitn(TO_DATAINIT, StickyTimeoutOpt); 3601 setbitn(TO_DATABLOCK, StickyTimeoutOpt); 3602 setbitn(TO_DATAFINAL, StickyTimeoutOpt); 3603 setbitn(TO_COMMAND, StickyTimeoutOpt); 3604 } 3605 continue; 3606 } 3607 else 3608 { 3609 register char *q = strchr(val, ':'); 3610 3611 if (q == NULL && (q = strchr(val, '=')) == NULL) 3612 { 3613 /* syntax error */ 3614 continue; 3615 } 3616 *q++ = '\0'; 3617 settimeout(val, q, sticky); 3618 } 3619 } 3620 } 3621