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