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.30 (Berkeley) 12/13/91 (with NEWDB)"; 28 #else 29 #ifdef DBM 30 static char sccsid[] = "@(#)alias.c 5.30 (Berkeley) 12/13/91 (with DBM)"; 31 #else 32 static char sccsid[] = "@(#)alias.c 5.30 (Berkeley) 12/13/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 # ifdef LOCKF 374 # define RDLK_MODE "r+" 375 # else 376 # define RDLK_MODE "r" 377 # endif 378 379 static 380 readaliases(aliasfile, init) 381 char *aliasfile; 382 bool init; 383 { 384 register char *p; 385 char *rhs; 386 bool skipping; 387 int naliases, bytes, longest; 388 FILE *af; 389 void (*oldsigint)(); 390 ADDRESS al, bl; 391 register STAB *s; 392 # ifdef NEWDB 393 DB *dbp; 394 # endif 395 char line[BUFSIZ]; 396 397 if ((af = fopen(aliasfile, RDLK_MODE)) == NULL) 398 { 399 if (tTd(27, 1)) 400 printf("Can't open %s\n", aliasfile); 401 errno = 0; 402 NoAlias++; 403 return; 404 } 405 406 # if defined(DBM) || defined(NEWDB) 407 /* see if someone else is rebuilding the alias file already */ 408 # ifdef LOCKF 409 if (lockf(fileno(af), F_TLOCK, 0) < 0) 410 # else 411 if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 412 # endif 413 { 414 /* yes, they are -- wait until done and then return */ 415 message(Arpa_Info, "Alias file is already being rebuilt"); 416 if (OpMode != MD_INITALIAS) 417 { 418 /* wait for other rebuild to complete */ 419 # ifdef LOCKF 420 (void) lockf(fileno(af), F_LOCK, 0); 421 # else 422 (void) flock(fileno(af), LOCK_EX); 423 # endif 424 } 425 (void) fclose(af); 426 errno = 0; 427 return; 428 } 429 # endif DBM 430 431 /* 432 ** If initializing, create the new DBM files. 433 */ 434 435 if (init) 436 { 437 oldsigint = signal(SIGINT, SIG_IGN); 438 # ifdef NEWDB 439 (void) strcpy(line, aliasfile); 440 (void) strcat(line, ".db"); 441 dbp = dbopen(line, 442 O_RDWR|O_CREAT|O_TRUNC, DBMMODE, DB_HASH, NULL); 443 if (dbp == NULL) 444 { 445 syserr("readaliases: cannot create %s", line); 446 (void) signal(SIGINT, oldsigint); 447 return; 448 } 449 # else 450 # ifdef DBM 451 (void) strcpy(line, aliasfile); 452 (void) strcat(line, ".dir"); 453 if (close(creat(line, DBMMODE)) < 0) 454 { 455 syserr("cannot make %s", line); 456 (void) signal(SIGINT, oldsigint); 457 return; 458 } 459 (void) strcpy(line, aliasfile); 460 (void) strcat(line, ".pag"); 461 if (close(creat(line, DBMMODE)) < 0) 462 { 463 syserr("cannot make %s", line); 464 (void) signal(SIGINT, oldsigint); 465 return; 466 } 467 dbminit(aliasfile); 468 # endif 469 # endif 470 } 471 472 /* 473 ** Read and interpret lines 474 */ 475 476 FileName = aliasfile; 477 LineNumber = 0; 478 naliases = bytes = longest = 0; 479 skipping = FALSE; 480 while (fgets(line, sizeof (line), af) != NULL) 481 { 482 int lhssize, rhssize; 483 484 LineNumber++; 485 p = index(line, '\n'); 486 if (p != NULL) 487 *p = '\0'; 488 switch (line[0]) 489 { 490 case '#': 491 case '\0': 492 skipping = FALSE; 493 continue; 494 495 case ' ': 496 case '\t': 497 if (!skipping) 498 syserr("Non-continuation line starts with space"); 499 skipping = TRUE; 500 continue; 501 } 502 skipping = FALSE; 503 504 /* 505 ** Process the LHS 506 ** Find the final colon, and parse the address. 507 ** It should resolve to a local name -- this will 508 ** be checked later (we want to optionally do 509 ** parsing of the RHS first to maximize error 510 ** detection). 511 */ 512 513 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 514 continue; 515 if (*p++ != ':') 516 { 517 syserr("missing colon"); 518 continue; 519 } 520 if (parseaddr(line, &al, 1, ':') == NULL) 521 { 522 syserr("illegal alias name"); 523 continue; 524 } 525 loweraddr(&al); 526 527 /* 528 ** Process the RHS. 529 ** 'al' is the internal form of the LHS address. 530 ** 'p' points to the text of the RHS. 531 */ 532 533 rhs = p; 534 for (;;) 535 { 536 register char c; 537 538 if (init && CheckAliases) 539 { 540 /* do parsing & compression of addresses */ 541 while (*p != '\0') 542 { 543 extern char *DelimChar; 544 545 while (isspace(*p) || *p == ',') 546 p++; 547 if (*p == '\0') 548 break; 549 if (parseaddr(p, &bl, -1, ',') == NULL) 550 usrerr("%s... bad address", p); 551 p = DelimChar; 552 } 553 } 554 else 555 { 556 p = &p[strlen(p)]; 557 if (p[-1] == '\n') 558 *--p = '\0'; 559 } 560 561 /* see if there should be a continuation line */ 562 c = fgetc(af); 563 if (!feof(af)) 564 (void) ungetc(c, af); 565 if (c != ' ' && c != '\t') 566 break; 567 568 /* read continuation line */ 569 if (fgets(p, sizeof line - (p - line), af) == NULL) 570 break; 571 LineNumber++; 572 } 573 if (al.q_mailer != LocalMailer) 574 { 575 syserr("cannot alias non-local names"); 576 continue; 577 } 578 579 /* 580 ** Insert alias into symbol table or DBM file 581 */ 582 583 lhssize = strlen(al.q_user) + 1; 584 rhssize = strlen(rhs) + 1; 585 586 # if defined(DBM) || defined(NEWDB) 587 if (init) 588 { 589 DBT key, content; 590 591 key.size = lhssize; 592 key.data = al.q_user; 593 content.size = rhssize; 594 content.data = rhs; 595 # ifdef NEWDB 596 if (dbp->put(dbp, &key, &content, 0) != 0) 597 syserr("readaliases: db put (%s)", al.q_user); 598 # else 599 store(key, content); 600 # endif 601 } 602 else 603 # endif DBM 604 { 605 s = stab(al.q_user, ST_ALIAS, ST_ENTER); 606 s->s_alias = newstr(rhs); 607 } 608 609 /* statistics */ 610 naliases++; 611 bytes += lhssize + rhssize; 612 if (rhssize > longest) 613 longest = rhssize; 614 } 615 616 # if defined(DBM) || defined(NEWDB) 617 if (init) 618 { 619 /* add the distinquished alias "@" */ 620 DBT key; 621 622 key.size = 2; 623 key.data = "@"; 624 # ifdef NEWDB 625 if (dbp->sync(dbp) != 0 || 626 dbp->put(dbp, &key, &key, 0) != 0 || 627 dbp->close(dbp) != 0) 628 syserr("readaliases: db close failure"); 629 # else 630 store(key, key); 631 # endif 632 633 /* restore the old signal */ 634 (void) signal(SIGINT, oldsigint); 635 } 636 # endif DBM 637 638 /* closing the alias file drops the lock */ 639 (void) fclose(af); 640 CurEnv->e_to = NULL; 641 FileName = NULL; 642 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 643 naliases, longest, bytes); 644 # ifdef LOG 645 if (LogLevel >= 8) 646 syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 647 naliases, longest, bytes); 648 # endif LOG 649 } 650 /* 651 ** FORWARD -- Try to forward mail 652 ** 653 ** This is similar but not identical to aliasing. 654 ** 655 ** Parameters: 656 ** user -- the name of the user who's mail we would like 657 ** to forward to. It must have been verified -- 658 ** i.e., the q_home field must have been filled 659 ** in. 660 ** sendq -- a pointer to the head of the send queue to 661 ** put this user's aliases in. 662 ** 663 ** Returns: 664 ** none. 665 ** 666 ** Side Effects: 667 ** New names are added to send queues. 668 */ 669 670 forward(user, sendq) 671 ADDRESS *user; 672 ADDRESS **sendq; 673 { 674 char buf[60]; 675 extern bool safefile(); 676 677 if (tTd(27, 1)) 678 printf("forward(%s)\n", user->q_paddr); 679 680 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 681 return; 682 if (user->q_home == NULL) 683 syserr("forward: no home"); 684 685 /* good address -- look for .forward file in home */ 686 define('z', user->q_home, CurEnv); 687 expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 688 if (!safefile(buf, user->q_uid, S_IREAD)) 689 return; 690 691 /* we do have an address to forward to -- do it */ 692 include(buf, "forwarding", user, sendq); 693 } 694