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