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