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