1 # include <pwd.h> 2 # include <sys/types.h> 3 # include <sys/stat.h> 4 # include <signal.h> 5 # include "sendmail.h" 6 7 # ifdef DBM 8 SCCSID(@(#)alias.c 4.8 02/15/85 (with DBM)); 9 # else DBM 10 SCCSID(@(#)alias.c 4.8 02/15/85 (without DBM)); 11 # endif DBM 12 13 /* 14 ** ALIAS -- Compute aliases. 15 ** 16 ** Scans the alias file for an alias for the given address. 17 ** If found, it arranges to deliver to the alias list instead. 18 ** Uses libdbm database if -DDBM. 19 ** 20 ** Parameters: 21 ** a -- address to alias. 22 ** sendq -- a pointer to the head of the send queue 23 ** to put the aliases in. 24 ** 25 ** Returns: 26 ** none 27 ** 28 ** Side Effects: 29 ** Aliases found are expanded. 30 ** 31 ** Notes: 32 ** If NoAlias (the "-n" flag) is set, no aliasing is 33 ** done. 34 ** 35 ** Deficiencies: 36 ** It should complain about names that are aliased to 37 ** nothing. 38 */ 39 40 41 #ifdef DBM 42 typedef struct 43 { 44 char *dptr; 45 int dsize; 46 } DATUM; 47 extern DATUM fetch(); 48 #endif DBM 49 50 alias(a, sendq) 51 register ADDRESS *a; 52 ADDRESS **sendq; 53 { 54 register char *p; 55 extern char *aliaslookup(); 56 57 if (NoAlias) 58 return; 59 # ifdef DEBUG 60 if (tTd(27, 1)) 61 printf("alias(%s)\n", a->q_paddr); 62 # endif 63 64 /* don't realias already aliased names */ 65 if (bitset(QDONTSEND, a->q_flags)) 66 return; 67 68 CurEnv->e_to = a->q_paddr; 69 70 /* 71 ** Look up this name 72 */ 73 74 p = aliaslookup(a->q_user); 75 if (p == NULL) 76 return; 77 78 /* 79 ** Match on Alias. 80 ** Deliver to the target list. 81 */ 82 83 # ifdef DEBUG 84 if (tTd(27, 1)) 85 printf("%s (%s, %s) aliased to %s\n", 86 a->q_paddr, a->q_host, a->q_user, p); 87 # endif 88 message(Arpa_Info, "aliased to %s", p); 89 AliasLevel++; 90 sendtolist(p, a, sendq); 91 AliasLevel--; 92 } 93 /* 94 ** ALIASLOOKUP -- look up a name in the alias file. 95 ** 96 ** Parameters: 97 ** name -- the name to look up. 98 ** 99 ** Returns: 100 ** the value of name. 101 ** NULL if unknown. 102 ** 103 ** Side Effects: 104 ** none. 105 ** 106 ** Warnings: 107 ** The return value will be trashed across calls. 108 */ 109 110 char * 111 aliaslookup(name) 112 char *name; 113 { 114 # ifdef DBM 115 DATUM rhs, lhs; 116 117 /* create a key for fetch */ 118 lhs.dptr = name; 119 lhs.dsize = strlen(name) + 1; 120 rhs = fetch(lhs); 121 return (rhs.dptr); 122 # else DBM 123 register STAB *s; 124 125 s = stab(name, ST_ALIAS, ST_FIND); 126 if (s == NULL) 127 return (NULL); 128 return (s->s_alias); 129 # endif DBM 130 } 131 /* 132 ** INITALIASES -- initialize for aliasing 133 ** 134 ** Very different depending on whether we are running DBM or not. 135 ** 136 ** Parameters: 137 ** aliasfile -- location of aliases. 138 ** init -- if set and if DBM, initialize the DBM files. 139 ** 140 ** Returns: 141 ** none. 142 ** 143 ** Side Effects: 144 ** initializes aliases: 145 ** if DBM: opens the database. 146 ** if ~DBM: reads the aliases into the symbol table. 147 */ 148 149 # define DBMMODE 0666 150 151 initaliases(aliasfile, init) 152 char *aliasfile; 153 bool init; 154 { 155 #ifdef DBM 156 int atcnt; 157 char buf[MAXNAME]; 158 time_t modtime; 159 int (*oldsigint)(); 160 #endif DBM 161 struct stat stb; 162 163 if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 164 { 165 NoAlias = TRUE; 166 errno = 0; 167 return; 168 } 169 170 # ifdef DBM 171 /* 172 ** Check to see that the alias file is complete. 173 ** If not, we will assume that someone died, and it is up 174 ** to us to rebuild it. 175 */ 176 177 dbminit(aliasfile); 178 atcnt = SafeAlias * 2; 179 if (atcnt > 0) 180 { 181 while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 182 sleep(30); 183 } 184 else 185 atcnt = 1; 186 187 /* 188 ** See if the DBM version of the file is out of date with 189 ** the text version. If so, go into 'init' mode automatically. 190 ** This only happens if our effective userid owns the DBM 191 ** version or if the mode of the database is 666 -- this 192 ** is an attempt to avoid protection problems. Note the 193 ** unpalatable hack to see if the stat succeeded. 194 */ 195 196 modtime = stb.st_mtime; 197 (void) strcpy(buf, aliasfile); 198 (void) strcat(buf, ".pag"); 199 stb.st_ino = 0; 200 if (!init && (atcnt < 0 || stat(buf, &stb) < 0 || stb.st_mtime < modtime)) 201 { 202 errno = 0; 203 if (AutoRebuild && stb.st_ino != 0 && 204 ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid())) 205 { 206 init = TRUE; 207 message(Arpa_Info, "rebuilding alias database"); 208 } 209 else 210 { 211 bool oldverb = Verbose; 212 213 Verbose = TRUE; 214 message(Arpa_Info, "Warning: alias database out of date"); 215 Verbose = oldverb; 216 } 217 } 218 219 /* 220 ** If initializing, create the new files. 221 ** We should lock the alias file here to prevent other 222 ** instantiations of sendmail from reading an incomplete 223 ** file -- or worse yet, doing a concurrent initialize. 224 */ 225 226 if (init) 227 { 228 oldsigint = signal(SIGINT, SIG_IGN); 229 (void) strcpy(buf, aliasfile); 230 (void) strcat(buf, ".dir"); 231 if (close(creat(buf, DBMMODE)) < 0) 232 { 233 syserr("cannot make %s", buf); 234 (void) signal(SIGINT, oldsigint); 235 return; 236 } 237 (void) strcpy(buf, aliasfile); 238 (void) strcat(buf, ".pag"); 239 if (close(creat(buf, DBMMODE)) < 0) 240 { 241 syserr("cannot make %s", buf); 242 (void) signal(SIGINT, oldsigint); 243 return; 244 } 245 } 246 247 /* 248 ** If necessary, load the DBM file. 249 ** If running without DBM, load the symbol table. 250 ** After loading the DBM file, add the distinquished alias "@". 251 */ 252 253 if (init) 254 { 255 DATUM key; 256 257 readaliases(aliasfile, TRUE); 258 key.dsize = 2; 259 key.dptr = "@"; 260 store(key, key); 261 (void) signal(SIGINT, oldsigint); 262 } 263 # else DBM 264 readaliases(aliasfile, init); 265 # endif DBM 266 } 267 /* 268 ** READALIASES -- read and process the alias file. 269 ** 270 ** This routine implements the part of initaliases that occurs 271 ** when we are not going to use the DBM stuff. 272 ** 273 ** Parameters: 274 ** aliasfile -- the pathname of the alias file master. 275 ** init -- if set, initialize the DBM stuff. 276 ** 277 ** Returns: 278 ** none. 279 ** 280 ** Side Effects: 281 ** Reads aliasfile into the symbol table. 282 ** Optionally, builds the .dir & .pag files. 283 */ 284 285 static 286 readaliases(aliasfile, init) 287 char *aliasfile; 288 bool init; 289 { 290 register char *p; 291 char *p2; 292 char *rhs; 293 bool skipping; 294 int naliases, bytes, longest; 295 FILE *af; 296 ADDRESS al, bl; 297 register STAB *s; 298 char line[BUFSIZ]; 299 300 if ((af = fopen(aliasfile, "r")) == NULL) 301 { 302 # ifdef DEBUG 303 if (tTd(27, 1)) 304 printf("Can't open %s\n", aliasfile); 305 # endif 306 errno = 0; 307 NoAlias++; 308 return; 309 } 310 311 /* 312 ** Read and interpret lines 313 */ 314 315 FileName = aliasfile; 316 LineNumber = 0; 317 naliases = bytes = longest = 0; 318 skipping = FALSE; 319 while (fgets(line, sizeof (line), af) != NULL) 320 { 321 int lhssize, rhssize; 322 323 LineNumber++; 324 switch (line[0]) 325 { 326 case '#': 327 case '\n': 328 case '\0': 329 skipping = FALSE; 330 continue; 331 332 case ' ': 333 case '\t': 334 if (!skipping) 335 syserr("Non-continuation line starts with space"); 336 skipping = TRUE; 337 continue; 338 } 339 skipping = FALSE; 340 341 /* 342 ** Process the LHS 343 ** Find the final colon, and parse the address. 344 ** It should resolve to a local name -- this will 345 ** be checked later (we want to optionally do 346 ** parsing of the RHS first to maximize error 347 ** detection). 348 */ 349 350 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 351 continue; 352 if (*p++ != ':') 353 { 354 syserr("missing colon"); 355 continue; 356 } 357 if (parseaddr(line, &al, 1, ':') == NULL) 358 { 359 syserr("illegal alias name"); 360 continue; 361 } 362 loweraddr(&al); 363 364 /* 365 ** Process the RHS. 366 ** 'al' is the internal form of the LHS address. 367 ** 'p' points to the text of the RHS. 368 */ 369 370 rhs = p; 371 for (;;) 372 { 373 register char c; 374 375 if (init) 376 { 377 /* do parsing & compression of addresses */ 378 c = *p; 379 while (c != '\0') 380 { 381 p2 = p; 382 while (*p != '\n' && *p != ',' && *p != '\0') 383 p++; 384 c = *p; 385 if (c == '\n') 386 c = '\0'; 387 *p = '\0'; 388 if (*p2 != '\0') 389 (void) parseaddr(p2, &bl, -1, ','); 390 if (c != '\0') 391 *p++ = c; 392 } 393 } 394 else 395 { 396 p = &p[strlen(p)]; 397 if (p[-1] == '\n') 398 *--p = '\0'; 399 } 400 401 /* see if there should be a continuation line */ 402 c = fgetc(af); 403 if (!feof(af)) 404 (void) ungetc(c, af); 405 if (c != ' ' && c != '\t') 406 break; 407 408 /* read continuation line */ 409 if (fgets(p, sizeof line - (p - line), af) == NULL) 410 break; 411 LineNumber++; 412 } 413 if (al.q_mailer != LocalMailer) 414 { 415 syserr("cannot alias non-local names"); 416 continue; 417 } 418 419 /* 420 ** Insert alias into symbol table or DBM file 421 */ 422 423 lhssize = strlen(al.q_user) + 1; 424 rhssize = strlen(rhs) + 1; 425 426 # ifdef DBM 427 if (init) 428 { 429 DATUM key, content; 430 431 key.dsize = lhssize; 432 key.dptr = al.q_user; 433 content.dsize = rhssize; 434 content.dptr = rhs; 435 store(key, content); 436 } 437 else 438 # endif DBM 439 { 440 s = stab(al.q_user, ST_ALIAS, ST_ENTER); 441 s->s_alias = newstr(rhs); 442 } 443 444 /* statistics */ 445 naliases++; 446 bytes += lhssize + rhssize; 447 if (rhssize > longest) 448 longest = rhssize; 449 } 450 (void) fclose(af); 451 CurEnv->e_to = NULL; 452 FileName = NULL; 453 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 454 naliases, longest, bytes); 455 } 456 /* 457 ** FORWARD -- Try to forward mail 458 ** 459 ** This is similar but not identical to aliasing. 460 ** 461 ** Parameters: 462 ** user -- the name of the user who's mail we would like 463 ** to forward to. It must have been verified -- 464 ** i.e., the q_home field must have been filled 465 ** in. 466 ** sendq -- a pointer to the head of the send queue to 467 ** put this user's aliases in. 468 ** 469 ** Returns: 470 ** none. 471 ** 472 ** Side Effects: 473 ** New names are added to send queues. 474 */ 475 476 forward(user, sendq) 477 ADDRESS *user; 478 ADDRESS **sendq; 479 { 480 char buf[60]; 481 extern bool safefile(); 482 483 # ifdef DEBUG 484 if (tTd(27, 1)) 485 printf("forward(%s)\n", user->q_paddr); 486 # endif DEBUG 487 488 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 489 return; 490 # ifdef DEBUG 491 if (user->q_home == NULL) 492 syserr("forward: no home"); 493 # endif DEBUG 494 495 /* good address -- look for .forward file in home */ 496 define('z', user->q_home, CurEnv); 497 expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 498 if (!safefile(buf, user->q_uid, S_IREAD)) 499 return; 500 501 /* we do have an address to forward to -- do it */ 502 include(buf, "forwarding", user, sendq); 503 } 504