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 #ifndef lint 18 #ifdef DBM 19 static char sccsid[] = "@(#)alias.c 5.15 (Berkeley) 04/01/88 (with DBM)"; 20 #else 21 static char sccsid[] = "@(#)alias.c 5.15 (Berkeley) 04/01/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[MAXNAME]; 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 } 592