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.11 (Berkeley) 02/18/93 (with NEWDB and NDBM)"; 33 #else 34 static char sccsid[] = "@(#)alias.c 6.11 (Berkeley) 02/18/93 (with NEWDB)"; 35 #endif 36 #else 37 #ifdef NDBM 38 static char sccsid[] = "@(#)alias.c 6.11 (Berkeley) 02/18/93 (with NDBM)"; 39 #else 40 static char sccsid[] = "@(#)alias.c 6.11 (Berkeley) 02/18/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 > 9) 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 DBM database %s.{pag,dir}", 355 aliasfile); 356 NoAlias = TRUE; 357 return; 358 } 359 # endif 360 # endif 361 } 362 } 363 else 364 atcnt = 1; 365 366 /* 367 ** See if the NDBM version of the file is out of date with 368 ** the text version. If so, go into 'init' mode automatically. 369 ** This only happens if our effective userid owns the DBM. 370 ** Note the unpalatable hack to see if the stat succeeded. 371 */ 372 373 modtime = stb.st_mtime; 374 (void) strcpy(buf, aliasfile); 375 # ifdef NEWDB 376 (void) strcat(buf, ".db"); 377 # else 378 (void) strcat(buf, ".pag"); 379 # endif 380 stb.st_ino = 0; 381 if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 382 { 383 errno = 0; 384 if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 385 { 386 init = TRUE; 387 automatic = TRUE; 388 message(Arpa_Info, "rebuilding alias database"); 389 #ifdef LOG 390 if (LogLevel > 14) 391 syslog(LOG_INFO, "rebuilding alias database"); 392 #endif /* LOG */ 393 } 394 else 395 { 396 #ifdef LOG 397 if (LogLevel > 3) 398 syslog(LOG_INFO, "alias database out of date"); 399 #endif /* LOG */ 400 message(Arpa_Info, "Warning: alias database out of date"); 401 } 402 } 403 404 405 /* 406 ** If necessary, load the NDBM file. 407 ** If running without NDBM, load the symbol table. 408 */ 409 410 if (init) 411 { 412 #ifdef LOG 413 if (LogLevel > 7) 414 { 415 extern char *username(); 416 417 syslog(LOG_NOTICE, "alias database %srebuilt by %s", 418 automatic ? "auto" : "", username()); 419 } 420 #endif /* LOG */ 421 readaliases(aliasfile, TRUE, e); 422 } 423 # else /* NDBM */ 424 readaliases(aliasfile, init, e); 425 # endif /* NDBM */ 426 } 427 /* 428 ** READALIASES -- read and process the alias file. 429 ** 430 ** This routine implements the part of initaliases that occurs 431 ** when we are not going to use the DBM stuff. 432 ** 433 ** Parameters: 434 ** aliasfile -- the pathname of the alias file master. 435 ** init -- if set, initialize the NDBM stuff. 436 ** 437 ** Returns: 438 ** none. 439 ** 440 ** Side Effects: 441 ** Reads aliasfile into the symbol table. 442 ** Optionally, builds the .dir & .pag files. 443 */ 444 445 static 446 readaliases(aliasfile, init, e) 447 char *aliasfile; 448 bool init; 449 register ENVELOPE *e; 450 { 451 register char *p; 452 char *rhs; 453 bool skipping; 454 int naliases, bytes, longest; 455 FILE *af; 456 bool makedbmfiles; 457 void (*oldsigint)(); 458 ADDRESS al, bl; 459 register STAB *s; 460 # ifdef NEWDB 461 DB *dbp; 462 # endif 463 # ifdef NDBM 464 DBM *dbmp; 465 # endif 466 # ifdef LOCKF 467 struct flock fld; 468 # endif 469 char line[BUFSIZ]; 470 471 if ((af = fopen(aliasfile, "r+")) == NULL) 472 { 473 if (init) 474 syserr("Can't open %s", aliasfile); 475 else if (tTd(27, 1)) 476 printf("Can't open %s\n", aliasfile); 477 errno = 0; 478 NoAlias++; 479 return; 480 } 481 482 # if defined(NDBM) || defined(NEWDB) 483 /* see if someone else is rebuilding the alias file already */ 484 # ifdef LOCKF 485 fld.l_type = F_WRLCK; 486 fld.l_whence = fld.l_start = fld.l_len = 0; 487 if (fcntl(fileno(af), F_SETLK, &fld) < 0) 488 # else 489 if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 490 # endif 491 { 492 /* yes, they are -- wait until done and then return */ 493 message(Arpa_Info, "Alias file is already being rebuilt"); 494 if (OpMode != MD_INITALIAS) 495 { 496 /* wait for other rebuild to complete */ 497 # ifdef LOCKF 498 (void) fcntl(fileno(af), F_SETLKW, &fld); 499 # else 500 (void) flock(fileno(af), LOCK_EX); 501 # endif 502 } 503 (void) fclose(af); 504 errno = 0; 505 return; 506 } 507 # endif /* NDBM */ 508 509 /* 510 ** If initializing, create the new DBM files. 511 */ 512 513 if (init) 514 { 515 oldsigint = signal(SIGINT, SIG_IGN); 516 # ifdef NEWDB 517 (void) strcpy(line, aliasfile); 518 (void) strcat(line, ".db"); 519 dbp = dbopen(line, 520 O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL); 521 if (dbp == NULL) 522 { 523 syserr("readaliases: cannot create %s", line); 524 (void) signal(SIGINT, oldsigint); 525 return; 526 } 527 # endif 528 # ifdef IF_MAKEDBMFILES 529 # ifdef NEWDB 530 makedbmfiles = access("/var/yp/Makefile", R_OK) == 0; 531 # endif 532 IF_MAKEDBMFILES 533 { 534 dbmp = dbm_open(aliasfile, 535 O_RDWR|O_CREAT|O_TRUNC, DBMMODE); 536 if (dbmp == NULL) 537 { 538 syserr("readaliases: cannot create %s.{dir,pag}", 539 aliasfile); 540 (void) signal(SIGINT, oldsigint); 541 return; 542 } 543 } 544 # endif 545 } 546 547 /* 548 ** Read and interpret lines 549 */ 550 551 FileName = aliasfile; 552 LineNumber = 0; 553 naliases = bytes = longest = 0; 554 skipping = FALSE; 555 while (fgets(line, sizeof (line), af) != NULL) 556 { 557 int lhssize, rhssize; 558 559 LineNumber++; 560 p = strchr(line, '\n'); 561 if (p != NULL) 562 *p = '\0'; 563 switch (line[0]) 564 { 565 case '#': 566 case '\0': 567 skipping = FALSE; 568 continue; 569 570 case ' ': 571 case '\t': 572 if (!skipping) 573 syserr("Non-continuation line starts with space"); 574 skipping = TRUE; 575 continue; 576 } 577 skipping = FALSE; 578 579 /* 580 ** Process the LHS 581 ** Find the colon separator, and parse the address. 582 ** It should resolve to a local name -- this will 583 ** be checked later (we want to optionally do 584 ** parsing of the RHS first to maximize error 585 ** detection). 586 */ 587 588 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 589 continue; 590 if (*p++ != ':') 591 { 592 syserr("missing colon"); 593 continue; 594 } 595 if (parseaddr(line, &al, 1, ':', e) == NULL) 596 { 597 syserr("illegal alias name"); 598 continue; 599 } 600 loweraddr(&al); 601 602 /* 603 ** Process the RHS. 604 ** 'al' is the internal form of the LHS address. 605 ** 'p' points to the text of the RHS. 606 */ 607 608 rhs = p; 609 for (;;) 610 { 611 register char c; 612 613 if (init && CheckAliases) 614 { 615 /* do parsing & compression of addresses */ 616 while (*p != '\0') 617 { 618 extern char *DelimChar; 619 620 while ((isascii(*p) && isspace(*p)) || 621 *p == ',') 622 p++; 623 if (*p == '\0') 624 break; 625 if (parseaddr(p, &bl, -1, ',', e) == NULL) 626 usrerr("%s... bad address", p); 627 p = DelimChar; 628 } 629 } 630 else 631 { 632 p = &p[strlen(p)]; 633 if (p[-1] == '\n') 634 *--p = '\0'; 635 } 636 637 /* see if there should be a continuation line */ 638 c = fgetc(af); 639 if (!feof(af)) 640 (void) ungetc(c, af); 641 if (c != ' ' && c != '\t') 642 break; 643 644 /* read continuation line */ 645 if (fgets(p, sizeof line - (p - line), af) == NULL) 646 break; 647 LineNumber++; 648 649 /* check for line overflow */ 650 if (strchr(p, '\n') == NULL) 651 { 652 usrerr("alias too long"); 653 break; 654 } 655 } 656 if (al.q_mailer != LocalMailer) 657 { 658 syserr("cannot alias non-local names"); 659 continue; 660 } 661 662 /* 663 ** Insert alias into symbol table or DBM file 664 */ 665 666 lhssize = strlen(al.q_user) + 1; 667 if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags)) 668 makelower(al.q_user); 669 rhssize = strlen(rhs) + 1; 670 671 # if defined(NDBM) || defined(NEWDB) 672 if (init) 673 { 674 DBdatum key, content; 675 int putstat; 676 677 key.xx.size = lhssize; 678 key.xx.data = al.q_user; 679 content.xx.size = rhssize; 680 content.xx.data = rhs; 681 # ifdef NEWDB 682 putstat = dbp->put(dbp, &key.dbt, &content.dbt, 683 R_NOOVERWRITE); 684 if (putstat > 0) 685 { 686 usrerr("050 Warning: duplicate alias name %s", 687 al.q_user); 688 putstat = dbp->put(dbp, &key.dbt, 689 &content.dbt, 0); 690 } 691 if (putstat != 0) 692 syserr("readaliases: db put (%s)", al.q_user); 693 # endif 694 # ifdef IF_MAKEDBMFILES 695 IF_MAKEDBMFILES 696 { 697 putstat = dbm_store(dbmp, key.dbm, content.dbm, 698 DBM_INSERT); 699 if (putstat > 0) 700 { 701 usrerr("050 Warning: duplicate alias name %s", 702 al.q_user); 703 putstat = dbm_store(dbmp, key.dbm, 704 content.dbm, DBM_REPLACE); 705 } 706 if (putstat != 0) 707 syserr("readaliases: dbm store (%s)", 708 al.q_user); 709 } 710 # endif 711 if (al.q_paddr != NULL) 712 free(al.q_paddr); 713 if (al.q_host != NULL) 714 free(al.q_host); 715 if (al.q_user != NULL) 716 free(al.q_user); 717 } 718 else 719 # endif /* NDBM */ 720 { 721 s = stab(al.q_user, ST_ALIAS, ST_ENTER); 722 s->s_alias = newstr(rhs); 723 } 724 725 /* statistics */ 726 naliases++; 727 bytes += lhssize + rhssize; 728 if (rhssize > longest) 729 longest = rhssize; 730 } 731 732 # if defined(NDBM) || defined(NEWDB) 733 if (init) 734 { 735 /* add the distinquished alias "@" */ 736 DBdatum key; 737 738 key.xx.size = 2; 739 key.xx.data = "@"; 740 # ifdef NEWDB 741 if (dbp->sync(dbp) != 0 || 742 dbp->put(dbp, &key.dbt, &key.dbt, 0) != 0 || 743 dbp->close(dbp) != 0) 744 syserr("readaliases: db close failure"); 745 # endif 746 # ifdef IF_MAKEDBMFILES 747 IF_MAKEDBMFILES 748 { 749 if (dbm_store(dbmp, key.dbm, key.dbm, DBM_REPLACE) != 0 || 750 dbm_error(dbmp)) 751 syserr("readaliases: dbm close failure"); 752 dbm_close(dbmp); 753 } 754 # endif 755 756 /* restore the old signal */ 757 (void) signal(SIGINT, oldsigint); 758 } 759 # endif /* NDBM */ 760 761 /* closing the alias file drops the lock */ 762 (void) fclose(af); 763 e->e_to = NULL; 764 FileName = NULL; 765 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 766 naliases, longest, bytes); 767 # ifdef LOG 768 if (LogLevel > 7) 769 syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 770 naliases, longest, bytes); 771 # endif /* LOG */ 772 } 773 /* 774 ** FORWARD -- Try to forward mail 775 ** 776 ** This is similar but not identical to aliasing. 777 ** 778 ** Parameters: 779 ** user -- the name of the user who's mail we would like 780 ** to forward to. It must have been verified -- 781 ** i.e., the q_home field must have been filled 782 ** in. 783 ** sendq -- a pointer to the head of the send queue to 784 ** put this user's aliases in. 785 ** 786 ** Returns: 787 ** none. 788 ** 789 ** Side Effects: 790 ** New names are added to send queues. 791 */ 792 793 forward(user, sendq, e) 794 ADDRESS *user; 795 ADDRESS **sendq; 796 register ENVELOPE *e; 797 { 798 char *pp; 799 char *ep; 800 extern bool safefile(); 801 802 if (tTd(27, 1)) 803 printf("forward(%s)\n", user->q_paddr); 804 805 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 806 return; 807 if (user->q_home == NULL) 808 syserr("forward: no home"); 809 810 /* good address -- look for .forward file in home */ 811 define('z', user->q_home, e); 812 define('u', user->q_user, e); 813 define('h', user->q_host, e); 814 if (ForwardPath == NULL) 815 ForwardPath = newstr("\201z/.forward"); 816 817 for (pp = ForwardPath; pp != NULL; pp = ep) 818 { 819 char buf[MAXPATHLEN+1]; 820 821 ep = strchr(pp, ':'); 822 if (ep != NULL) 823 *ep = '\0'; 824 expand(pp, buf, &buf[sizeof buf - 1], e); 825 if (ep != NULL) 826 *ep++ = ':'; 827 if (tTd(27, 3)) 828 printf("forward: trying %s\n", buf); 829 if (include(buf, TRUE, user, sendq, e) == 0) 830 break; 831 } 832 } 833