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