1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms are permitted 7 * provided that the above copyright notice and this paragraph are 8 * duplicated in all such forms and that any documentation, 9 * advertising materials, and other materials related to such 10 * distribution and use acknowledge that the software was developed 11 * by the University of California, Berkeley. The name of the 12 * University may not be used to endorse or promote products derived 13 * from this software without specific prior written permission. 14 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 16 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 17 */ 18 19 #ifndef lint 20 #ifdef DBM 21 static char sccsid[] = "@(#)alias.c 5.16 (Berkeley) 07/14/88 (with DBM)"; 22 #else 23 static char sccsid[] = "@(#)alias.c 5.16 (Berkeley) 07/14/88 (without DBM)"; 24 #endif 25 #endif /* not lint */ 26 27 # include <pwd.h> 28 # include <sys/types.h> 29 # include <sys/stat.h> 30 # include <signal.h> 31 # include <errno.h> 32 # include "sendmail.h" 33 # include <sys/file.h> 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 /* see if someone else is rebuilding the alias file already */ 339 if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 340 { 341 /* yes, they are -- wait until done and then return */ 342 message(Arpa_Info, "Alias file is already being rebuilt"); 343 if (OpMode != MD_INITALIAS) 344 { 345 /* wait for other rebuild to complete */ 346 (void) flock(fileno(af), LOCK_EX); 347 } 348 (void) fclose(af); 349 errno = 0; 350 return; 351 } 352 # endif DBM 353 354 /* 355 ** If initializing, create the new DBM files. 356 */ 357 358 if (init) 359 { 360 oldsigint = signal(SIGINT, SIG_IGN); 361 (void) strcpy(line, aliasfile); 362 (void) strcat(line, ".dir"); 363 if (close(creat(line, DBMMODE)) < 0) 364 { 365 syserr("cannot make %s", line); 366 (void) signal(SIGINT, oldsigint); 367 return; 368 } 369 (void) strcpy(line, aliasfile); 370 (void) strcat(line, ".pag"); 371 if (close(creat(line, DBMMODE)) < 0) 372 { 373 syserr("cannot make %s", line); 374 (void) signal(SIGINT, oldsigint); 375 return; 376 } 377 dbminit(aliasfile); 378 } 379 380 /* 381 ** Read and interpret lines 382 */ 383 384 FileName = aliasfile; 385 LineNumber = 0; 386 naliases = bytes = longest = 0; 387 skipping = FALSE; 388 while (fgets(line, sizeof (line), af) != NULL) 389 { 390 int lhssize, rhssize; 391 392 LineNumber++; 393 p = index(line, '\n'); 394 if (p != NULL) 395 *p = '\0'; 396 switch (line[0]) 397 { 398 case '#': 399 case '\0': 400 skipping = FALSE; 401 continue; 402 403 case ' ': 404 case '\t': 405 if (!skipping) 406 syserr("Non-continuation line starts with space"); 407 skipping = TRUE; 408 continue; 409 } 410 skipping = FALSE; 411 412 /* 413 ** Process the LHS 414 ** Find the final colon, and parse the address. 415 ** It should resolve to a local name -- this will 416 ** be checked later (we want to optionally do 417 ** parsing of the RHS first to maximize error 418 ** detection). 419 */ 420 421 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 422 continue; 423 if (*p++ != ':') 424 { 425 syserr("missing colon"); 426 continue; 427 } 428 if (parseaddr(line, &al, 1, ':') == NULL) 429 { 430 syserr("illegal alias name"); 431 continue; 432 } 433 loweraddr(&al); 434 435 /* 436 ** Process the RHS. 437 ** 'al' is the internal form of the LHS address. 438 ** 'p' points to the text of the RHS. 439 */ 440 441 rhs = p; 442 for (;;) 443 { 444 register char c; 445 446 if (init && CheckAliases) 447 { 448 /* do parsing & compression of addresses */ 449 while (*p != '\0') 450 { 451 extern char *DelimChar; 452 453 while (isspace(*p) || *p == ',') 454 p++; 455 if (*p == '\0') 456 break; 457 if (parseaddr(p, &bl, -1, ',') == NULL) 458 usrerr("%s... bad address", p); 459 p = DelimChar; 460 } 461 } 462 else 463 { 464 p = &p[strlen(p)]; 465 if (p[-1] == '\n') 466 *--p = '\0'; 467 } 468 469 /* see if there should be a continuation line */ 470 c = fgetc(af); 471 if (!feof(af)) 472 (void) ungetc(c, af); 473 if (c != ' ' && c != '\t') 474 break; 475 476 /* read continuation line */ 477 if (fgets(p, sizeof line - (p - line), af) == NULL) 478 break; 479 LineNumber++; 480 } 481 if (al.q_mailer != LocalMailer) 482 { 483 syserr("cannot alias non-local names"); 484 continue; 485 } 486 487 /* 488 ** Insert alias into symbol table or DBM file 489 */ 490 491 lhssize = strlen(al.q_user) + 1; 492 rhssize = strlen(rhs) + 1; 493 494 # ifdef DBM 495 if (init) 496 { 497 DATUM key, content; 498 499 key.dsize = lhssize; 500 key.dptr = al.q_user; 501 content.dsize = rhssize; 502 content.dptr = rhs; 503 store(key, content); 504 } 505 else 506 # endif DBM 507 { 508 s = stab(al.q_user, ST_ALIAS, ST_ENTER); 509 s->s_alias = newstr(rhs); 510 } 511 512 /* statistics */ 513 naliases++; 514 bytes += lhssize + rhssize; 515 if (rhssize > longest) 516 longest = rhssize; 517 } 518 519 # ifdef DBM 520 if (init) 521 { 522 /* add the distinquished alias "@" */ 523 DATUM key; 524 525 key.dsize = 2; 526 key.dptr = "@"; 527 store(key, key); 528 529 /* restore the old signal */ 530 (void) signal(SIGINT, oldsigint); 531 } 532 # endif DBM 533 534 /* closing the alias file drops the lock */ 535 (void) fclose(af); 536 CurEnv->e_to = NULL; 537 FileName = NULL; 538 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 539 naliases, longest, bytes); 540 # ifdef LOG 541 if (LogLevel >= 8) 542 syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 543 naliases, longest, bytes); 544 # endif LOG 545 } 546 /* 547 ** FORWARD -- Try to forward mail 548 ** 549 ** This is similar but not identical to aliasing. 550 ** 551 ** Parameters: 552 ** user -- the name of the user who's mail we would like 553 ** to forward to. It must have been verified -- 554 ** i.e., the q_home field must have been filled 555 ** in. 556 ** sendq -- a pointer to the head of the send queue to 557 ** put this user's aliases in. 558 ** 559 ** Returns: 560 ** none. 561 ** 562 ** Side Effects: 563 ** New names are added to send queues. 564 */ 565 566 forward(user, sendq) 567 ADDRESS *user; 568 ADDRESS **sendq; 569 { 570 char buf[60]; 571 extern bool safefile(); 572 573 # ifdef DEBUG 574 if (tTd(27, 1)) 575 printf("forward(%s)\n", user->q_paddr); 576 # endif DEBUG 577 578 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 579 return; 580 # ifdef DEBUG 581 if (user->q_home == NULL) 582 syserr("forward: no home"); 583 # endif DEBUG 584 585 /* good address -- look for .forward file in home */ 586 define('z', user->q_home, CurEnv); 587 expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 588 if (!safefile(buf, user->q_uid, S_IREAD)) 589 return; 590 591 /* we do have an address to forward to -- do it */ 592 include(buf, "forwarding", user, sendq); 593 } 594