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.42 11/28/82 (with DBM)); 9 # else DBM 10 SCCSID(@(#)alias.c 3.42 11/28/82 (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 sendto(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 (stat(aliasfile, &stb) < 0) 164 { 165 NoAlias = TRUE; 166 return; 167 } 168 169 # ifdef DBM 170 /* 171 ** Check to see that the alias file is complete. 172 ** If not, we will assume that someone died, and it is up 173 ** to us to rebuild it. 174 */ 175 176 dbminit(aliasfile); 177 atcnt = 10; 178 while (SafeAlias && !init && atcnt-- >= 0 && aliaslookup("@") == NULL) 179 sleep(30); 180 181 /* 182 ** See if the DBM version of the file is out of date with 183 ** the text version. If so, go into 'init' mode automatically. 184 ** This only happens if our effective userid owns the DBM 185 ** version or if the mode of the database is 666 -- this 186 ** is an attempt to avoid protection problems. Note the 187 ** unpalatable hack to see if the stat succeeded. 188 */ 189 190 modtime = stb.st_mtime; 191 (void) strcpy(buf, aliasfile); 192 (void) strcat(buf, ".pag"); 193 stb.st_ino = 0; 194 if (!init && (atcnt < 0 || stat(buf, &stb) < 0 || stb.st_mtime < modtime)) 195 { 196 if (AutoRebuild && stb.st_ino != 0 && 197 ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid())) 198 { 199 init = TRUE; 200 message(Arpa_Info, "rebuilding alias database"); 201 } 202 else 203 { 204 bool oldverb = Verbose; 205 206 Verbose = TRUE; 207 message(Arpa_Info, "Warning: alias database out of date"); 208 Verbose = oldverb; 209 } 210 } 211 212 /* 213 ** If initializing, create the new files. 214 ** We should lock the alias file here to prevent other 215 ** instantiations of sendmail from reading an incomplete 216 ** file -- or worse yet, doing a concurrent initialize. 217 */ 218 219 if (init) 220 { 221 oldsigint = signal(SIGINT, SIG_IGN); 222 (void) strcpy(buf, aliasfile); 223 (void) strcat(buf, ".dir"); 224 if (close(creat(buf, DBMMODE)) < 0) 225 { 226 syserr("cannot make %s", buf); 227 (void) signal(SIGINT, oldsigint); 228 return; 229 } 230 (void) strcpy(buf, aliasfile); 231 (void) strcat(buf, ".pag"); 232 if (close(creat(buf, DBMMODE)) < 0) 233 { 234 syserr("cannot make %s", buf); 235 (void) signal(SIGINT, oldsigint); 236 return; 237 } 238 } 239 240 /* 241 ** If necessary, load the DBM file. 242 ** If running without DBM, load the symbol table. 243 ** After loading the DBM file, add the distinquished alias "@". 244 */ 245 246 if (init) 247 { 248 DATUM key; 249 250 readaliases(aliasfile, TRUE); 251 key.dsize = 2; 252 key.dptr = "@"; 253 store(key, key); 254 (void) signal(SIGINT, oldsigint); 255 } 256 # else DBM 257 readaliases(aliasfile, init); 258 # endif DBM 259 } 260 /* 261 ** READALIASES -- read and process the alias file. 262 ** 263 ** This routine implements the part of initaliases that occurs 264 ** when we are not going to use the DBM stuff. 265 ** 266 ** Parameters: 267 ** aliasfile -- the pathname of the alias file master. 268 ** init -- if set, initialize the DBM stuff. 269 ** 270 ** Returns: 271 ** none. 272 ** 273 ** Side Effects: 274 ** Reads aliasfile into the symbol table. 275 ** Optionally, builds the .dir & .pag files. 276 */ 277 278 static 279 readaliases(aliasfile, init) 280 char *aliasfile; 281 bool init; 282 { 283 register char *p; 284 char *p2; 285 char *rhs; 286 bool skipping; 287 int naliases, bytes, longest; 288 FILE *af; 289 ADDRESS al, bl; 290 register STAB *s; 291 char line[BUFSIZ]; 292 293 if ((af = fopen(aliasfile, "r")) == NULL) 294 { 295 # ifdef DEBUG 296 if (tTd(27, 1)) 297 printf("Can't open %s\n", aliasfile); 298 # endif 299 errno = 0; 300 NoAlias++; 301 return; 302 } 303 304 /* 305 ** Read and interpret lines 306 */ 307 308 FileName = aliasfile; 309 LineNumber = 0; 310 naliases = bytes = longest = 0; 311 skipping = FALSE; 312 while (fgets(line, sizeof (line), af) != NULL) 313 { 314 int lhssize, rhssize; 315 316 LineNumber++; 317 switch (line[0]) 318 { 319 case '#': 320 case '\n': 321 case '\0': 322 skipping = FALSE; 323 continue; 324 325 case ' ': 326 case '\t': 327 if (!skipping) 328 syserr("Non-continuation line starts with space"); 329 skipping = TRUE; 330 continue; 331 } 332 skipping = FALSE; 333 334 /* 335 ** Process the LHS 336 ** Find the final colon, and parse the address. 337 ** It should resolve to a local name -- this will 338 ** be checked later (we want to optionally do 339 ** parsing of the RHS first to maximize error 340 ** detection). 341 */ 342 343 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 344 continue; 345 if (*p == '\0' || *p == '\n') 346 { 347 syserr("missing colon"); 348 continue; 349 } 350 *p++ = '\0'; 351 if (parse(line, &al, 1) == NULL) 352 { 353 *--p = ':'; 354 syserr("illegal alias name"); 355 continue; 356 } 357 358 /* 359 ** Process the RHS. 360 ** 'al' is the internal form of the LHS address. 361 ** 'p' points to the text of the RHS. 362 */ 363 364 rhs = p; 365 for (;;) 366 { 367 register char c; 368 369 if (init) 370 { 371 /* do parsing & compression of addresses */ 372 c = *p; 373 while (c != '\0') 374 { 375 p2 = p; 376 while (*p != '\n' && *p != ',' && *p != '\0') 377 p++; 378 c = *p; 379 *p++ = '\0'; 380 if (c == '\n') 381 c = '\0'; 382 if (*p2 == '\0') 383 { 384 p[-1] = c; 385 continue; 386 } 387 (void) parse(p2, &bl, -1); 388 p[-1] = c; 389 while (isspace(*p)) 390 p++; 391 } 392 } 393 else 394 p = &p[strlen(p)]; 395 396 /* see if there should be a continuation line */ 397 c = fgetc(af); 398 if (!feof(af)) 399 (void) ungetc(c, af); 400 if (c != ' ' && c != '\t') 401 break; 402 403 /* read continuation line */ 404 p--; 405 if (fgets(p, sizeof line - (p - line), af) == NULL) 406 break; 407 LineNumber++; 408 } 409 if (al.q_mailer != LocalMailer) 410 { 411 syserr("cannot alias non-local names"); 412 continue; 413 } 414 415 /* 416 ** Insert alias into symbol table or DBM file 417 */ 418 419 lhssize = strlen(al.q_user) + 1; 420 rhssize = strlen(rhs) + 1; 421 422 # ifdef DBM 423 if (init) 424 { 425 DATUM key, content; 426 427 key.dsize = lhssize; 428 key.dptr = al.q_user; 429 content.dsize = rhssize; 430 content.dptr = rhs; 431 store(key, content); 432 } 433 else 434 # endif DBM 435 { 436 s = stab(al.q_user, ST_ALIAS, ST_ENTER); 437 s->s_alias = newstr(rhs); 438 } 439 440 /* statistics */ 441 naliases++; 442 bytes += lhssize + rhssize; 443 if (rhssize > longest) 444 longest = rhssize; 445 } 446 (void) fclose(af); 447 CurEnv->e_to = NULL; 448 FileName = NULL; 449 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 450 naliases, longest, bytes); 451 } 452 /* 453 ** FORWARD -- Try to forward mail 454 ** 455 ** This is similar but not identical to aliasing. 456 ** 457 ** Parameters: 458 ** user -- the name of the user who's mail we would like 459 ** to forward to. It must have been verified -- 460 ** i.e., the q_home field must have been filled 461 ** in. 462 ** sendq -- a pointer to the head of the send queue to 463 ** put this user's aliases in. 464 ** 465 ** Returns: 466 ** none. 467 ** 468 ** Side Effects: 469 ** New names are added to send queues. 470 */ 471 472 forward(user, sendq) 473 ADDRESS *user; 474 ADDRESS **sendq; 475 { 476 char buf[60]; 477 extern bool safefile(); 478 479 # ifdef DEBUG 480 if (tTd(27, 1)) 481 printf("forward(%s)\n", user->q_paddr); 482 # endif DEBUG 483 484 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 485 return; 486 # ifdef DEBUG 487 if (user->q_home == NULL) 488 syserr("forward: no home"); 489 # endif DEBUG 490 491 /* good address -- look for .forward file in home */ 492 define('z', user->q_home, CurEnv); 493 expand("$z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 494 if (!safefile(buf, user->q_uid, S_IREAD)) 495 return; 496 497 /* we do have an address to forward to -- do it */ 498 include(buf, "forwarding", user, sendq); 499 } 500