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