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 static char SccsId[] = "@(#)alias.c 5.1 (Berkeley) 06/07/85"; 13 #endif not lint 14 15 # include <pwd.h> 16 # include <sys/types.h> 17 # include <sys/stat.h> 18 # include <signal.h> 19 # include <errno.h> 20 # include "sendmail.h" 21 # ifdef FLOCK 22 # include <sys/file.h> 23 # endif FLOCK 24 25 # ifdef DBM 26 SCCSID(@(#)alias.c 5.1 06/07/85 (with DBM)); 27 # else DBM 28 SCCSID(@(#)alias.c 5.1 06/07/85 (without DBM)); 29 # endif DBM 30 31 /* 32 ** ALIAS -- Compute aliases. 33 ** 34 ** Scans the alias file for an alias for the given address. 35 ** If found, it arranges to deliver to the alias list instead. 36 ** Uses libdbm database if -DDBM. 37 ** 38 ** Parameters: 39 ** a -- address to alias. 40 ** sendq -- a pointer to the head of the send queue 41 ** to put the aliases in. 42 ** 43 ** Returns: 44 ** none 45 ** 46 ** Side Effects: 47 ** Aliases found are expanded. 48 ** 49 ** Notes: 50 ** If NoAlias (the "-n" flag) is set, no aliasing is 51 ** done. 52 ** 53 ** Deficiencies: 54 ** It should complain about names that are aliased to 55 ** nothing. 56 */ 57 58 59 #ifdef DBM 60 typedef struct 61 { 62 char *dptr; 63 int dsize; 64 } DATUM; 65 extern DATUM fetch(); 66 #endif DBM 67 68 alias(a, sendq) 69 register ADDRESS *a; 70 ADDRESS **sendq; 71 { 72 register char *p; 73 extern char *aliaslookup(); 74 75 if (NoAlias) 76 return; 77 # ifdef DEBUG 78 if (tTd(27, 1)) 79 printf("alias(%s)\n", a->q_paddr); 80 # endif 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 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 } 226 else 227 { 228 #ifdef LOG 229 syslog(LOG_INFO, "alias database out of date"); 230 #endif LOG 231 message(Arpa_Info, "Warning: alias database out of date"); 232 } 233 } 234 235 236 /* 237 ** If necessary, load the DBM file. 238 ** If running without DBM, load the symbol table. 239 */ 240 241 if (init) 242 { 243 readaliases(aliasfile, TRUE); 244 } 245 # else DBM 246 readaliases(aliasfile, init); 247 # endif DBM 248 } 249 /* 250 ** READALIASES -- read and process the alias file. 251 ** 252 ** This routine implements the part of initaliases that occurs 253 ** when we are not going to use the DBM stuff. 254 ** 255 ** Parameters: 256 ** aliasfile -- the pathname of the alias file master. 257 ** init -- if set, initialize the DBM stuff. 258 ** 259 ** Returns: 260 ** none. 261 ** 262 ** Side Effects: 263 ** Reads aliasfile into the symbol table. 264 ** Optionally, builds the .dir & .pag files. 265 */ 266 267 static 268 readaliases(aliasfile, init) 269 char *aliasfile; 270 bool init; 271 { 272 register char *p; 273 char *p2; 274 char *rhs; 275 bool skipping; 276 int naliases, bytes, longest; 277 FILE *af; 278 int (*oldsigint)(); 279 ADDRESS al, bl; 280 register STAB *s; 281 char line[BUFSIZ]; 282 283 if ((af = fopen(aliasfile, "r")) == NULL) 284 { 285 # ifdef DEBUG 286 if (tTd(27, 1)) 287 printf("Can't open %s\n", aliasfile); 288 # endif 289 errno = 0; 290 NoAlias++; 291 return; 292 } 293 294 # ifdef DBM 295 # ifdef FLOCK 296 /* see if someone else is rebuilding the alias file already */ 297 if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 298 { 299 /* yes, they are -- wait until done and then return */ 300 message(Arpa_Info, "Alias file is already being rebuilt"); 301 if (OpMode != MD_INITALIAS) 302 { 303 /* wait for other rebuild to complete */ 304 (void) flock(fileno(af), LOCK_EX); 305 } 306 fclose(af); 307 errno = 0; 308 return; 309 } 310 # endif FLOCK 311 # endif DBM 312 313 /* 314 ** If initializing, create the new DBM files. 315 */ 316 317 if (init) 318 { 319 oldsigint = signal(SIGINT, SIG_IGN); 320 (void) strcpy(line, aliasfile); 321 (void) strcat(line, ".dir"); 322 if (close(creat(line, DBMMODE)) < 0) 323 { 324 syserr("cannot make %s", line); 325 (void) signal(SIGINT, oldsigint); 326 return; 327 } 328 (void) strcpy(line, aliasfile); 329 (void) strcat(line, ".pag"); 330 if (close(creat(line, DBMMODE)) < 0) 331 { 332 syserr("cannot make %s", line); 333 (void) signal(SIGINT, oldsigint); 334 return; 335 } 336 } 337 338 /* 339 ** Read and interpret lines 340 */ 341 342 FileName = aliasfile; 343 LineNumber = 0; 344 naliases = bytes = longest = 0; 345 skipping = FALSE; 346 while (fgets(line, sizeof (line), af) != NULL) 347 { 348 int lhssize, rhssize; 349 350 LineNumber++; 351 switch (line[0]) 352 { 353 case '#': 354 case '\n': 355 case '\0': 356 skipping = FALSE; 357 continue; 358 359 case ' ': 360 case '\t': 361 if (!skipping) 362 syserr("Non-continuation line starts with space"); 363 skipping = TRUE; 364 continue; 365 } 366 skipping = FALSE; 367 368 /* 369 ** Process the LHS 370 ** Find the final colon, and parse the address. 371 ** It should resolve to a local name -- this will 372 ** be checked later (we want to optionally do 373 ** parsing of the RHS first to maximize error 374 ** detection). 375 */ 376 377 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 378 continue; 379 if (*p++ != ':') 380 { 381 syserr("missing colon"); 382 continue; 383 } 384 if (parseaddr(line, &al, 1, ':') == NULL) 385 { 386 syserr("illegal alias name"); 387 continue; 388 } 389 loweraddr(&al); 390 391 /* 392 ** Process the RHS. 393 ** 'al' is the internal form of the LHS address. 394 ** 'p' points to the text of the RHS. 395 */ 396 397 rhs = p; 398 for (;;) 399 { 400 register char c; 401 402 if (init) 403 { 404 /* do parsing & compression of addresses */ 405 c = *p; 406 while (c != '\0') 407 { 408 p2 = p; 409 while (*p != '\n' && *p != ',' && *p != '\0') 410 p++; 411 c = *p; 412 if (c == '\n') 413 c = '\0'; 414 *p = '\0'; 415 if (*p2 != '\0') 416 (void) parseaddr(p2, &bl, -1, ','); 417 if (c != '\0') 418 *p++ = c; 419 } 420 } 421 else 422 { 423 p = &p[strlen(p)]; 424 if (p[-1] == '\n') 425 *--p = '\0'; 426 } 427 428 /* see if there should be a continuation line */ 429 c = fgetc(af); 430 if (!feof(af)) 431 (void) ungetc(c, af); 432 if (c != ' ' && c != '\t') 433 break; 434 435 /* read continuation line */ 436 if (fgets(p, sizeof line - (p - line), af) == NULL) 437 break; 438 LineNumber++; 439 } 440 if (al.q_mailer != LocalMailer) 441 { 442 syserr("cannot alias non-local names"); 443 continue; 444 } 445 446 /* 447 ** Insert alias into symbol table or DBM file 448 */ 449 450 lhssize = strlen(al.q_user) + 1; 451 rhssize = strlen(rhs) + 1; 452 453 # ifdef DBM 454 if (init) 455 { 456 DATUM key, content; 457 458 key.dsize = lhssize; 459 key.dptr = al.q_user; 460 content.dsize = rhssize; 461 content.dptr = rhs; 462 store(key, content); 463 } 464 else 465 # endif DBM 466 { 467 s = stab(al.q_user, ST_ALIAS, ST_ENTER); 468 s->s_alias = newstr(rhs); 469 } 470 471 /* statistics */ 472 naliases++; 473 bytes += lhssize + rhssize; 474 if (rhssize > longest) 475 longest = rhssize; 476 } 477 478 # ifdef DBM 479 if (init) 480 { 481 /* add the distinquished alias "@" */ 482 DATUM key; 483 484 key.dsize = 2; 485 key.dptr = "@"; 486 store(key, key); 487 488 /* restore the old signal */ 489 (void) signal(SIGINT, oldsigint); 490 } 491 # endif DBM 492 493 /* closing the alias file drops the lock */ 494 (void) fclose(af); 495 CurEnv->e_to = NULL; 496 FileName = NULL; 497 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 498 naliases, longest, bytes); 499 } 500 /* 501 ** FORWARD -- Try to forward mail 502 ** 503 ** This is similar but not identical to aliasing. 504 ** 505 ** Parameters: 506 ** user -- the name of the user who's mail we would like 507 ** to forward to. It must have been verified -- 508 ** i.e., the q_home field must have been filled 509 ** in. 510 ** sendq -- a pointer to the head of the send queue to 511 ** put this user's aliases in. 512 ** 513 ** Returns: 514 ** none. 515 ** 516 ** Side Effects: 517 ** New names are added to send queues. 518 */ 519 520 forward(user, sendq) 521 ADDRESS *user; 522 ADDRESS **sendq; 523 { 524 char buf[60]; 525 extern bool safefile(); 526 527 # ifdef DEBUG 528 if (tTd(27, 1)) 529 printf("forward(%s)\n", user->q_paddr); 530 # endif DEBUG 531 532 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 533 return; 534 # ifdef DEBUG 535 if (user->q_home == NULL) 536 syserr("forward: no home"); 537 # endif DEBUG 538 539 /* good address -- look for .forward file in home */ 540 define('z', user->q_home, CurEnv); 541 expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 542 if (!safefile(buf, user->q_uid, S_IREAD)) 543 return; 544 545 /* we do have an address to forward to -- do it */ 546 include(buf, "forwarding", user, sendq); 547 } 548