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