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