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