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