readcf.c revision 42575
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 14static char sccsid[] = "@(#)readcf.c 8.235 (Berkeley) 8/18/1998"; 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 70void 71readcf(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 finis(FALSE, EX_OSFILE); 110 } 111 112 if (fstat(fileno(cf), &statb) < 0) 113 { 114 syserr("cannot fstat"); 115 finis(FALSE, EX_OSFILE); 116 } 117 118 if (!S_ISREG(statb.st_mode)) 119 { 120 syserr("not a plain file"); 121 finis(FALSE, 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 finis(FALSE, 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 579void 580translate_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 660void 661toomany(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 687void 688fileclass(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 820void 821makemailer(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 1154char * 1155munchstring(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 1222char ** 1223makeargv(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 1263void 1264printrules() 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 1294void 1295printmailer(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 1350static BITMAP StickyOpt; /* set if option is stuck */ 1351extern void settimeout __P((char *, char *)); 1352 1353 1354#if NAMED_BIND 1355 1356struct 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 1377struct 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_USER 1519#define O_TRUSTUSER 0xa7 1520 { "TrustedUser", O_TRUSTUSER, FALSE }, 1521#endif 1522#if _FFR_MAX_MIME_HEADER_LENGTH 1523#define O_MAXMIMEHDRLEN 0xa8 1524 { "MaxMimeHeaderLength", O_MAXMIMEHDRLEN, FALSE }, 1525#endif 1526#if _FFR_CONTROL_SOCKET 1527#define O_CONTROLSOCKET 0xa9 1528 { "ControlSocketName", O_CONTROLSOCKET, FALSE }, 1529#endif 1530 { NULL, '\0', FALSE } 1531}; 1532 1533 1534 1535void 1536setoption(opt, val, safe, sticky, e) 1537 int opt; 1538 char *val; 1539 bool safe; 1540 bool sticky; 1541 register ENVELOPE *e; 1542{ 1543 register char *p; 1544 register struct optioninfo *o; 1545 char *subopt; 1546 int mid; 1547 bool can_setuid = RunAsUid == 0; 1548 auto char *ep; 1549 char buf[50]; 1550 extern bool atobool __P((char *)); 1551 extern time_t convtime __P((char *, char)); 1552 extern int QueueLA; 1553 extern int RefuseLA; 1554 extern bool Warn_Q_option; 1555 extern void setalias __P((char *)); 1556 extern int atooct __P((char *)); 1557 extern void setdefuser __P((void)); 1558 extern void setdaemonoptions __P((char *)); 1559 1560 errno = 0; 1561 if (opt == ' ') 1562 { 1563 /* full word options */ 1564 struct optioninfo *sel; 1565 1566 p = strchr(val, '='); 1567 if (p == NULL) 1568 p = &val[strlen(val)]; 1569 while (*--p == ' ') 1570 continue; 1571 while (*++p == ' ') 1572 *p = '\0'; 1573 if (p == val) 1574 { 1575 syserr("readcf: null option name"); 1576 return; 1577 } 1578 if (*p == '=') 1579 *p++ = '\0'; 1580 while (*p == ' ') 1581 p++; 1582 subopt = strchr(val, '.'); 1583 if (subopt != NULL) 1584 *subopt++ = '\0'; 1585 sel = NULL; 1586 for (o = OptionTab; o->o_name != NULL; o++) 1587 { 1588 if (strncasecmp(o->o_name, val, strlen(val)) != 0) 1589 continue; 1590 if (strlen(o->o_name) == strlen(val)) 1591 { 1592 /* completely specified -- this must be it */ 1593 sel = NULL; 1594 break; 1595 } 1596 if (sel != NULL) 1597 break; 1598 sel = o; 1599 } 1600 if (sel != NULL && o->o_name == NULL) 1601 o = sel; 1602 else if (o->o_name == NULL) 1603 { 1604 syserr("readcf: unknown option name %s", val); 1605 return; 1606 } 1607 else if (sel != NULL) 1608 { 1609 syserr("readcf: ambiguous option name %s (matches %s and %s)", 1610 val, sel->o_name, o->o_name); 1611 return; 1612 } 1613 if (strlen(val) != strlen(o->o_name)) 1614 { 1615 int oldVerbose = Verbose; 1616 1617 Verbose = 1; 1618 message("Option %s used as abbreviation for %s", 1619 val, o->o_name); 1620 Verbose = oldVerbose; 1621 } 1622 opt = o->o_code; 1623 val = p; 1624 } 1625 else 1626 { 1627 for (o = OptionTab; o->o_name != NULL; o++) 1628 { 1629 if (o->o_code == opt) 1630 break; 1631 } 1632 subopt = NULL; 1633 } 1634 1635 if (tTd(37, 1)) 1636 { 1637 printf(isascii(opt) && isprint(opt) ? 1638 "setoption %s (%c).%s=" : 1639 "setoption %s (0x%x).%s=", 1640 o->o_name == NULL ? "<unknown>" : o->o_name, 1641 opt, 1642 subopt == NULL ? "" : subopt); 1643 xputs(val); 1644 } 1645 1646 /* 1647 ** See if this option is preset for us. 1648 */ 1649 1650 if (!sticky && bitnset(opt, StickyOpt)) 1651 { 1652 if (tTd(37, 1)) 1653 printf(" (ignored)\n"); 1654 return; 1655 } 1656 1657 /* 1658 ** Check to see if this option can be specified by this user. 1659 */ 1660 1661 if (!safe && RealUid == 0) 1662 safe = TRUE; 1663 if (!safe && !o->o_safe) 1664 { 1665 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1666 { 1667 if (tTd(37, 1)) 1668 printf(" (unsafe)"); 1669 (void) drop_privileges(TRUE); 1670 } 1671 } 1672 if (tTd(37, 1)) 1673 printf("\n"); 1674 1675 switch (opt & 0xff) 1676 { 1677 case '7': /* force seven-bit input */ 1678 SevenBitInput = atobool(val); 1679 break; 1680 1681#if MIME8TO7 1682 case '8': /* handling of 8-bit input */ 1683 switch (*val) 1684 { 1685 case 'm': /* convert 8-bit, convert MIME */ 1686 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1687 break; 1688 1689 case 'p': /* pass 8 bit, convert MIME */ 1690 MimeMode = MM_CVTMIME|MM_PASS8BIT; 1691 break; 1692 1693 case 's': /* strict adherence */ 1694 MimeMode = MM_CVTMIME; 1695 break; 1696 1697#if 0 1698 case 'r': /* reject 8-bit, don't convert MIME */ 1699 MimeMode = 0; 1700 break; 1701 1702 case 'j': /* "just send 8" */ 1703 MimeMode = MM_PASS8BIT; 1704 break; 1705 1706 case 'a': /* encode 8 bit if available */ 1707 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1708 break; 1709 1710 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1711 MimeMode = MM_MIME8BIT; 1712 break; 1713#endif 1714 1715 default: 1716 syserr("Unknown 8-bit mode %c", *val); 1717 finis(FALSE, EX_USAGE); 1718 } 1719 break; 1720#endif 1721 1722 case 'A': /* set default alias file */ 1723 if (val[0] == '\0') 1724 setalias("aliases"); 1725 else 1726 setalias(val); 1727 break; 1728 1729 case 'a': /* look N minutes for "@:@" in alias file */ 1730 if (val[0] == '\0') 1731 SafeAlias = 5 * 60; /* five minutes */ 1732 else 1733 SafeAlias = convtime(val, 'm'); 1734 break; 1735 1736 case 'B': /* substitution for blank character */ 1737 SpaceSub = val[0]; 1738 if (SpaceSub == '\0') 1739 SpaceSub = ' '; 1740 break; 1741 1742 case 'b': /* min blocks free on queue fs/max msg size */ 1743 p = strchr(val, '/'); 1744 if (p != NULL) 1745 { 1746 *p++ = '\0'; 1747 MaxMessageSize = atol(p); 1748 } 1749 MinBlocksFree = atol(val); 1750 break; 1751 1752 case 'c': /* don't connect to "expensive" mailers */ 1753 NoConnect = atobool(val); 1754 break; 1755 1756 case 'C': /* checkpoint every N addresses */ 1757 CheckpointInterval = atoi(val); 1758 break; 1759 1760 case 'd': /* delivery mode */ 1761 switch (*val) 1762 { 1763 case '\0': 1764 e->e_sendmode = SM_DELIVER; 1765 break; 1766 1767 case SM_QUEUE: /* queue only */ 1768 case SM_DEFER: /* queue only and defer map lookups */ 1769#if !QUEUE 1770 syserr("need QUEUE to set -odqueue or -oddefer"); 1771#endif /* QUEUE */ 1772 /* fall through..... */ 1773 1774 case SM_DELIVER: /* do everything */ 1775 case SM_FORK: /* fork after verification */ 1776 e->e_sendmode = *val; 1777 break; 1778 1779 default: 1780 syserr("Unknown delivery mode %c", *val); 1781 finis(FALSE, EX_USAGE); 1782 } 1783 buf[0] = (char)e->e_sendmode; 1784 buf[1] = '\0'; 1785 define(macid("{deliveryMode}", NULL), newstr(buf), e); 1786 break; 1787 1788 case 'D': /* rebuild alias database as needed */ 1789 AutoRebuild = atobool(val); 1790 break; 1791 1792 case 'E': /* error message header/header file */ 1793 if (*val != '\0') 1794 ErrMsgFile = newstr(val); 1795 break; 1796 1797 case 'e': /* set error processing mode */ 1798 switch (*val) 1799 { 1800 case EM_QUIET: /* be silent about it */ 1801 case EM_MAIL: /* mail back */ 1802 case EM_BERKNET: /* do berknet error processing */ 1803 case EM_WRITE: /* write back (or mail) */ 1804 case EM_PRINT: /* print errors normally (default) */ 1805 e->e_errormode = *val; 1806 break; 1807 } 1808 break; 1809 1810 case 'F': /* file mode */ 1811 FileMode = atooct(val) & 0777; 1812 break; 1813 1814 case 'f': /* save Unix-style From lines on front */ 1815 SaveFrom = atobool(val); 1816 break; 1817 1818 case 'G': /* match recipients against GECOS field */ 1819 MatchGecos = atobool(val); 1820 break; 1821 1822 case 'g': /* default gid */ 1823 g_opt: 1824 if (isascii(*val) && isdigit(*val)) 1825 DefGid = atoi(val); 1826 else 1827 { 1828 register struct group *gr; 1829 1830 DefGid = -1; 1831 gr = getgrnam(val); 1832 if (gr == NULL) 1833 syserr("readcf: option %c: unknown group %s", 1834 opt, val); 1835 else 1836 DefGid = gr->gr_gid; 1837 } 1838 break; 1839 1840 case 'H': /* help file */ 1841 if (val[0] == '\0') 1842 HelpFile = "sendmail.hf"; 1843 else 1844 HelpFile = newstr(val); 1845 break; 1846 1847 case 'h': /* maximum hop count */ 1848 MaxHopCount = atoi(val); 1849 break; 1850 1851 case 'I': /* use internet domain name server */ 1852#if NAMED_BIND 1853 for (p = val; *p != 0; ) 1854 { 1855 bool clearmode; 1856 char *q; 1857 struct resolverflags *rfp; 1858 1859 while (*p == ' ') 1860 p++; 1861 if (*p == '\0') 1862 break; 1863 clearmode = FALSE; 1864 if (*p == '-') 1865 clearmode = TRUE; 1866 else if (*p != '+') 1867 p--; 1868 p++; 1869 q = p; 1870 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1871 p++; 1872 if (*p != '\0') 1873 *p++ = '\0'; 1874 if (strcasecmp(q, "HasWildcardMX") == 0) 1875 { 1876 HasWildcardMX = !clearmode; 1877 continue; 1878 } 1879 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1880 { 1881 if (strcasecmp(q, rfp->rf_name) == 0) 1882 break; 1883 } 1884 if (rfp->rf_name == NULL) 1885 syserr("readcf: I option value %s unrecognized", q); 1886 else if (clearmode) 1887 _res.options &= ~rfp->rf_bits; 1888 else 1889 _res.options |= rfp->rf_bits; 1890 } 1891 if (tTd(8, 2)) 1892 printf("_res.options = %x, HasWildcardMX = %d\n", 1893 (u_int) _res.options, HasWildcardMX); 1894#else 1895 usrerr("name server (I option) specified but BIND not compiled in"); 1896#endif 1897 break; 1898 1899 case 'i': /* ignore dot lines in message */ 1900 IgnrDot = atobool(val); 1901 break; 1902 1903 case 'j': /* send errors in MIME (RFC 1341) format */ 1904 SendMIMEErrors = atobool(val); 1905 break; 1906 1907 case 'J': /* .forward search path */ 1908 ForwardPath = newstr(val); 1909 break; 1910 1911 case 'k': /* connection cache size */ 1912 MaxMciCache = atoi(val); 1913 if (MaxMciCache < 0) 1914 MaxMciCache = 0; 1915 break; 1916 1917 case 'K': /* connection cache timeout */ 1918 MciCacheTimeout = convtime(val, 'm'); 1919 break; 1920 1921 case 'l': /* use Errors-To: header */ 1922 UseErrorsTo = atobool(val); 1923 break; 1924 1925 case 'L': /* log level */ 1926 if (safe || LogLevel < atoi(val)) 1927 LogLevel = atoi(val); 1928 break; 1929 1930 case 'M': /* define macro */ 1931 mid = macid(val, &ep); 1932 p = newstr(ep); 1933 if (!safe) 1934 cleanstrcpy(p, p, MAXNAME); 1935 define(mid, p, CurEnv); 1936 sticky = FALSE; 1937 break; 1938 1939 case 'm': /* send to me too */ 1940 MeToo = atobool(val); 1941 break; 1942 1943 case 'n': /* validate RHS in newaliases */ 1944 CheckAliases = atobool(val); 1945 break; 1946 1947 /* 'N' available -- was "net name" */ 1948 1949 case 'O': /* daemon options */ 1950#if DAEMON 1951 setdaemonoptions(val); 1952#else 1953 syserr("DaemonPortOptions (O option) set but DAEMON not compiled in"); 1954#endif 1955 break; 1956 1957 case 'o': /* assume old style headers */ 1958 if (atobool(val)) 1959 CurEnv->e_flags |= EF_OLDSTYLE; 1960 else 1961 CurEnv->e_flags &= ~EF_OLDSTYLE; 1962 break; 1963 1964 case 'p': /* select privacy level */ 1965 p = val; 1966 for (;;) 1967 { 1968 register struct prival *pv; 1969 extern struct prival PrivacyValues[]; 1970 1971 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1972 p++; 1973 if (*p == '\0') 1974 break; 1975 val = p; 1976 while (isascii(*p) && isalnum(*p)) 1977 p++; 1978 if (*p != '\0') 1979 *p++ = '\0'; 1980 1981 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1982 { 1983 if (strcasecmp(val, pv->pv_name) == 0) 1984 break; 1985 } 1986 if (pv->pv_name == NULL) 1987 syserr("readcf: Op line: %s unrecognized", val); 1988 PrivacyFlags |= pv->pv_flag; 1989 } 1990 sticky = FALSE; 1991 break; 1992 1993 case 'P': /* postmaster copy address for returned mail */ 1994 PostMasterCopy = newstr(val); 1995 break; 1996 1997 case 'q': /* slope of queue only function */ 1998 QueueFactor = atoi(val); 1999 break; 2000 2001 case 'Q': /* queue directory */ 2002 if (val[0] == '\0') 2003 QueueDir = "mqueue"; 2004 else 2005 QueueDir = newstr(val); 2006 if (RealUid != 0 && !safe) 2007 Warn_Q_option = TRUE; 2008 break; 2009 2010 case 'R': /* don't prune routes */ 2011 DontPruneRoutes = atobool(val); 2012 break; 2013 2014 case 'r': /* read timeout */ 2015 if (subopt == NULL) 2016 inittimeouts(val); 2017 else 2018 settimeout(subopt, val); 2019 break; 2020 2021 case 'S': /* status file */ 2022 if (val[0] == '\0') 2023 StatFile = "sendmail.st"; 2024 else 2025 StatFile = newstr(val); 2026 break; 2027 2028 case 's': /* be super safe, even if expensive */ 2029 SuperSafe = atobool(val); 2030 break; 2031 2032 case 'T': /* queue timeout */ 2033 p = strchr(val, '/'); 2034 if (p != NULL) 2035 { 2036 *p++ = '\0'; 2037 settimeout("queuewarn", p); 2038 } 2039 settimeout("queuereturn", val); 2040 break; 2041 2042 case 't': /* time zone name */ 2043 TimeZoneSpec = newstr(val); 2044 break; 2045 2046 case 'U': /* location of user database */ 2047 UdbSpec = newstr(val); 2048 break; 2049 2050 case 'u': /* set default uid */ 2051 for (p = val; *p != '\0'; p++) 2052 { 2053 if (*p == '.' || *p == '/' || *p == ':') 2054 { 2055 *p++ = '\0'; 2056 break; 2057 } 2058 } 2059 if (isascii(*val) && isdigit(*val)) 2060 { 2061 DefUid = atoi(val); 2062 setdefuser(); 2063 } 2064 else 2065 { 2066 register struct passwd *pw; 2067 2068 DefUid = -1; 2069 pw = sm_getpwnam(val); 2070 if (pw == NULL) 2071 syserr("readcf: option u: unknown user %s", val); 2072 else 2073 { 2074 DefUid = pw->pw_uid; 2075 DefGid = pw->pw_gid; 2076 DefUser = newstr(pw->pw_name); 2077 } 2078 } 2079 2080#ifdef UID_MAX 2081 if (DefUid > UID_MAX) 2082 { 2083 syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored", 2084 DefUid, UID_MAX); 2085 } 2086#endif 2087 2088 /* handle the group if it is there */ 2089 if (*p == '\0') 2090 break; 2091 val = p; 2092 goto g_opt; 2093 2094 case 'V': /* fallback MX host */ 2095 if (val[0] != '\0') 2096 FallBackMX = newstr(val); 2097 break; 2098 2099 case 'v': /* run in verbose mode */ 2100 Verbose = atobool(val) ? 1 : 0; 2101 break; 2102 2103 case 'w': /* if we are best MX, try host directly */ 2104 TryNullMXList = atobool(val); 2105 break; 2106 2107 /* 'W' available -- was wizard password */ 2108 2109 case 'x': /* load avg at which to auto-queue msgs */ 2110 QueueLA = atoi(val); 2111 break; 2112 2113 case 'X': /* load avg at which to auto-reject connections */ 2114 RefuseLA = atoi(val); 2115 break; 2116 2117 case 'y': /* work recipient factor */ 2118 WkRecipFact = atoi(val); 2119 break; 2120 2121 case 'Y': /* fork jobs during queue runs */ 2122 ForkQueueRuns = atobool(val); 2123 break; 2124 2125 case 'z': /* work message class factor */ 2126 WkClassFact = atoi(val); 2127 break; 2128 2129 case 'Z': /* work time factor */ 2130 WkTimeFact = atoi(val); 2131 break; 2132 2133 case O_QUEUESORTORD: /* queue sorting order */ 2134 switch (*val) 2135 { 2136 case 'h': /* Host first */ 2137 case 'H': 2138 QueueSortOrder = QS_BYHOST; 2139 break; 2140 2141 case 'p': /* Priority order */ 2142 case 'P': 2143 QueueSortOrder = QS_BYPRIORITY; 2144 break; 2145 2146 case 't': /* Submission time */ 2147 case 'T': 2148 QueueSortOrder = QS_BYTIME; 2149 break; 2150 2151 default: 2152 syserr("Invalid queue sort order \"%s\"", val); 2153 } 2154 break; 2155 2156 case O_HOSTSFILE: /* pathname of /etc/hosts file */ 2157 HostsFile = newstr(val); 2158 break; 2159 2160 case O_MQA: /* minimum queue age between deliveries */ 2161 MinQueueAge = convtime(val, 'm'); 2162 break; 2163 2164 case O_DEFCHARSET: /* default character set for mimefying */ 2165 DefaultCharSet = newstr(denlstring(val, TRUE, TRUE)); 2166 break; 2167 2168 case O_SSFILE: /* service switch file */ 2169 ServiceSwitchFile = newstr(val); 2170 break; 2171 2172 case O_DIALDELAY: /* delay for dial-on-demand operation */ 2173 DialDelay = convtime(val, 's'); 2174 break; 2175 2176 case O_NORCPTACTION: /* what to do if no recipient */ 2177 if (strcasecmp(val, "none") == 0) 2178 NoRecipientAction = NRA_NO_ACTION; 2179 else if (strcasecmp(val, "add-to") == 0) 2180 NoRecipientAction = NRA_ADD_TO; 2181 else if (strcasecmp(val, "add-apparently-to") == 0) 2182 NoRecipientAction = NRA_ADD_APPARENTLY_TO; 2183 else if (strcasecmp(val, "add-bcc") == 0) 2184 NoRecipientAction = NRA_ADD_BCC; 2185 else if (strcasecmp(val, "add-to-undisclosed") == 0) 2186 NoRecipientAction = NRA_ADD_TO_UNDISCLOSED; 2187 else 2188 syserr("Invalid NoRecipientAction: %s", val); 2189 break; 2190 2191 case O_SAFEFILEENV: /* chroot() environ for writing to files */ 2192 SafeFileEnv = newstr(val); 2193 break; 2194 2195 case O_MAXMSGSIZE: /* maximum message size */ 2196 MaxMessageSize = atol(val); 2197 break; 2198 2199 case O_COLONOKINADDR: /* old style handling of colon addresses */ 2200 ColonOkInAddr = atobool(val); 2201 break; 2202 2203 case O_MAXQUEUERUN: /* max # of jobs in a single queue run */ 2204 MaxQueueRun = atol(val); 2205 break; 2206 2207 case O_MAXCHILDREN: /* max # of children of daemon */ 2208 MaxChildren = atoi(val); 2209 break; 2210 2211 case O_KEEPCNAMES: /* don't expand CNAME records */ 2212 DontExpandCnames = atobool(val); 2213 break; 2214 2215 case O_MUSTQUOTE: /* must quote these characters in phrases */ 2216 strcpy(buf, "@,;:\\()[]"); 2217 if (strlen(val) < (SIZE_T) sizeof buf - 10) 2218 strcat(buf, val); 2219 MustQuoteChars = newstr(buf); 2220 break; 2221 2222 case O_SMTPGREETING: /* SMTP greeting message (old $e macro) */ 2223 SmtpGreeting = newstr(munchstring(val, NULL, '\0')); 2224 break; 2225 2226 case O_UNIXFROM: /* UNIX From_ line (old $l macro) */ 2227 UnixFromLine = newstr(munchstring(val, NULL, '\0')); 2228 break; 2229 2230 case O_OPCHARS: /* operator characters (old $o macro) */ 2231 OperatorChars = newstr(munchstring(val, NULL, '\0')); 2232 break; 2233 2234 case O_DONTINITGRPS: /* don't call initgroups(3) */ 2235 DontInitGroups = atobool(val); 2236 break; 2237 2238 case O_SLFH: /* make sure from fits on one line */ 2239 SingleLineFromHeader = atobool(val); 2240 break; 2241 2242 case O_ABH: /* allow HELO commands with syntax errors */ 2243 AllowBogusHELO = atobool(val); 2244 break; 2245 2246 case O_CONNTHROT: /* connection rate throttle */ 2247 ConnRateThrottle = atoi(val); 2248 break; 2249 2250 case O_UGW: /* group writable files are unsafe */ 2251 if (!atobool(val)) 2252 DontBlameSendmail |= DBS_GROUPWRITABLEFORWARDFILESAFE|DBS_GROUPWRITABLEINCLUDEFILESAFE; 2253 break; 2254 2255 case O_DBLBOUNCE: /* address to which to send double bounces */ 2256 if (val[0] != '\0') 2257 DoubleBounceAddr = newstr(val); 2258 else 2259 syserr("readcf: option DoubleBounceAddress: value required"); 2260 break; 2261 2262 case O_HSDIR: /* persistent host status directory */ 2263 if (val[0] != '\0') 2264 HostStatDir = newstr(val); 2265 break; 2266 2267 case O_SINGTHREAD: /* single thread deliveries (requires hsdir) */ 2268 SingleThreadDelivery = atobool(val); 2269 break; 2270 2271 case O_RUNASUSER: /* run bulk of code as this user */ 2272 for (p = val; *p != '\0'; p++) 2273 { 2274 if (*p == '.' || *p == '/' || *p == ':') 2275 { 2276 *p++ = '\0'; 2277 break; 2278 } 2279 } 2280 if (isascii(*val) && isdigit(*val)) 2281 { 2282 if (can_setuid) 2283 RunAsUid = atoi(val); 2284 } 2285 else 2286 { 2287 register struct passwd *pw; 2288 2289 pw = sm_getpwnam(val); 2290 if (pw == NULL) 2291 syserr("readcf: option RunAsUser: unknown user %s", val); 2292 else if (can_setuid) 2293 { 2294 if (*p == '\0') 2295 RunAsUserName = newstr(val); 2296 RunAsUid = pw->pw_uid; 2297 RunAsGid = pw->pw_gid; 2298 } 2299 } 2300#ifdef UID_MAX 2301 if (RunAsUid > UID_MAX) 2302 { 2303 syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored", 2304 RunAsUid, UID_MAX); 2305 } 2306#endif 2307 if (*p != '\0') 2308 { 2309 if (isascii(*p) && isdigit(*p)) 2310 { 2311 if (can_setuid) 2312 RunAsGid = atoi(p); 2313 } 2314 else 2315 { 2316 register struct group *gr; 2317 2318 gr = getgrnam(p); 2319 if (gr == NULL) 2320 syserr("readcf: option RunAsUser: unknown group %s", 2321 p); 2322 else if (can_setuid) 2323 RunAsGid = gr->gr_gid; 2324 } 2325 } 2326 if (tTd(47, 5)) 2327 printf("readcf: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid); 2328 break; 2329 2330#if _FFR_DSN_RRT_OPTION 2331 case O_DSN_RRT: 2332 RrtImpliesDsn = atobool(val); 2333 break; 2334#endif 2335 2336#if _FFR_PIDFILE_OPTION 2337 case O_PIDFILE: 2338 free(PidFile); 2339 PidFile = newstr(val); 2340 break; 2341#endif 2342 2343 case O_DONTBLAMESENDMAIL: 2344 p = val; 2345 for (;;) 2346 { 2347 register struct dbsval *dbs; 2348 extern struct dbsval DontBlameSendmailValues[]; 2349 2350 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 2351 p++; 2352 if (*p == '\0') 2353 break; 2354 val = p; 2355 while (isascii(*p) && isalnum(*p)) 2356 p++; 2357 if (*p != '\0') 2358 *p++ = '\0'; 2359 2360 for (dbs = DontBlameSendmailValues; 2361 dbs->dbs_name != NULL; dbs++) 2362 { 2363 if (strcasecmp(val, dbs->dbs_name) == 0) 2364 break; 2365 } 2366 if (dbs->dbs_name == NULL) 2367 syserr("readcf: DontBlameSendmail option: %s unrecognized", val); 2368 else if (dbs->dbs_flag == DBS_SAFE) 2369 DontBlameSendmail = DBS_SAFE; 2370 else 2371 DontBlameSendmail |= dbs->dbs_flag; 2372 } 2373 sticky = FALSE; 2374 break; 2375 2376 case O_DPI: 2377 DontProbeInterfaces = atobool(val); 2378 break; 2379 2380 case O_MAXRCPT: 2381 MaxRcptPerMsg = atoi(val); 2382 break; 2383 2384#if _FFR_DEADLETTERDROP_OPTION 2385 case O_DEADLETTER: 2386 if (DeadLetterDrop != NULL) 2387 free(DeadLetterDrop); 2388 DeadLetterDrop = newstr(val); 2389 break; 2390#endif 2391 2392#if _FFR_DONTLOCKFILESFORREAD_OPTION 2393 case O_DONTLOCK: 2394 DontLockReadFiles = atobool(val); 2395 break; 2396#endif 2397 2398#if _FFR_MAXALIASRECURSION_OPTION 2399 case O_MAXALIASRCSN: 2400 MaxAliasRecursion = atoi(val); 2401 break; 2402#endif 2403 2404#if _FFR_CONNECTONLYTO_OPTION 2405 case O_CNCTONLYTO: 2406 /* XXX should probably use gethostbyname */ 2407 ConnectOnlyTo = inet_addr(val); 2408 break; 2409#endif 2410 2411#if _FFR_TRUSTED_USER 2412 case O_TRUSTUSER: 2413 if (isascii(*val) && isdigit(*val)) 2414 TrustedUid = atoi(val); 2415 else 2416 { 2417 register struct passwd *pw; 2418 2419 TrustedUid = 0; 2420 pw = sm_getpwnam(val); 2421 if (pw == NULL) 2422 syserr("readcf: option TrustedUser: unknown user %s", val); 2423 else 2424 TrustedUid = pw->pw_uid; 2425 } 2426 2427#ifdef UID_MAX 2428 if (TrustedUid > UID_MAX) 2429 { 2430 syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)", 2431 TrustedUid, UID_MAX); 2432 TrustedUid = 0; 2433 } 2434#endif 2435 break; 2436#endif 2437 2438#if _FFR_MAX_MIME_HEADER_LENGTH 2439 case O_MAXMIMEHDRLEN: 2440 p = strchr(val, '/'); 2441 if (p != NULL) 2442 *p++ = '\0'; 2443 MaxMimeHeaderLength = atoi(val); 2444 if (p != NULL && *p != '\0') 2445 MaxMimeFieldLength = atoi(p); 2446 else 2447 MaxMimeFieldLength = MaxMimeHeaderLength / 2; 2448 2449 if (MaxMimeHeaderLength < 0) 2450 MaxMimeHeaderLength = 0; 2451 else if (MaxMimeHeaderLength < 128) 2452 printf("Warning: MaxMimeHeaderLength: header length limit set lower than 128\n"); 2453 2454 if (MaxMimeFieldLength < 0) 2455 MaxMimeFieldLength = 0; 2456 else if (MaxMimeFieldLength < 40) 2457 printf("Warning: MaxMimeHeaderLength: field length limit set lower than 40\n"); 2458 break; 2459#endif 2460 2461#if _FFR_CONTROL_SOCKET 2462 case O_CONTROLSOCKET: 2463 if (ControlSocketName != NULL) 2464 free(ControlSocketName); 2465 ControlSocketName = newstr(val); 2466 break; 2467#endif 2468 2469 default: 2470 if (tTd(37, 1)) 2471 { 2472 if (isascii(opt) && isprint(opt)) 2473 printf("Warning: option %c unknown\n", opt); 2474 else 2475 printf("Warning: option 0x%x unknown\n", opt); 2476 } 2477 break; 2478 } 2479 if (sticky) 2480 setbitn(opt, StickyOpt); 2481} 2482/* 2483** SETCLASS -- set a string into a class 2484** 2485** Parameters: 2486** class -- the class to put the string in. 2487** str -- the string to enter 2488** 2489** Returns: 2490** none. 2491** 2492** Side Effects: 2493** puts the word into the symbol table. 2494*/ 2495 2496void 2497setclass(class, str) 2498 int class; 2499 char *str; 2500{ 2501 register STAB *s; 2502 2503 if (tTd(37, 8)) 2504 printf("setclass(%s, %s)\n", macname(class), str); 2505 s = stab(str, ST_CLASS, ST_ENTER); 2506 setbitn(class, s->s_class); 2507} 2508/* 2509** MAKEMAPENTRY -- create a map entry 2510** 2511** Parameters: 2512** line -- the config file line 2513** 2514** Returns: 2515** A pointer to the map that has been created. 2516** NULL if there was a syntax error. 2517** 2518** Side Effects: 2519** Enters the map into the dictionary. 2520*/ 2521 2522MAP * 2523makemapentry(line) 2524 char *line; 2525{ 2526 register char *p; 2527 char *mapname; 2528 char *classname; 2529 register STAB *s; 2530 STAB *class; 2531 2532 for (p = line; isascii(*p) && isspace(*p); p++) 2533 continue; 2534 if (!(isascii(*p) && isalnum(*p))) 2535 { 2536 syserr("readcf: config K line: no map name"); 2537 return NULL; 2538 } 2539 2540 mapname = p; 2541 while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.') 2542 continue; 2543 if (*p != '\0') 2544 *p++ = '\0'; 2545 while (isascii(*p) && isspace(*p)) 2546 p++; 2547 if (!(isascii(*p) && isalnum(*p))) 2548 { 2549 syserr("readcf: config K line, map %s: no map class", mapname); 2550 return NULL; 2551 } 2552 classname = p; 2553 while (isascii(*++p) && isalnum(*p)) 2554 continue; 2555 if (*p != '\0') 2556 *p++ = '\0'; 2557 while (isascii(*p) && isspace(*p)) 2558 p++; 2559 2560 /* look up the class */ 2561 class = stab(classname, ST_MAPCLASS, ST_FIND); 2562 if (class == NULL) 2563 { 2564 syserr("readcf: map %s: class %s not available", mapname, classname); 2565 return NULL; 2566 } 2567 2568 /* enter the map */ 2569 s = stab(mapname, ST_MAP, ST_ENTER); 2570 s->s_map.map_class = &class->s_mapclass; 2571 s->s_map.map_mname = newstr(mapname); 2572 2573 if (class->s_mapclass.map_parse(&s->s_map, p)) 2574 s->s_map.map_mflags |= MF_VALID; 2575 2576 if (tTd(37, 5)) 2577 { 2578 printf("map %s, class %s, flags %lx, file %s,\n", 2579 s->s_map.map_mname, s->s_map.map_class->map_cname, 2580 s->s_map.map_mflags, 2581 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 2582 printf("\tapp %s, domain %s, rebuild %s\n", 2583 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 2584 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 2585 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 2586 } 2587 2588 return &s->s_map; 2589} 2590/* 2591** STRTORWSET -- convert string to rewriting set number 2592** 2593** Parameters: 2594** p -- the pointer to the string to decode. 2595** endp -- if set, store the trailing delimiter here. 2596** stabmode -- ST_ENTER to create this entry, ST_FIND if 2597** it must already exist. 2598** 2599** Returns: 2600** The appropriate ruleset number. 2601** -1 if it is not valid (error already printed) 2602*/ 2603 2604int 2605strtorwset(p, endp, stabmode) 2606 char *p; 2607 char **endp; 2608 int stabmode; 2609{ 2610 int ruleset; 2611 static int nextruleset = MAXRWSETS; 2612 2613 while (isascii(*p) && isspace(*p)) 2614 p++; 2615 if (!isascii(*p)) 2616 { 2617 syserr("invalid ruleset name: \"%.20s\"", p); 2618 return -1; 2619 } 2620 if (isdigit(*p)) 2621 { 2622 ruleset = strtol(p, endp, 10); 2623 if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 2624 { 2625 syserr("bad ruleset %d (%d max)", 2626 ruleset, MAXRWSETS / 2); 2627 ruleset = -1; 2628 } 2629 } 2630 else 2631 { 2632 STAB *s; 2633 char delim; 2634 char *q; 2635 2636 q = p; 2637 while (*p != '\0' && isascii(*p) && 2638 (isalnum(*p) || *p == '_')) 2639 p++; 2640 if (q == p || !(isascii(*q) && isalpha(*q))) 2641 { 2642 /* no valid characters */ 2643 syserr("invalid ruleset name: \"%.20s\"", q); 2644 return -1; 2645 } 2646 while (isascii(*p) && isspace(*p)) 2647 *p++ = '\0'; 2648 delim = *p; 2649 if (delim != '\0') 2650 *p = '\0'; 2651 s = stab(q, ST_RULESET, stabmode); 2652 if (delim != '\0') 2653 *p = delim; 2654 2655 if (s == NULL) 2656 return -1; 2657 2658 if (stabmode == ST_ENTER && delim == '=') 2659 { 2660 while (isascii(*++p) && isspace(*p)) 2661 continue; 2662 if (!(isascii(*p) && isdigit(*p))) 2663 { 2664 syserr("bad ruleset definition \"%s\" (number required after `=')", q); 2665 ruleset = -1; 2666 } 2667 else 2668 { 2669 ruleset = strtol(p, endp, 10); 2670 if (ruleset >= MAXRWSETS / 2 || ruleset < 0) 2671 { 2672 syserr("bad ruleset number %d in \"%s\" (%d max)", 2673 ruleset, q, MAXRWSETS / 2); 2674 ruleset = -1; 2675 } 2676 } 2677 } 2678 else 2679 { 2680 if (endp != NULL) 2681 *endp = p; 2682 if (s->s_ruleset > 0) 2683 ruleset = s->s_ruleset; 2684 else if ((ruleset = --nextruleset) < MAXRWSETS / 2) 2685 { 2686 syserr("%s: too many named rulesets (%d max)", 2687 q, MAXRWSETS / 2); 2688 ruleset = -1; 2689 } 2690 } 2691 if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset) 2692 { 2693 syserr("%s: ruleset changed value (old %d, new %d)", 2694 q, s->s_ruleset, ruleset); 2695 ruleset = s->s_ruleset; 2696 } 2697 else if (ruleset > 0) 2698 { 2699 s->s_ruleset = ruleset; 2700 } 2701 } 2702 return ruleset; 2703} 2704/* 2705** INITTIMEOUTS -- parse and set timeout values 2706** 2707** Parameters: 2708** val -- a pointer to the values. If NULL, do initial 2709** settings. 2710** 2711** Returns: 2712** none. 2713** 2714** Side Effects: 2715** Initializes the TimeOuts structure 2716*/ 2717 2718#define SECONDS 2719#define MINUTES * 60 2720#define HOUR * 3600 2721 2722void 2723inittimeouts(val) 2724 register char *val; 2725{ 2726 register char *p; 2727 extern time_t convtime __P((char *, char)); 2728 2729 if (tTd(37, 2)) 2730 printf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val); 2731 if (val == NULL) 2732 { 2733 TimeOuts.to_connect = (time_t) 0 SECONDS; 2734 TimeOuts.to_initial = (time_t) 5 MINUTES; 2735 TimeOuts.to_helo = (time_t) 5 MINUTES; 2736 TimeOuts.to_mail = (time_t) 10 MINUTES; 2737 TimeOuts.to_rcpt = (time_t) 1 HOUR; 2738 TimeOuts.to_datainit = (time_t) 5 MINUTES; 2739 TimeOuts.to_datablock = (time_t) 1 HOUR; 2740 TimeOuts.to_datafinal = (time_t) 1 HOUR; 2741 TimeOuts.to_rset = (time_t) 5 MINUTES; 2742 TimeOuts.to_quit = (time_t) 2 MINUTES; 2743 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 2744 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 2745#if IDENTPROTO 2746 TimeOuts.to_ident = (time_t) 30 SECONDS; 2747#else 2748 TimeOuts.to_ident = (time_t) 0 SECONDS; 2749#endif 2750 TimeOuts.to_fileopen = (time_t) 60 SECONDS; 2751 if (tTd(37, 5)) 2752 { 2753 printf("Timeouts:\n"); 2754 printf(" connect = %ld\n", (long)TimeOuts.to_connect); 2755 printf(" initial = %ld\n", (long)TimeOuts.to_initial); 2756 printf(" helo = %ld\n", (long)TimeOuts.to_helo); 2757 printf(" mail = %ld\n", (long)TimeOuts.to_mail); 2758 printf(" rcpt = %ld\n", (long)TimeOuts.to_rcpt); 2759 printf(" datainit = %ld\n", (long)TimeOuts.to_datainit); 2760 printf(" datablock = %ld\n", (long)TimeOuts.to_datablock); 2761 printf(" datafinal = %ld\n", (long)TimeOuts.to_datafinal); 2762 printf(" rset = %ld\n", (long)TimeOuts.to_rset); 2763 printf(" quit = %ld\n", (long)TimeOuts.to_quit); 2764 printf(" nextcommand = %ld\n", (long)TimeOuts.to_nextcommand); 2765 printf(" miscshort = %ld\n", (long)TimeOuts.to_miscshort); 2766 printf(" ident = %ld\n", (long)TimeOuts.to_ident); 2767 printf(" fileopen = %ld\n", (long)TimeOuts.to_fileopen); 2768 } 2769 return; 2770 } 2771 2772 for (;; val = p) 2773 { 2774 while (isascii(*val) && isspace(*val)) 2775 val++; 2776 if (*val == '\0') 2777 break; 2778 for (p = val; *p != '\0' && *p != ','; p++) 2779 continue; 2780 if (*p != '\0') 2781 *p++ = '\0'; 2782 2783 if (isascii(*val) && isdigit(*val)) 2784 { 2785 /* old syntax -- set everything */ 2786 TimeOuts.to_mail = convtime(val, 'm'); 2787 TimeOuts.to_rcpt = TimeOuts.to_mail; 2788 TimeOuts.to_datainit = TimeOuts.to_mail; 2789 TimeOuts.to_datablock = TimeOuts.to_mail; 2790 TimeOuts.to_datafinal = TimeOuts.to_mail; 2791 TimeOuts.to_nextcommand = TimeOuts.to_mail; 2792 continue; 2793 } 2794 else 2795 { 2796 register char *q = strchr(val, ':'); 2797 2798 if (q == NULL && (q = strchr(val, '=')) == NULL) 2799 { 2800 /* syntax error */ 2801 continue; 2802 } 2803 *q++ = '\0'; 2804 settimeout(val, q); 2805 } 2806 } 2807} 2808/* 2809** SETTIMEOUT -- set an individual timeout 2810** 2811** Parameters: 2812** name -- the name of the timeout. 2813** val -- the value of the timeout. 2814** 2815** Returns: 2816** none. 2817*/ 2818 2819void 2820settimeout(name, val) 2821 char *name; 2822 char *val; 2823{ 2824 register char *p; 2825 time_t to; 2826 extern time_t convtime __P((char *, char)); 2827 2828 if (tTd(37, 2)) 2829 printf("settimeout(%s = %s)\n", name, val); 2830 2831 to = convtime(val, 'm'); 2832 p = strchr(name, '.'); 2833 if (p != NULL) 2834 *p++ = '\0'; 2835 2836 if (strcasecmp(name, "initial") == 0) 2837 TimeOuts.to_initial = to; 2838 else if (strcasecmp(name, "mail") == 0) 2839 TimeOuts.to_mail = to; 2840 else if (strcasecmp(name, "rcpt") == 0) 2841 TimeOuts.to_rcpt = to; 2842 else if (strcasecmp(name, "datainit") == 0) 2843 TimeOuts.to_datainit = to; 2844 else if (strcasecmp(name, "datablock") == 0) 2845 TimeOuts.to_datablock = to; 2846 else if (strcasecmp(name, "datafinal") == 0) 2847 TimeOuts.to_datafinal = to; 2848 else if (strcasecmp(name, "command") == 0) 2849 TimeOuts.to_nextcommand = to; 2850 else if (strcasecmp(name, "rset") == 0) 2851 TimeOuts.to_rset = to; 2852 else if (strcasecmp(name, "helo") == 0) 2853 TimeOuts.to_helo = to; 2854 else if (strcasecmp(name, "quit") == 0) 2855 TimeOuts.to_quit = to; 2856 else if (strcasecmp(name, "misc") == 0) 2857 TimeOuts.to_miscshort = to; 2858 else if (strcasecmp(name, "ident") == 0) 2859 TimeOuts.to_ident = to; 2860 else if (strcasecmp(name, "fileopen") == 0) 2861 TimeOuts.to_fileopen = to; 2862 else if (strcasecmp(name, "connect") == 0) 2863 TimeOuts.to_connect = to; 2864 else if (strcasecmp(name, "iconnect") == 0) 2865 TimeOuts.to_iconnect = to; 2866 else if (strcasecmp(name, "queuewarn") == 0) 2867 { 2868 to = convtime(val, 'h'); 2869 if (p == NULL || strcmp(p, "*") == 0) 2870 { 2871 TimeOuts.to_q_warning[TOC_NORMAL] = to; 2872 TimeOuts.to_q_warning[TOC_URGENT] = to; 2873 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 2874 } 2875 else if (strcasecmp(p, "normal") == 0) 2876 TimeOuts.to_q_warning[TOC_NORMAL] = to; 2877 else if (strcasecmp(p, "urgent") == 0) 2878 TimeOuts.to_q_warning[TOC_URGENT] = to; 2879 else if (strcasecmp(p, "non-urgent") == 0) 2880 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 2881 else 2882 syserr("settimeout: invalid queuewarn subtimeout %s", p); 2883 } 2884 else if (strcasecmp(name, "queuereturn") == 0) 2885 { 2886 to = convtime(val, 'd'); 2887 if (p == NULL || strcmp(p, "*") == 0) 2888 { 2889 TimeOuts.to_q_return[TOC_NORMAL] = to; 2890 TimeOuts.to_q_return[TOC_URGENT] = to; 2891 TimeOuts.to_q_return[TOC_NONURGENT] = to; 2892 } 2893 else if (strcasecmp(p, "normal") == 0) 2894 TimeOuts.to_q_return[TOC_NORMAL] = to; 2895 else if (strcasecmp(p, "urgent") == 0) 2896 TimeOuts.to_q_return[TOC_URGENT] = to; 2897 else if (strcasecmp(p, "non-urgent") == 0) 2898 TimeOuts.to_q_return[TOC_NONURGENT] = to; 2899 else 2900 syserr("settimeout: invalid queuereturn subtimeout %s", p); 2901 } 2902 else if (strcasecmp(name, "hoststatus") == 0) 2903 MciInfoTimeout = convtime(val, 'm'); 2904 else 2905 syserr("settimeout: invalid timeout %s", name); 2906} 2907