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