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