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