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