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