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