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