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