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