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.25 (Berkeley) 07/26/91 (with DBM)"; 24 #else 25 #ifdef NEWDB 26 static char sccsid[] = "@(#)alias.c 5.25 (Berkeley) 07/26/91 (with NEWDB)"; 27 #else 28 static char sccsid[] = "@(#)alias.c 5.25 (Berkeley) 07/26/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 = hash_open(buf, O_RDONLY, DBMMODE, 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 = hash_open(buf, O_RDONLY, DBMMODE, NULL); 259 if (AliasDBptr == NULL) 260 { 261 syserr("initaliases: cannot open %s", buf); 262 NoAlias = TRUE; 263 return; 264 } 265 # else 266 # ifdef NDBM 267 dbminit(aliasfile); 268 # endif 269 # endif 270 } 271 } 272 else 273 atcnt = 1; 274 275 /* 276 ** See if the DBM version of the file is out of date with 277 ** the text version. If so, go into 'init' mode automatically. 278 ** This only happens if our effective userid owns the DBM. 279 ** Note the unpalatable hack to see if the stat succeeded. 280 */ 281 282 modtime = stb.st_mtime; 283 (void) strcpy(buf, aliasfile); 284 # ifdef NEWDB 285 (void) strcat(buf, ".db"); 286 # else 287 (void) strcat(buf, ".pag"); 288 # endif 289 stb.st_ino = 0; 290 if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 291 { 292 errno = 0; 293 if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 294 { 295 init = TRUE; 296 automatic = TRUE; 297 message(Arpa_Info, "rebuilding alias database"); 298 #ifdef LOG 299 if (LogLevel >= 7) 300 syslog(LOG_INFO, "rebuilding alias database"); 301 #endif LOG 302 } 303 else 304 { 305 #ifdef LOG 306 if (LogLevel >= 7) 307 syslog(LOG_INFO, "alias database out of date"); 308 #endif LOG 309 message(Arpa_Info, "Warning: alias database out of date"); 310 } 311 } 312 313 314 /* 315 ** If necessary, load the DBM file. 316 ** If running without DBM, load the symbol table. 317 */ 318 319 if (init) 320 { 321 #ifdef LOG 322 if (LogLevel >= 6) 323 { 324 extern char *username(); 325 326 syslog(LOG_NOTICE, "alias database %srebuilt by %s", 327 automatic ? "auto" : "", username()); 328 } 329 #endif LOG 330 readaliases(aliasfile, TRUE); 331 } 332 # else DBM 333 readaliases(aliasfile, init); 334 # endif DBM 335 } 336 /* 337 ** READALIASES -- read and process the alias file. 338 ** 339 ** This routine implements the part of initaliases that occurs 340 ** when we are not going to use the DBM stuff. 341 ** 342 ** Parameters: 343 ** aliasfile -- the pathname of the alias file master. 344 ** init -- if set, initialize the DBM stuff. 345 ** 346 ** Returns: 347 ** none. 348 ** 349 ** Side Effects: 350 ** Reads aliasfile into the symbol table. 351 ** Optionally, builds the .dir & .pag files. 352 */ 353 354 static 355 readaliases(aliasfile, init) 356 char *aliasfile; 357 bool init; 358 { 359 register char *p; 360 char *rhs; 361 bool skipping; 362 int naliases, bytes, longest; 363 FILE *af; 364 void (*oldsigint)(); 365 ADDRESS al, bl; 366 register STAB *s; 367 # ifdef NEWDB 368 DB *dbp; 369 # endif 370 char line[BUFSIZ]; 371 372 if ((af = fopen(aliasfile, "r")) == NULL) 373 { 374 if (tTd(27, 1)) 375 printf("Can't open %s\n", aliasfile); 376 errno = 0; 377 NoAlias++; 378 return; 379 } 380 381 # if defined(DBM) || defined(NEWDB) 382 /* see if someone else is rebuilding the alias file already */ 383 if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 384 { 385 /* yes, they are -- wait until done and then return */ 386 message(Arpa_Info, "Alias file is already being rebuilt"); 387 if (OpMode != MD_INITALIAS) 388 { 389 /* wait for other rebuild to complete */ 390 (void) flock(fileno(af), LOCK_EX); 391 } 392 (void) fclose(af); 393 errno = 0; 394 return; 395 } 396 # endif DBM 397 398 /* 399 ** If initializing, create the new DBM files. 400 */ 401 402 if (init) 403 { 404 oldsigint = signal(SIGINT, SIG_IGN); 405 # ifdef DBM 406 (void) strcpy(line, aliasfile); 407 (void) strcat(line, ".dir"); 408 if (close(creat(line, DBMMODE)) < 0) 409 { 410 syserr("cannot make %s", line); 411 (void) signal(SIGINT, oldsigint); 412 return; 413 } 414 (void) strcpy(line, aliasfile); 415 (void) strcat(line, ".pag"); 416 if (close(creat(line, DBMMODE)) < 0) 417 { 418 syserr("cannot make %s", line); 419 (void) signal(SIGINT, oldsigint); 420 return; 421 } 422 dbminit(aliasfile); 423 # endif 424 # ifdef NEWDB 425 (void) strcpy(line, aliasfile); 426 (void) strcat(line, ".db"); 427 dbp = hash_open(line, O_RDWR|O_CREAT|O_TRUNC, DBMMODE, NULL); 428 if (dbp == NULL) 429 { 430 syserr("readaliases: cannot create %s", line); 431 (void) signal(SIGINT, oldsigint); 432 return; 433 } 434 # endif 435 } 436 437 /* 438 ** Read and interpret lines 439 */ 440 441 FileName = aliasfile; 442 LineNumber = 0; 443 naliases = bytes = longest = 0; 444 skipping = FALSE; 445 while (fgets(line, sizeof (line), af) != NULL) 446 { 447 int lhssize, rhssize; 448 449 LineNumber++; 450 p = index(line, '\n'); 451 if (p != NULL) 452 *p = '\0'; 453 switch (line[0]) 454 { 455 case '#': 456 case '\0': 457 skipping = FALSE; 458 continue; 459 460 case ' ': 461 case '\t': 462 if (!skipping) 463 syserr("Non-continuation line starts with space"); 464 skipping = TRUE; 465 continue; 466 } 467 skipping = FALSE; 468 469 /* 470 ** Process the LHS 471 ** Find the final colon, and parse the address. 472 ** It should resolve to a local name -- this will 473 ** be checked later (we want to optionally do 474 ** parsing of the RHS first to maximize error 475 ** detection). 476 */ 477 478 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 479 continue; 480 if (*p++ != ':') 481 { 482 syserr("missing colon"); 483 continue; 484 } 485 if (parseaddr(line, &al, 1, ':') == NULL) 486 { 487 syserr("illegal alias name"); 488 continue; 489 } 490 loweraddr(&al); 491 492 /* 493 ** Process the RHS. 494 ** 'al' is the internal form of the LHS address. 495 ** 'p' points to the text of the RHS. 496 */ 497 498 rhs = p; 499 for (;;) 500 { 501 register char c; 502 503 if (init && CheckAliases) 504 { 505 /* do parsing & compression of addresses */ 506 while (*p != '\0') 507 { 508 extern char *DelimChar; 509 510 while (isspace(*p) || *p == ',') 511 p++; 512 if (*p == '\0') 513 break; 514 if (parseaddr(p, &bl, -1, ',') == NULL) 515 usrerr("%s... bad address", p); 516 p = DelimChar; 517 } 518 } 519 else 520 { 521 p = &p[strlen(p)]; 522 if (p[-1] == '\n') 523 *--p = '\0'; 524 } 525 526 /* see if there should be a continuation line */ 527 c = fgetc(af); 528 if (!feof(af)) 529 (void) ungetc(c, af); 530 if (c != ' ' && c != '\t') 531 break; 532 533 /* read continuation line */ 534 if (fgets(p, sizeof line - (p - line), af) == NULL) 535 break; 536 LineNumber++; 537 } 538 if (al.q_mailer != LocalMailer) 539 { 540 syserr("cannot alias non-local names"); 541 continue; 542 } 543 544 /* 545 ** Insert alias into symbol table or DBM file 546 */ 547 548 lhssize = strlen(al.q_user) + 1; 549 rhssize = strlen(rhs) + 1; 550 551 # if defined(DBM) || defined(NEWDB) 552 if (init) 553 { 554 DBT key, content; 555 556 key.size = lhssize; 557 key.data = al.q_user; 558 content.size = rhssize; 559 content.data = rhs; 560 # ifdef DBM 561 store(key, content); 562 # else 563 if (dbp->put(dbp, &key, &content, R_PUT) != 0) 564 syserr("readaliases: db put (%s)", al.q_user); 565 # endif 566 } 567 else 568 # endif DBM 569 { 570 s = stab(al.q_user, ST_ALIAS, ST_ENTER); 571 s->s_alias = newstr(rhs); 572 } 573 574 /* statistics */ 575 naliases++; 576 bytes += lhssize + rhssize; 577 if (rhssize > longest) 578 longest = rhssize; 579 } 580 581 # if defined(DBM) || defined(NEWDB) 582 if (init) 583 { 584 /* add the distinquished alias "@" */ 585 DBT key; 586 587 key.size = 2; 588 key.data = "@"; 589 # ifdef NEWDB 590 if (dbp->sync(dbp) != 0 || 591 dbp->put(dbp, &key, &key, R_PUT) != 0 || 592 dbp->close(dbp) != 0) 593 syserr("readaliases: db close failure"); 594 # else 595 store(key, key); 596 # endif 597 598 /* restore the old signal */ 599 (void) signal(SIGINT, oldsigint); 600 } 601 # endif DBM 602 603 /* closing the alias file drops the lock */ 604 (void) fclose(af); 605 CurEnv->e_to = NULL; 606 FileName = NULL; 607 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 608 naliases, longest, bytes); 609 # ifdef LOG 610 if (LogLevel >= 8) 611 syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 612 naliases, longest, bytes); 613 # endif LOG 614 } 615 /* 616 ** FORWARD -- Try to forward mail 617 ** 618 ** This is similar but not identical to aliasing. 619 ** 620 ** Parameters: 621 ** user -- the name of the user who's mail we would like 622 ** to forward to. It must have been verified -- 623 ** i.e., the q_home field must have been filled 624 ** in. 625 ** sendq -- a pointer to the head of the send queue to 626 ** put this user's aliases in. 627 ** 628 ** Returns: 629 ** none. 630 ** 631 ** Side Effects: 632 ** New names are added to send queues. 633 */ 634 635 forward(user, sendq) 636 ADDRESS *user; 637 ADDRESS **sendq; 638 { 639 char buf[60]; 640 extern bool safefile(); 641 642 if (tTd(27, 1)) 643 printf("forward(%s)\n", user->q_paddr); 644 645 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 646 return; 647 if (user->q_home == NULL) 648 syserr("forward: no home"); 649 650 /* good address -- look for .forward file in home */ 651 define('z', user->q_home, CurEnv); 652 expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 653 if (!safefile(buf, user->q_uid, S_IREAD)) 654 return; 655 656 /* we do have an address to forward to -- do it */ 657 include(buf, "forwarding", user, sendq); 658 } 659