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