1 # include "sendmail.h" 2 3 SCCSID(@(#)readcf.c 3.32 09/24/82); 4 5 /* 6 ** READCF -- read control file. 7 ** 8 ** This routine reads the control file and builds the internal 9 ** form. 10 ** 11 ** The file is formatted as a sequence of lines, each taken 12 ** atomically. The first character of each line describes how 13 ** the line is to be interpreted. The lines are: 14 ** Dxval Define macro x to have value val. 15 ** Cxword Put word into class x. 16 ** Fxfile [fmt] Read file for lines to put into 17 ** class x. Use scanf string 'fmt' 18 ** or "%s" if not present. Fmt should 19 ** only produce one string-valued result. 20 ** Hname: value Define header with field-name 'name' 21 ** and value as specified; this will be 22 ** macro expanded immediately before 23 ** use. 24 ** Sn Use rewriting set n. 25 ** Rlhs rhs Rewrite addresses that match lhs to 26 ** be rhs. 27 ** Mn p f s r a Define mailer. n - internal name, 28 ** p - pathname, f - flags, s - rewriting 29 ** ruleset for sender, s - rewriting ruleset 30 ** for recipients, a - argument vector. 31 ** Oxvalue Set option x to value. 32 ** Pname=value Set precedence name to value. 33 ** 34 ** Parameters: 35 ** cfname -- control file name. 36 ** safe -- set if this is a system configuration file. 37 ** Non-system configuration files can not do 38 ** certain things (e.g., leave the SUID bit on 39 ** when executing mailers). 40 ** 41 ** Returns: 42 ** none. 43 ** 44 ** Side Effects: 45 ** Builds several internal tables. 46 */ 47 48 readcf(cfname, safe) 49 char *cfname; 50 bool safe; 51 { 52 FILE *cf; 53 char buf[MAXLINE]; 54 register char *p; 55 struct rewrite *rwp = NULL; 56 extern char **prescan(); 57 extern char **copyplist(); 58 int class; 59 int ruleset = 0; 60 char exbuf[MAXLINE]; 61 char *q; 62 63 cf = fopen(cfname, "r"); 64 if (cf == NULL) 65 { 66 syserr("cannot open %s", cfname); 67 exit(EX_OSFILE); 68 } 69 70 LineNumber = 0; 71 while (fgetfolded(buf, sizeof buf, cf) != NULL) 72 { 73 switch (buf[0]) 74 { 75 case '\0': 76 case '#': /* comment */ 77 break; 78 79 case 'R': /* rewriting rule */ 80 for (p = &buf[1]; *p != '\0' && *p != '\t'; p++) 81 continue; 82 83 if (*p == '\0') 84 { 85 syserr("line %d: invalid rewrite line \"%s\"", 86 LineNumber, buf); 87 break; 88 } 89 90 /* allocate space for the rule header */ 91 if (rwp == NULL) 92 { 93 RewriteRules[ruleset] = rwp = 94 (struct rewrite *) xalloc(sizeof *rwp); 95 } 96 else 97 { 98 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 99 rwp = rwp->r_next; 100 } 101 rwp->r_next = NULL; 102 103 /* expand and save the LHS */ 104 *p = '\0'; 105 expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv); 106 rwp->r_lhs = prescan(exbuf, '\t'); 107 if (rwp->r_lhs != NULL) 108 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 109 110 /* expand and save the RHS */ 111 while (*++p == '\t') 112 continue; 113 q = p; 114 while (*p != '\0' && *p != '\t') 115 p++; 116 *p = '\0'; 117 expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv); 118 rwp->r_rhs = prescan(exbuf, '\t'); 119 if (rwp->r_rhs != NULL) 120 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 121 break; 122 123 case 'S': /* select rewriting set */ 124 ruleset = atoi(&buf[1]); 125 if (ruleset >= MAXRWSETS || ruleset < 0) 126 { 127 syserr("readcf: line %d: bad ruleset %d (%d max)", 128 LineNumber, ruleset, MAXRWSETS); 129 ruleset = 0; 130 } 131 rwp = NULL; 132 break; 133 134 case 'D': /* macro definition */ 135 define(buf[1], newstr(&buf[2])); 136 break; 137 138 case 'H': /* required header line */ 139 (void) chompheader(&buf[1], TRUE); 140 break; 141 142 case 'C': /* word class */ 143 case 'F': /* word class from file */ 144 class = buf[1]; 145 if (!isalpha(class)) 146 goto badline; 147 if (isupper(class)) 148 class -= 'A'; 149 else 150 class -= 'a'; 151 152 /* read list of words from argument or file */ 153 if (buf[0] == 'F') 154 { 155 /* read from file */ 156 for (p = &buf[2]; *p != '\0' && !isspace(*p); p++) 157 continue; 158 if (*p == '\0') 159 p = "%s"; 160 else 161 { 162 *p = '\0'; 163 while (isspace(*++p)) 164 continue; 165 } 166 fileclass(class, &buf[2], p); 167 break; 168 } 169 170 /* scan the list of words and set class for all */ 171 for (p = &buf[2]; *p != '\0'; ) 172 { 173 register char *wd; 174 char delim; 175 register STAB *s; 176 177 while (*p != '\0' && isspace(*p)) 178 p++; 179 wd = p; 180 while (*p != '\0' && !isspace(*p)) 181 p++; 182 delim = *p; 183 *p = '\0'; 184 if (wd[0] != '\0') 185 { 186 s = stab(wd, ST_CLASS, ST_ENTER); 187 s->s_class |= 1L << class; 188 } 189 *p = delim; 190 } 191 break; 192 193 case 'M': /* define mailer */ 194 makemailer(&buf[1], safe); 195 break; 196 197 case 'O': /* set option */ 198 if (buf[2] == '\0') 199 Option[buf[1]] = ""; 200 else 201 Option[buf[1]] = newstr(&buf[2]); 202 break; 203 204 case 'P': /* set precedence */ 205 if (NumPriorities >= MAXPRIORITIES) 206 { 207 syserr("readcf: line %d: too many P lines, %d max", 208 LineNumber, MAXPRIORITIES); 209 break; 210 } 211 for (p = &buf[1]; *p != '\0' && *p != '='; p++) 212 continue; 213 if (*p == '\0') 214 goto badline; 215 *p = '\0'; 216 Priorities[NumPriorities].pri_name = newstr(&buf[1]); 217 Priorities[NumPriorities].pri_val = atoi(++p); 218 NumPriorities++; 219 break; 220 221 default: 222 badline: 223 syserr("readcf: line %d: unknown control line \"%s\"", 224 LineNumber, buf); 225 } 226 } 227 } 228 /* 229 ** FILECLASS -- read members of a class from a file 230 ** 231 ** Parameters: 232 ** class -- class to define. 233 ** filename -- name of file to read. 234 ** fmt -- scanf string to use for match. 235 ** 236 ** Returns: 237 ** none 238 ** 239 ** Side Effects: 240 ** 241 ** puts all lines in filename that match a scanf into 242 ** the named class. 243 */ 244 245 fileclass(class, filename, fmt) 246 int class; 247 char *filename; 248 char *fmt; 249 { 250 register FILE *f; 251 char buf[MAXLINE]; 252 253 f = fopen(filename, "r"); 254 if (f == NULL) 255 { 256 syserr("cannot open %s", filename); 257 return; 258 } 259 260 while (fgets(buf, sizeof buf, f) != NULL) 261 { 262 register STAB *s; 263 char wordbuf[MAXNAME+1]; 264 265 if (sscanf(buf, fmt, wordbuf) != 1) 266 continue; 267 s = stab(wordbuf, ST_CLASS, ST_ENTER); 268 s->s_class |= 1L << class; 269 } 270 271 (void) fclose(f); 272 } 273 /* 274 ** MAKEMAILER -- define a new mailer. 275 ** 276 ** Parameters: 277 ** line -- description of mailer. This is in tokens 278 ** separated by white space. The fields are: 279 ** * the name of the mailer, as refered to 280 ** in the rewriting rules. 281 ** * the pathname of the program to fork to 282 ** execute it. 283 ** * the options needed by this program. 284 ** * the macro string needed to translate 285 ** a local "from" name to one that can be 286 ** returned to this machine. 287 ** * the argument vector (a series of parameters). 288 ** safe -- set if this is a safe configuration file. 289 ** 290 ** Returns: 291 ** none. 292 ** 293 ** Side Effects: 294 ** enters the mailer into the mailer table. 295 */ 296 297 # define SETWORD \ 298 { \ 299 while (*p != '\0' && isspace(*p)) \ 300 p++; \ 301 q = p; \ 302 while (*p != '\0' && !isspace(*p)) \ 303 p++; \ 304 if (*p != '\0') \ 305 *p++ = '\0'; \ 306 } 307 308 makemailer(line, safe) 309 char *line; 310 bool safe; 311 { 312 register char *p; 313 register char *q; 314 register struct mailer *m; 315 register STAB *s; 316 int i; 317 char *mname; 318 char *mpath; 319 u_long mopts; 320 short mrset, msset; 321 char *margv[MAXPV + 1]; 322 extern u_long mfencode(); 323 extern int NextMailer; 324 325 if (NextMailer >= MAXMAILERS) 326 { 327 syserr("readcf: line %d: too many mailers defined (%d max)", 328 LineNumber, MAXMAILERS); 329 return; 330 } 331 332 /* collect initial information */ 333 p = line; 334 SETWORD; 335 mname = q; 336 SETWORD; 337 mpath = q; 338 SETWORD; 339 mopts = mfencode(q); 340 if (!safe) 341 mopts &= ~M_RESTR; 342 SETWORD; 343 msset = atoi(q); 344 SETWORD; 345 mrset = atoi(q); 346 347 if (*p == '\0') 348 { 349 syserr("readcf: line %d: invalid M line in configuration file", 350 LineNumber); 351 return; 352 } 353 if (msset >= MAXRWSETS || mrset >= MAXRWSETS) 354 { 355 syserr("readcf: line %d: invalid rewrite set, %d max", 356 LineNumber, MAXRWSETS); 357 return; 358 } 359 360 /* allocate a mailer */ 361 m = (struct mailer *) xalloc(sizeof *m); 362 m->m_name = newstr(mname); 363 m->m_mailer = newstr(mpath); 364 m->m_flags = mopts; 365 m->m_r_rwset = mrset; 366 m->m_s_rwset = msset; 367 m->m_badstat = EX_UNAVAILABLE; 368 m->m_mno = NextMailer; 369 Mailer[NextMailer++] = m; 370 371 /* collect the argument vector */ 372 for (i = 0; i < MAXPV - 1 && *p != '\0'; i++) 373 { 374 SETWORD; 375 margv[i] = newstr(q); 376 } 377 margv[i++] = NULL; 378 379 /* save the argv */ 380 m->m_argv = (char **) xalloc(sizeof margv[0] * i); 381 bmove((char *) margv, (char *) m->m_argv, sizeof margv[0] * i); 382 s = stab(m->m_name, ST_MAILER, ST_ENTER); 383 s->s_mailer = m; 384 } 385 /* 386 ** PRINTRULES -- print rewrite rules (for debugging) 387 ** 388 ** Parameters: 389 ** none. 390 ** 391 ** Returns: 392 ** none. 393 ** 394 ** Side Effects: 395 ** prints rewrite rules. 396 */ 397 398 # ifdef DEBUG 399 400 printrules() 401 { 402 register struct rewrite *rwp; 403 register int ruleset; 404 405 for (ruleset = 0; ruleset < 10; ruleset++) 406 { 407 if (RewriteRules[ruleset] == NULL) 408 continue; 409 printf("\n----Rule Set %d:", ruleset); 410 411 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 412 { 413 printf("\nLHS:"); 414 printav(rwp->r_lhs); 415 printf("RHS:"); 416 printav(rwp->r_rhs); 417 } 418 } 419 } 420 421 # endif DEBUG 422 /* 423 ** MFENCODE -- crack mailer options 424 ** 425 ** These options modify the functioning of the mailer 426 ** from the configuration table. 427 ** 428 ** Parameters: 429 ** p -- pointer to vector of options. 430 ** 431 ** Returns: 432 ** option list in binary. 433 ** 434 ** Side Effects: 435 ** none. 436 */ 437 438 struct optlist 439 { 440 char opt_name; /* external name of option */ 441 u_long opt_value; /* internal name of option */ 442 }; 443 struct optlist OptList[] = 444 { 445 'f', M_FOPT, 446 'r', M_ROPT, 447 'q', M_QUIET, 448 'S', M_RESTR, 449 'n', M_NHDR, 450 'l', M_LOCAL, 451 's', M_STRIPQ, 452 'm', M_MUSER, 453 'F', M_NEEDFROM, 454 'D', M_NEEDDATE, 455 'M', M_MSGID, 456 'u', M_USR_UPPER, 457 'h', M_HST_UPPER, 458 'x', M_FULLNAME, 459 'A', M_ARPAFMT, 460 'U', M_UGLYUUCP, 461 'e', M_EXPENSIVE, 462 'X', M_FULLSMTP, 463 'C', M_CANONICAL, 464 '\0', 0 465 }; 466 467 u_long 468 mfencode(p) 469 register char *p; 470 { 471 register struct optlist *o; 472 register u_long opts = 0; 473 474 while (*p != '\0') 475 { 476 for (o = OptList; o->opt_name != '\0' && o->opt_name != *p; o++) 477 continue; 478 if (o->opt_name == '\0') 479 syserr("bad mailer option %c", *p); 480 opts |= o->opt_value; 481 p++; 482 } 483 return (opts); 484 } 485 /* 486 ** MFDECODE -- decode mailer flags into external form. 487 ** 488 ** Parameters: 489 ** flags -- value of flags to decode. 490 ** f -- file to write them onto. 491 ** 492 ** Returns: 493 ** none. 494 ** 495 ** Side Effects: 496 ** none. 497 */ 498 499 mfdecode(flags, f) 500 u_long flags; 501 FILE *f; 502 { 503 register struct optlist *o; 504 505 putc('?', f); 506 for (o = OptList; o->opt_name != '\0'; o++) 507 { 508 if ((o->opt_value & flags) == o->opt_value) 509 { 510 flags &= ~o->opt_value; 511 putc(o->opt_name, f); 512 } 513 } 514 putc('?', f); 515 } 516