1 # include <pwd.h> 2 # include <sys/types.h> 3 # include <sys/stat.h> 4 # include "sendmail.h" 5 6 # ifdef DBM 7 SCCSID(@(#)alias.c 3.33.1.1 06/06/82 (with DBM)); 8 # else DBM 9 SCCSID(@(#)alias.c 3.33.1.1 06/06/82 (without DBM)); 10 # endif DBM 11 12 /* 13 ** ALIAS -- Compute aliases. 14 ** 15 ** Scans the file /usr/lib/aliases for a set of aliases. 16 ** If found, it arranges to deliver to them. Uses libdbm 17 ** database if -DDBM. 18 ** 19 ** Parameters: 20 ** a -- address to alias. 21 ** sendq -- a pointer to the head of the send queue 22 ** to put the aliases in. 23 ** 24 ** Returns: 25 ** none 26 ** 27 ** Side Effects: 28 ** Aliases found are expanded. 29 ** 30 ** Files: 31 ** /usr/lib/aliases -- the mail aliases. The format is 32 ** a series of lines of the form: 33 ** alias:name1,name2,name3,... 34 ** where 'alias' expands to all of 35 ** 'name[i]'. Continuations begin with 36 ** space or tab. 37 ** /usr/lib/aliases.pag, /usr/lib/aliases.dir: libdbm version 38 ** of alias file. Keys are aliases, datums 39 ** (data?) are name1,name2, ... 40 ** 41 ** Notes: 42 ** If NoAlias (the "-n" flag) is set, no aliasing is 43 ** done. 44 ** 45 ** Deficiencies: 46 ** It should complain about names that are aliased to 47 ** nothing. 48 ** It is unsophisticated about line overflows. 49 */ 50 51 52 #ifdef DBM 53 typedef struct 54 { 55 char *dptr; 56 int dsize; 57 } DATUM; 58 extern DATUM fetch(); 59 #endif DBM 60 61 alias(a, sendq) 62 register ADDRESS *a; 63 ADDRESS **sendq; 64 { 65 register char *p; 66 extern char *aliaslookup(); 67 68 if (NoAlias) 69 return; 70 # ifdef DEBUG 71 if (Debug) 72 printf("alias(%s)\n", a->q_paddr); 73 # endif 74 75 /* don't realias already aliased names */ 76 if (bitset(QDONTSEND, a->q_flags)) 77 return; 78 79 CurEnv->e_to = a->q_paddr; 80 81 /* 82 ** Look up this name 83 */ 84 85 p = aliaslookup(a->q_user); 86 if (p == NULL) 87 return; 88 89 /* 90 ** Match on Alias. 91 ** Deliver to the target list. 92 */ 93 94 # ifdef DEBUG 95 if (Debug) 96 printf("%s (%s, %s) aliased to %s\n", 97 a->q_paddr, a->q_host, a->q_user, p); 98 # endif 99 if (Verbose) 100 message(Arpa_Info, "aliased to %s", p); 101 AliasLevel++; 102 sendto(p, 1, a, sendq); 103 AliasLevel--; 104 } 105 /* 106 ** ALIASLOOKUP -- look up a name in the alias file. 107 ** 108 ** Parameters: 109 ** name -- the name to look up. 110 ** 111 ** Returns: 112 ** the value of name. 113 ** NULL if unknown. 114 ** 115 ** Side Effects: 116 ** none. 117 ** 118 ** Warnings: 119 ** The return value will be trashed across calls. 120 */ 121 122 char * 123 aliaslookup(name) 124 char *name; 125 { 126 # ifdef DBM 127 DATUM rhs, lhs; 128 129 /* create a key for fetch */ 130 lhs.dptr = name; 131 lhs.dsize = strlen(name) + 1; 132 rhs = fetch(lhs); 133 return (rhs.dptr); 134 # else DBM 135 register STAB *s; 136 137 s = stab(name, ST_ALIAS, ST_FIND); 138 if (s == NULL) 139 return (NULL); 140 return (s->s_alias); 141 # endif DBM 142 } 143 /* 144 ** INITALIASES -- initialize for aliasing 145 ** 146 ** Very different depending on whether we are running DBM or not. 147 ** 148 ** Parameters: 149 ** aliasfile -- location of aliases. 150 ** init -- if set and if DBM, initialize the DBM files. 151 ** 152 ** Returns: 153 ** none. 154 ** 155 ** Side Effects: 156 ** initializes aliases: 157 ** if DBM: opens the database. 158 ** if ~DBM: reads the aliases into the symbol table. 159 */ 160 161 # define DBMMODE 0666 162 163 initaliases(aliasfile, init) 164 char *aliasfile; 165 bool init; 166 { 167 char buf[MAXNAME]; 168 struct stat stb; 169 time_t modtime; 170 171 /* 172 ** See if the DBM version of the file is out of date with 173 ** the text version. If so, go into 'init' mode automatically. 174 ** This only happens if our effective userid owns the DBM 175 ** version or if the mode of the database is 666 -- this 176 ** is an attempt to avoid protection problems. Note the 177 ** unpalatable hack to see if the stat succeeded. 178 */ 179 180 if (stat(aliasfile, &stb) < 0) 181 return; 182 # ifdef DBM 183 modtime = stb.st_mtime; 184 (void) strcpy(buf, aliasfile); 185 (void) strcat(buf, ".pag"); 186 stb.st_ino = 0; 187 if ((stat(buf, &stb) < 0 || stb.st_mtime < modtime) && !init) 188 { 189 if (stb.st_ino != 0 && 190 ((stb.st_mode & 0666) == 0666 || stb.st_uid == geteuid())) 191 { 192 init = TRUE; 193 if (Verbose) 194 message(Arpa_Info, "rebuilding alias database"); 195 } 196 else 197 { 198 message(Arpa_Info, "Warning: alias database out of date"); 199 } 200 } 201 # endif DBM 202 203 /* 204 ** If initializing, create the new files. 205 ** We should lock the alias file here to prevent other 206 ** instantiations of sendmail from reading an incomplete 207 ** file -- or worse yet, doing a concurrent initialize. 208 */ 209 210 # ifdef DBM 211 if (init) 212 { 213 (void) strcpy(buf, aliasfile); 214 (void) strcat(buf, ".dir"); 215 if (close(creat(buf, DBMMODE)) < 0) 216 { 217 syserr("cannot make %s", buf); 218 return; 219 } 220 (void) strcpy(buf, aliasfile); 221 (void) strcat(buf, ".pag"); 222 if (close(creat(buf, DBMMODE)) < 0) 223 { 224 syserr("cannot make %s", buf); 225 return; 226 } 227 } 228 229 /* 230 ** Open and, if necessary, load the DBM file. 231 ** If running without DBM, load the symbol table. 232 */ 233 234 dbminit(aliasfile); 235 if (init) 236 readaliases(aliasfile, TRUE); 237 # else DBM 238 readaliases(aliasfile, init); 239 # endif DBM 240 } 241 /* 242 ** READALIASES -- read and process the alias file. 243 ** 244 ** This routine implements the part of initaliases that occurs 245 ** when we are not going to use the DBM stuff. 246 ** 247 ** Parameters: 248 ** aliasfile -- the pathname of the alias file master. 249 ** init -- if set, initialize the DBM stuff. 250 ** 251 ** Returns: 252 ** none. 253 ** 254 ** Side Effects: 255 ** Reads aliasfile into the symbol table. 256 ** Optionally, builds the .dir & .pag files. 257 */ 258 259 static 260 readaliases(aliasfile, init) 261 char *aliasfile; 262 bool init; 263 { 264 char line[BUFSIZ]; 265 register char *p; 266 char *p2; 267 char *rhs; 268 bool skipping; 269 ADDRESS al, bl; 270 FILE *af; 271 int lineno; 272 register STAB *s; 273 int naliases, bytes, longest; 274 275 if ((af = fopen(aliasfile, "r")) == NULL) 276 { 277 # ifdef DEBUG 278 if (Debug) 279 printf("Can't open %s\n", aliasfile); 280 # endif 281 errno = 0; 282 NoAlias++; 283 return; 284 } 285 286 /* 287 ** Read and interpret lines 288 */ 289 290 lineno = 0; 291 naliases = bytes = longest = 0; 292 skipping = FALSE; 293 while (fgets(line, sizeof (line), af) != NULL) 294 { 295 int lhssize, rhssize; 296 297 lineno++; 298 switch (line[0]) 299 { 300 case '#': 301 case '\n': 302 case '\0': 303 skipping = FALSE; 304 continue; 305 306 case ' ': 307 case '\t': 308 if (!skipping) 309 syserr("aliases: %d: Non-continuation line starts with space", lineno); 310 skipping = TRUE; 311 continue; 312 } 313 skipping = FALSE; 314 315 /* 316 ** Process the LHS 317 ** Find the final colon, and parse the address. 318 ** It should resolve to a local name -- this will 319 ** be checked later (we want to optionally do 320 ** parsing of the RHS first to maximize error 321 ** detection). 322 */ 323 324 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 325 continue; 326 if (*p == '\0' || *p == '\n') 327 { 328 syntaxerr: 329 syserr("%s, line %d: syntax error", aliasfile, lineno); 330 continue; 331 } 332 *p++ = '\0'; 333 if (parse(line, &al, 1) == NULL) 334 { 335 *--p = ':'; 336 goto syntaxerr; 337 } 338 339 /* 340 ** Process the RHS. 341 ** 'al' is the internal form of the LHS address. 342 ** 'p' points to the text of the RHS. 343 ** 'p' may begin with a colon (i.e., the 344 ** separator was "::") which will use the 345 ** first address as the person to send 346 ** errors to -- i.e., designates the 347 ** list maintainer. 348 */ 349 350 if (*p == ':') 351 { 352 ADDRESS *maint; 353 354 while (isspace(*++p)) 355 continue; 356 rhs = p; 357 while (*p != '\0' && *p != ',') 358 p++; 359 if (*p != ',') 360 goto syntaxerr; 361 *p++ = '\0'; 362 maint = parse(p, (ADDRESS *) NULL, 1); 363 if (maint == NULL) 364 syserr("Illegal list maintainer for list %s", al.q_user); 365 else if (CurEnv->e_returnto == &CurEnv->e_from) 366 { 367 CurEnv->e_returnto = maint; 368 MailBack++; 369 } 370 } 371 372 rhs = p; 373 for (;;) 374 { 375 register char c; 376 377 if (init) 378 { 379 /* do parsing & compression of addresses */ 380 c = *p; 381 while (c != '\0') 382 { 383 p2 = p; 384 while (*p != '\n' && *p != ',' && *p != '\0') 385 p++; 386 c = *p; 387 *p++ = '\0'; 388 if (c == '\n') 389 c = '\0'; 390 if (*p2 == '\0') 391 { 392 p[-1] = c; 393 continue; 394 } 395 (void) parse(p2, &bl, -1); 396 p[-1] = c; 397 while (isspace(*p)) 398 p++; 399 } 400 } 401 else 402 p = &p[strlen(p)]; 403 404 /* see if there should be a continuation line */ 405 c = fgetc(af); 406 if (!feof(af)) 407 (void) ungetc(c, af); 408 if (c != ' ' && c != '\t') 409 break; 410 411 /* read continuation line */ 412 p--; 413 if (fgets(p, sizeof line - (p - line), af) == NULL) 414 break; 415 lineno++; 416 } 417 if (al.q_mailer != LocalMailer) 418 { 419 syserr("aliases: %d: cannot alias non-local names", lineno); 420 continue; 421 } 422 423 /* 424 ** Insert alias into symbol table or DBM file 425 */ 426 427 lhssize = strlen(al.q_user) + 1; 428 rhssize = strlen(rhs) + 1; 429 430 # ifdef DBM 431 if (init) 432 { 433 DATUM key, content; 434 435 key.dsize = lhssize; 436 key.dptr = al.q_user; 437 content.dsize = rhssize; 438 content.dptr = rhs; 439 store(key, content); 440 } 441 else 442 # endif DBM 443 { 444 s = stab(al.q_user, ST_ALIAS, ST_ENTER); 445 s->s_alias = newstr(rhs); 446 } 447 448 /* statistics */ 449 naliases++; 450 bytes += lhssize + rhssize; 451 if (rhssize > longest) 452 longest = rhssize; 453 } 454 (void) fclose(af); 455 CurEnv->e_to = NULL; 456 if (Verbose) 457 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 458 naliases, longest, bytes); 459 } 460 /* 461 ** FORWARD -- Try to forward mail 462 ** 463 ** This is similar but not identical to aliasing. 464 ** 465 ** Parameters: 466 ** user -- the name of the user who's mail we would like 467 ** to forward to. It must have been verified -- 468 ** i.e., the q_home field must have been filled 469 ** in. 470 ** sendq -- a pointer to the head of the send queue to 471 ** put this user's aliases in. 472 ** 473 ** Returns: 474 ** none. 475 ** 476 ** Side Effects: 477 ** New names are added to send queues. 478 */ 479 480 forward(user, sendq) 481 ADDRESS *user; 482 ADDRESS **sendq; 483 { 484 char buf[60]; 485 extern bool safefile(); 486 487 # ifdef DEBUG 488 if (Debug) 489 printf("forward(%s)\n", user->q_paddr); 490 # endif DEBUG 491 492 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 493 return; 494 # ifdef DEBUG 495 if (user->q_home == NULL) 496 syserr("forward: no home"); 497 # endif DEBUG 498 499 /* good address -- look for .forward file in home */ 500 define('z', user->q_home); 501 expand("$z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 502 if (!safefile(buf, user->q_uid, S_IREAD)) 503 return; 504 505 /* we do have an address to forward to -- do it */ 506 include(buf, "forwarding", user, sendq); 507 } 508 /* 509 ** HOSTALIAS -- alias a host name 510 ** 511 ** Given a host name, look it up in the host alias table 512 ** and return it's value. If nothing, return NULL. 513 ** 514 ** Parameters: 515 ** a -- address to alias. 516 ** 517 ** Returns: 518 ** text result of aliasing. 519 ** NULL if none. 520 ** 521 ** Side Effects: 522 ** none. 523 */ 524 525 char * 526 hostalias(a) 527 register ADDRESS *a; 528 { 529 char buf[MAXNAME+2]; 530 register char *p; 531 532 (void) strcpy(buf, "/"); 533 (void) strcat(buf, a->q_host); 534 makelower(buf); 535 536 p = aliaslookup(buf); 537 if (p == NULL) 538 return (NULL); 539 (void) sprintf(buf, p, a->q_user); 540 return (newstr(buf)); 541 } 542