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