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