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 # include <pwd.h> 12 # include <sys/types.h> 13 # include <sys/stat.h> 14 # include <signal.h> 15 # include <errno.h> 16 # include "sendmail.h" 17 # ifdef FLOCK 18 # include <sys/file.h> 19 # endif FLOCK 20 21 #ifndef lint 22 # ifdef DBM 23 static char SccsId[] = "@(#)alias.c 5.13 (Berkeley) 04/17/86 (with DBM)"; 24 # else DBM 25 static char SccsId[] = "@(#)alias.c 5.13 (Berkeley) 04/17/86 (without DBM)"; 26 # endif DBM 27 #endif not lint 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 static bool initialized = FALSE; 181 182 if (initialized) 183 return; 184 initialized = TRUE; 185 186 if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 187 { 188 if (aliasfile != NULL && init) 189 syserr("Cannot open %s", aliasfile); 190 NoAlias = TRUE; 191 errno = 0; 192 return; 193 } 194 195 # ifdef DBM 196 /* 197 ** Check to see that the alias file is complete. 198 ** If not, we will assume that someone died, and it is up 199 ** to us to rebuild it. 200 */ 201 202 if (!init) 203 dbminit(aliasfile); 204 atcnt = SafeAlias * 2; 205 if (atcnt > 0) 206 { 207 while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 208 { 209 /* 210 ** Reinitialize alias file in case the new 211 ** one is mv'ed in instead of cp'ed in. 212 ** 213 ** Only works with new DBM -- old one will 214 ** just consume file descriptors forever. 215 ** If you have a dbmclose() it can be 216 ** added before the sleep(30). 217 */ 218 219 sleep(30); 220 # ifdef NDBM 221 dbminit(aliasfile); 222 # endif NDBM 223 } 224 } 225 else 226 atcnt = 1; 227 228 /* 229 ** See if the DBM version of the file is out of date with 230 ** the text version. If so, go into 'init' mode automatically. 231 ** This only happens if our effective userid owns the DBM 232 ** version or if the mode of the database is 666 -- this 233 ** is an attempt to avoid protection problems. Note the 234 ** unpalatable hack to see if the stat succeeded. 235 */ 236 237 modtime = stb.st_mtime; 238 (void) strcpy(buf, aliasfile); 239 (void) strcat(buf, ".pag"); 240 stb.st_ino = 0; 241 if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 242 { 243 errno = 0; 244 if (AutoRebuild && stb.st_ino != 0 && 245 ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid())) 246 { 247 init = TRUE; 248 automatic = TRUE; 249 message(Arpa_Info, "rebuilding alias database"); 250 #ifdef LOG 251 if (LogLevel >= 7) 252 syslog(LOG_INFO, "rebuilding alias database"); 253 #endif LOG 254 } 255 else 256 { 257 #ifdef LOG 258 if (LogLevel >= 7) 259 syslog(LOG_INFO, "alias database out of date"); 260 #endif LOG 261 message(Arpa_Info, "Warning: alias database out of date"); 262 } 263 } 264 265 266 /* 267 ** If necessary, load the DBM file. 268 ** If running without DBM, load the symbol table. 269 */ 270 271 if (init) 272 { 273 #ifdef LOG 274 if (LogLevel >= 6) 275 { 276 extern char *username(); 277 278 syslog(LOG_NOTICE, "alias database %srebuilt by %s", 279 automatic ? "auto" : "", username()); 280 } 281 #endif LOG 282 readaliases(aliasfile, TRUE); 283 } 284 # else DBM 285 readaliases(aliasfile, init); 286 # endif DBM 287 } 288 /* 289 ** READALIASES -- read and process the alias file. 290 ** 291 ** This routine implements the part of initaliases that occurs 292 ** when we are not going to use the DBM stuff. 293 ** 294 ** Parameters: 295 ** aliasfile -- the pathname of the alias file master. 296 ** init -- if set, initialize the DBM stuff. 297 ** 298 ** Returns: 299 ** none. 300 ** 301 ** Side Effects: 302 ** Reads aliasfile into the symbol table. 303 ** Optionally, builds the .dir & .pag files. 304 */ 305 306 static 307 readaliases(aliasfile, init) 308 char *aliasfile; 309 bool init; 310 { 311 register char *p; 312 char *rhs; 313 bool skipping; 314 int naliases, bytes, longest; 315 FILE *af; 316 int (*oldsigint)(); 317 ADDRESS al, bl; 318 register STAB *s; 319 char line[BUFSIZ]; 320 321 if ((af = fopen(aliasfile, "r")) == NULL) 322 { 323 # ifdef DEBUG 324 if (tTd(27, 1)) 325 printf("Can't open %s\n", aliasfile); 326 # endif 327 errno = 0; 328 NoAlias++; 329 return; 330 } 331 332 # ifdef DBM 333 # ifdef FLOCK 334 /* see if someone else is rebuilding the alias file already */ 335 if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 336 { 337 /* yes, they are -- wait until done and then return */ 338 message(Arpa_Info, "Alias file is already being rebuilt"); 339 if (OpMode != MD_INITALIAS) 340 { 341 /* wait for other rebuild to complete */ 342 (void) flock(fileno(af), LOCK_EX); 343 } 344 (void) fclose(af); 345 errno = 0; 346 return; 347 } 348 # endif FLOCK 349 # endif DBM 350 351 /* 352 ** If initializing, create the new DBM files. 353 */ 354 355 if (init) 356 { 357 oldsigint = signal(SIGINT, SIG_IGN); 358 (void) strcpy(line, aliasfile); 359 (void) strcat(line, ".dir"); 360 if (close(creat(line, DBMMODE)) < 0) 361 { 362 syserr("cannot make %s", line); 363 (void) signal(SIGINT, oldsigint); 364 return; 365 } 366 (void) strcpy(line, aliasfile); 367 (void) strcat(line, ".pag"); 368 if (close(creat(line, DBMMODE)) < 0) 369 { 370 syserr("cannot make %s", line); 371 (void) signal(SIGINT, oldsigint); 372 return; 373 } 374 dbminit(aliasfile); 375 } 376 377 /* 378 ** Read and interpret lines 379 */ 380 381 FileName = aliasfile; 382 LineNumber = 0; 383 naliases = bytes = longest = 0; 384 skipping = FALSE; 385 while (fgets(line, sizeof (line), af) != NULL) 386 { 387 int lhssize, rhssize; 388 389 LineNumber++; 390 p = index(line, '\n'); 391 if (p != NULL) 392 *p = '\0'; 393 switch (line[0]) 394 { 395 case '#': 396 case '\0': 397 skipping = FALSE; 398 continue; 399 400 case ' ': 401 case '\t': 402 if (!skipping) 403 syserr("Non-continuation line starts with space"); 404 skipping = TRUE; 405 continue; 406 } 407 skipping = FALSE; 408 409 /* 410 ** Process the LHS 411 ** Find the final colon, and parse the address. 412 ** It should resolve to a local name -- this will 413 ** be checked later (we want to optionally do 414 ** parsing of the RHS first to maximize error 415 ** detection). 416 */ 417 418 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 419 continue; 420 if (*p++ != ':') 421 { 422 syserr("missing colon"); 423 continue; 424 } 425 if (parseaddr(line, &al, 1, ':') == NULL) 426 { 427 syserr("illegal alias name"); 428 continue; 429 } 430 loweraddr(&al); 431 432 /* 433 ** Process the RHS. 434 ** 'al' is the internal form of the LHS address. 435 ** 'p' points to the text of the RHS. 436 */ 437 438 rhs = p; 439 for (;;) 440 { 441 register char c; 442 443 if (init && CheckAliases) 444 { 445 /* do parsing & compression of addresses */ 446 while (*p != '\0') 447 { 448 extern char *DelimChar; 449 450 while (isspace(*p) || *p == ',') 451 p++; 452 if (*p == '\0') 453 break; 454 if (parseaddr(p, &bl, -1, ',') == NULL) 455 usrerr("%s... bad address", p); 456 p = DelimChar; 457 } 458 } 459 else 460 { 461 p = &p[strlen(p)]; 462 if (p[-1] == '\n') 463 *--p = '\0'; 464 } 465 466 /* see if there should be a continuation line */ 467 c = fgetc(af); 468 if (!feof(af)) 469 (void) ungetc(c, af); 470 if (c != ' ' && c != '\t') 471 break; 472 473 /* read continuation line */ 474 if (fgets(p, sizeof line - (p - line), af) == NULL) 475 break; 476 LineNumber++; 477 } 478 if (al.q_mailer != LocalMailer) 479 { 480 syserr("cannot alias non-local names"); 481 continue; 482 } 483 484 /* 485 ** Insert alias into symbol table or DBM file 486 */ 487 488 lhssize = strlen(al.q_user) + 1; 489 rhssize = strlen(rhs) + 1; 490 491 # ifdef DBM 492 if (init) 493 { 494 DATUM key, content; 495 496 key.dsize = lhssize; 497 key.dptr = al.q_user; 498 content.dsize = rhssize; 499 content.dptr = rhs; 500 store(key, content); 501 } 502 else 503 # endif DBM 504 { 505 s = stab(al.q_user, ST_ALIAS, ST_ENTER); 506 s->s_alias = newstr(rhs); 507 } 508 509 /* statistics */ 510 naliases++; 511 bytes += lhssize + rhssize; 512 if (rhssize > longest) 513 longest = rhssize; 514 } 515 516 # ifdef DBM 517 if (init) 518 { 519 /* add the distinquished alias "@" */ 520 DATUM key; 521 522 key.dsize = 2; 523 key.dptr = "@"; 524 store(key, key); 525 526 /* restore the old signal */ 527 (void) signal(SIGINT, oldsigint); 528 } 529 # endif DBM 530 531 /* closing the alias file drops the lock */ 532 (void) fclose(af); 533 CurEnv->e_to = NULL; 534 FileName = NULL; 535 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 536 naliases, longest, bytes); 537 # ifdef LOG 538 if (LogLevel >= 8) 539 syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 540 naliases, longest, bytes); 541 # endif LOG 542 } 543 /* 544 ** FORWARD -- Try to forward mail 545 ** 546 ** This is similar but not identical to aliasing. 547 ** 548 ** Parameters: 549 ** user -- the name of the user who's mail we would like 550 ** to forward to. It must have been verified -- 551 ** i.e., the q_home field must have been filled 552 ** in. 553 ** sendq -- a pointer to the head of the send queue to 554 ** put this user's aliases in. 555 ** 556 ** Returns: 557 ** none. 558 ** 559 ** Side Effects: 560 ** New names are added to send queues. 561 */ 562 563 forward(user, sendq) 564 ADDRESS *user; 565 ADDRESS **sendq; 566 { 567 char buf[60]; 568 extern bool safefile(); 569 570 # ifdef DEBUG 571 if (tTd(27, 1)) 572 printf("forward(%s)\n", user->q_paddr); 573 # endif DEBUG 574 575 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 576 return; 577 # ifdef DEBUG 578 if (user->q_home == NULL) 579 syserr("forward: no home"); 580 # endif DEBUG 581 582 /* good address -- look for .forward file in home */ 583 define('z', user->q_home, CurEnv); 584 expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 585 if (!safefile(buf, user->q_uid, S_IREAD)) 586 return; 587 588 /* we do have an address to forward to -- do it */ 589 include(buf, "forwarding", user, sendq); 590 } 591