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.6 (Berkeley) 10/24/85 (with DBM)"; 14 # else DBM 15 static char SccsId[] = "@(#)alias.c 5.6 (Berkeley) 10/24/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 p = index(line, '\n'); 357 if (p != NULL) 358 *p = '\0'; 359 switch (line[0]) 360 { 361 case '#': 362 case '\0': 363 skipping = FALSE; 364 continue; 365 366 case ' ': 367 case '\t': 368 if (!skipping) 369 syserr("Non-continuation line starts with space"); 370 skipping = TRUE; 371 continue; 372 } 373 skipping = FALSE; 374 375 /* 376 ** Process the LHS 377 ** Find the final colon, and parse the address. 378 ** It should resolve to a local name -- this will 379 ** be checked later (we want to optionally do 380 ** parsing of the RHS first to maximize error 381 ** detection). 382 */ 383 384 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 385 continue; 386 if (*p++ != ':') 387 { 388 syserr("missing colon"); 389 continue; 390 } 391 if (parseaddr(line, &al, 1, ':') == NULL) 392 { 393 syserr("illegal alias name"); 394 continue; 395 } 396 loweraddr(&al); 397 398 /* 399 ** Process the RHS. 400 ** 'al' is the internal form of the LHS address. 401 ** 'p' points to the text of the RHS. 402 */ 403 404 rhs = p; 405 for (;;) 406 { 407 register char c; 408 409 if (init) 410 { 411 /* do parsing & compression of addresses */ 412 while (*p != '\0') 413 { 414 extern char *DelimChar; 415 416 while (isspace(*p) || *p == ',') 417 p++; 418 if (*p == '\0') 419 break; 420 if (parseaddr(p, &bl, -1, ',') == NULL) 421 usrerr("%s... bad address", p); 422 p = DelimChar; 423 } 424 } 425 else 426 { 427 p = &p[strlen(p)]; 428 if (p[-1] == '\n') 429 *--p = '\0'; 430 } 431 432 /* see if there should be a continuation line */ 433 c = fgetc(af); 434 if (!feof(af)) 435 (void) ungetc(c, af); 436 if (c != ' ' && c != '\t') 437 break; 438 439 /* read continuation line */ 440 if (fgets(p, sizeof line - (p - line), af) == NULL) 441 break; 442 LineNumber++; 443 } 444 if (al.q_mailer != LocalMailer) 445 { 446 syserr("cannot alias non-local names"); 447 continue; 448 } 449 450 /* 451 ** Insert alias into symbol table or DBM file 452 */ 453 454 lhssize = strlen(al.q_user) + 1; 455 rhssize = strlen(rhs) + 1; 456 457 # ifdef DBM 458 if (init) 459 { 460 DATUM key, content; 461 462 key.dsize = lhssize; 463 key.dptr = al.q_user; 464 content.dsize = rhssize; 465 content.dptr = rhs; 466 store(key, content); 467 } 468 else 469 # endif DBM 470 { 471 s = stab(al.q_user, ST_ALIAS, ST_ENTER); 472 s->s_alias = newstr(rhs); 473 } 474 475 /* statistics */ 476 naliases++; 477 bytes += lhssize + rhssize; 478 if (rhssize > longest) 479 longest = rhssize; 480 } 481 482 # ifdef DBM 483 if (init) 484 { 485 /* add the distinquished alias "@" */ 486 DATUM key; 487 488 key.dsize = 2; 489 key.dptr = "@"; 490 store(key, key); 491 492 /* restore the old signal */ 493 (void) signal(SIGINT, oldsigint); 494 } 495 # endif DBM 496 497 /* closing the alias file drops the lock */ 498 (void) fclose(af); 499 CurEnv->e_to = NULL; 500 FileName = NULL; 501 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 502 naliases, longest, bytes); 503 # ifdef LOG 504 if (LogLevel >= 8) 505 syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 506 naliases, longest, bytes); 507 # endif LOG 508 } 509 /* 510 ** FORWARD -- Try to forward mail 511 ** 512 ** This is similar but not identical to aliasing. 513 ** 514 ** Parameters: 515 ** user -- the name of the user who's mail we would like 516 ** to forward to. It must have been verified -- 517 ** i.e., the q_home field must have been filled 518 ** in. 519 ** sendq -- a pointer to the head of the send queue to 520 ** put this user's aliases in. 521 ** 522 ** Returns: 523 ** none. 524 ** 525 ** Side Effects: 526 ** New names are added to send queues. 527 */ 528 529 forward(user, sendq) 530 ADDRESS *user; 531 ADDRESS **sendq; 532 { 533 char buf[60]; 534 extern bool safefile(); 535 536 # ifdef DEBUG 537 if (tTd(27, 1)) 538 printf("forward(%s)\n", user->q_paddr); 539 # endif DEBUG 540 541 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 542 return; 543 # ifdef DEBUG 544 if (user->q_home == NULL) 545 syserr("forward: no home"); 546 # endif DEBUG 547 548 /* good address -- look for .forward file in home */ 549 define('z', user->q_home, CurEnv); 550 expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 551 if (!safefile(buf, user->q_uid, S_IREAD)) 552 return; 553 554 /* we do have an address to forward to -- do it */ 555 include(buf, "forwarding", user, sendq); 556 } 557