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