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