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