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