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