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