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