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