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