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