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: #if !defined(lint) && !defined(NOSCCS) 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 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 0644 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, longest; 315: long bytes; 316: FILE *af; 317: int (*oldsigint)(); 318: ADDRESS al, bl; 319: register STAB *s; 320: char line[BUFSIZ]; 321: 322: if ((af = fopen(aliasfile, "r")) == NULL) 323: { 324: # ifdef DEBUG 325: if (tTd(27, 1)) 326: printf("Can't open %s\n", aliasfile); 327: # endif 328: errno = 0; 329: NoAlias++; 330: return; 331: } 332: 333: # ifdef DBM 334: # ifdef FLOCK 335: /* see if someone else is rebuilding the alias file already */ 336: if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 337: { 338: /* yes, they are -- wait until done and then return */ 339: message(Arpa_Info, "Alias file is already being rebuilt"); 340: if (OpMode != MD_INITALIAS) 341: { 342: /* wait for other rebuild to complete */ 343: (void) flock(fileno(af), LOCK_EX); 344: } 345: (void) fclose(af); 346: errno = 0; 347: return; 348: } 349: # endif FLOCK 350: # endif DBM 351: 352: /* 353: ** If initializing, create the new DBM files. 354: */ 355: 356: if (init) 357: { 358: oldsigint = signal(SIGINT, SIG_IGN); 359: (void) strcpy(line, aliasfile); 360: (void) strcat(line, ".dir"); 361: if (close(creat(line, DBMMODE)) < 0) 362: { 363: syserr("cannot make %s", line); 364: (void) signal(SIGINT, oldsigint); 365: return; 366: } 367: (void) strcpy(line, aliasfile); 368: (void) strcat(line, ".pag"); 369: if (close(creat(line, DBMMODE)) < 0) 370: { 371: syserr("cannot make %s", line); 372: (void) signal(SIGINT, oldsigint); 373: return; 374: } 375: dbminit(aliasfile); 376: } 377: 378: /* 379: ** Read and interpret lines 380: */ 381: 382: FileName = aliasfile; 383: LineNumber = 0; 384: naliases = bytes = longest = 0; 385: skipping = FALSE; 386: while (fgets(line, sizeof (line), af) != NULL) 387: { 388: int lhssize, rhssize; 389: 390: LineNumber++; 391: p = index(line, '\n'); 392: if (p != NULL) 393: *p = '\0'; 394: switch (line[0]) 395: { 396: case '#': 397: case '\0': 398: skipping = FALSE; 399: continue; 400: 401: case ' ': 402: case '\t': 403: if (!skipping) 404: syserr("Non-continuation line starts with space"); 405: skipping = TRUE; 406: continue; 407: } 408: skipping = FALSE; 409: 410: /* 411: ** Process the LHS 412: ** Find the final colon, and parse the address. 413: ** It should resolve to a local name -- this will 414: ** be checked later (we want to optionally do 415: ** parsing of the RHS first to maximize error 416: ** detection). 417: */ 418: 419: for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 420: continue; 421: if (*p++ != ':') 422: { 423: syserr("missing colon"); 424: continue; 425: } 426: if (parseaddr(line, &al, 1, ':') == NULL) 427: { 428: syserr("illegal alias name"); 429: continue; 430: } 431: loweraddr(&al); 432: 433: /* 434: ** Process the RHS. 435: ** 'al' is the internal form of the LHS address. 436: ** 'p' points to the text of the RHS. 437: */ 438: 439: rhs = p; 440: for (;;) 441: { 442: register char c; 443: 444: if (init && CheckAliases) 445: { 446: /* do parsing & compression of addresses */ 447: while (*p != '\0') 448: { 449: extern char *DelimChar; 450: 451: while (isspace(*p) || *p == ',') 452: p++; 453: if (*p == '\0') 454: break; 455: if (parseaddr(p, &bl, -1, ',') == NULL) 456: usrerr("%s... bad address", p); 457: if (bl.q_host && bl.q_host[0]) 458: free(bl.q_host); 459: p = DelimChar; 460: } 461: } 462: else 463: { 464: p = &p[strlen(p)]; 465: if (p[-1] == '\n') 466: *--p = '\0'; 467: } 468: 469: /* see if there should be a continuation line */ 470: c = fgetc(af); 471: if (!feof(af)) 472: (void) ungetc(c, af); 473: if (c != ' ' && c != '\t') 474: break; 475: 476: /* read continuation line */ 477: if (fgets(p, sizeof line - (p - line), af) == NULL) 478: break; 479: LineNumber++; 480: } 481: if (al.q_mailer != LocalMailer) 482: { 483: syserr("cannot alias non-local names"); 484: continue; 485: } 486: 487: /* 488: ** Insert alias into symbol table or DBM file 489: */ 490: 491: lhssize = strlen(al.q_user) + 1; 492: rhssize = strlen(rhs) + 1; 493: 494: # ifdef DBM 495: if (init) 496: { 497: DATUM key, content; 498: 499: key.dsize = lhssize; 500: key.dptr = al.q_user; 501: content.dsize = rhssize; 502: content.dptr = rhs; 503: store(key, content); 504: if (al.q_paddr) 505: free(al.q_paddr); 506: if (al.q_host) 507: free(al.q_host); 508: if (al.q_user) 509: free(al.q_user); 510: } 511: else 512: # endif DBM 513: { 514: s = stab(al.q_user, ST_ALIAS, ST_ENTER); 515: s->s_alias = newstr(rhs); 516: } 517: 518: /* statistics */ 519: naliases++; 520: bytes += lhssize + rhssize; 521: if (rhssize > longest) 522: longest = rhssize; 523: } 524: 525: # ifdef DBM 526: if (init) 527: { 528: /* add the distinquished alias "@" */ 529: DATUM key; 530: 531: key.dsize = 2; 532: key.dptr = "@"; 533: store(key, key); 534: 535: /* restore the old signal */ 536: (void) signal(SIGINT, oldsigint); 537: } 538: # endif DBM 539: 540: /* closing the alias file drops the lock */ 541: (void) fclose(af); 542: CurEnv->e_to = NULL; 543: FileName = NULL; 544: message(Arpa_Info, "%d aliases, longest %d bytes, %ld bytes total", 545: naliases, longest, bytes); 546: # ifdef LOG 547: if (LogLevel >= 8) 548: syslog(LOG_INFO, "%d aliases, longest %d bytes, %ld bytes total", 549: naliases, longest, bytes); 550: # endif LOG 551: } 552: /* 553: ** FORWARD -- Try to forward mail 554: ** 555: ** This is similar but not identical to aliasing. 556: ** 557: ** Parameters: 558: ** user -- the name of the user who's mail we would like 559: ** to forward to. It must have been verified -- 560: ** i.e., the q_home field must have been filled 561: ** in. 562: ** sendq -- a pointer to the head of the send queue to 563: ** put this user's aliases in. 564: ** 565: ** Returns: 566: ** none. 567: ** 568: ** Side Effects: 569: ** New names are added to send queues. 570: */ 571: 572: forward(user, sendq) 573: ADDRESS *user; 574: ADDRESS **sendq; 575: { 576: char buf[60]; 577: extern bool safefile(); 578: 579: # ifdef DEBUG 580: if (tTd(27, 1)) 581: printf("forward(%s)\n", user->q_paddr); 582: # endif DEBUG 583: 584: if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 585: return; 586: # ifdef DEBUG 587: if (user->q_home == NULL) 588: syserr("forward: no home"); 589: # endif DEBUG 590: 591: /* good address -- look for .forward file in home */ 592: define('z', user->q_home, CurEnv); 593: expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 594: if (!safefile(buf, user->q_uid, S_IREAD)) 595: return; 596: 597: /* we do have an address to forward to -- do it */ 598: include(buf, "forwarding", user, sendq); 599: }