note that _getshort used by sendmail
[unix-history] / usr / src / usr.sbin / sendmail / src / alias.c
CommitLineData
d185cb11
DF
1/*
2** Sendmail
3** Copyright (c) 1983 Eric P. Allman
4** Berkeley, California
5**
6** Copyright (c) 1983 Regents of the University of California.
7** All rights reserved. The Berkeley software License Agreement
8** specifies the terms and conditions for redistribution.
9*/
10
b3cbe40f 11# include <pwd.h>
9c3f729b
EA
12# include <sys/types.h>
13# include <sys/stat.h>
7bdc419c 14# include <signal.h>
e6e345fa 15# include <errno.h>
96faada8 16# include "sendmail.h"
e6e345fa
EA
17# ifdef FLOCK
18# include <sys/file.h>
19# endif FLOCK
b3cbe40f 20
1cc8903f
EA
21#ifndef lint
22# ifdef DBM
a78155b0 23static char SccsId[] = "@(#)alias.c 5.13 (Berkeley) %G% (with DBM)";
1cc8903f 24# else DBM
a78155b0 25static char SccsId[] = "@(#)alias.c 5.13 (Berkeley) %G% (without DBM)";
1cc8903f
EA
26# endif DBM
27#endif not lint
28
916b3375 29
b3cbe40f
EA
30/*
31** ALIAS -- Compute aliases.
32**
7338e3d4
EA
33** Scans the alias file for an alias for the given address.
34** If found, it arranges to deliver to the alias list instead.
35** Uses libdbm database if -DDBM.
b3cbe40f
EA
36**
37** Parameters:
bdfaa762 38** a -- address to alias.
d4f42161
EA
39** sendq -- a pointer to the head of the send queue
40** to put the aliases in.
b3cbe40f
EA
41**
42** Returns:
43** none
44**
45** Side Effects:
9e3c0a28 46** Aliases found are expanded.
b3cbe40f 47**
b3cbe40f
EA
48** Notes:
49** If NoAlias (the "-n" flag) is set, no aliasing is
50** done.
51**
52** Deficiencies:
53** It should complain about names that are aliased to
54** nothing.
b3cbe40f
EA
55*/
56
57
c9b9c7a2 58#ifdef DBM
a530c75f
EA
59typedef struct
60{
61 char *dptr;
f4dbf345
EA
62 int dsize;
63} DATUM;
f4dbf345 64extern DATUM fetch();
c9b9c7a2 65#endif DBM
b3cbe40f 66
d4f42161 67alias(a, sendq)
bdfaa762 68 register ADDRESS *a;
d4f42161 69 ADDRESS **sendq;
b3cbe40f 70{
29871fef 71 register char *p;
abae7b2d 72 extern ADDRESS *sendto();
35cc3fad 73 extern char *aliaslookup();
b3cbe40f 74
b3cbe40f 75# ifdef DEBUG
9678c96d 76 if (tTd(27, 1))
cdb17311 77 printf("alias(%s)\n", a->q_paddr);
b3cbe40f
EA
78# endif
79
cdb17311
EA
80 /* don't realias already aliased names */
81 if (bitset(QDONTSEND, a->q_flags))
82 return;
83
2654b031 84 CurEnv->e_to = a->q_paddr;
cdb17311 85
74c5fe7c
EA
86 /*
87 ** Look up this name
88 */
89
2e3062fe
EA
90 if (NoAlias)
91 p = NULL;
92 else
93 p = aliaslookup(a->q_user);
cdb17311
EA
94 if (p == NULL)
95 return;
b3cbe40f 96
d916f0ca 97 /*
cdb17311
EA
98 ** Match on Alias.
99 ** Deliver to the target list.
d916f0ca
EA
100 */
101
cdb17311 102# ifdef DEBUG
9678c96d 103 if (tTd(27, 1))
cdb17311
EA
104 printf("%s (%s, %s) aliased to %s\n",
105 a->q_paddr, a->q_host, a->q_user, p);
106# endif
91f69adf 107 message(Arpa_Info, "aliased to %s", p);
cdb17311 108 AliasLevel++;
abae7b2d 109 a->q_child = sendto(p, 1, a, 0);
cdb17311
EA
110 AliasLevel--;
111}
112\f/*
35cc3fad
EA
113** ALIASLOOKUP -- look up a name in the alias file.
114**
115** Parameters:
116** name -- the name to look up.
117**
118** Returns:
119** the value of name.
120** NULL if unknown.
121**
122** Side Effects:
123** none.
124**
125** Warnings:
126** The return value will be trashed across calls.
127*/
128
129char *
130aliaslookup(name)
131 char *name;
132{
133# ifdef DBM
134 DATUM rhs, lhs;
135
136 /* create a key for fetch */
137 lhs.dptr = name;
138 lhs.dsize = strlen(name) + 1;
139 rhs = fetch(lhs);
140 return (rhs.dptr);
141# else DBM
142 register STAB *s;
143
144 s = stab(name, ST_ALIAS, ST_FIND);
145 if (s == NULL)
146 return (NULL);
147 return (s->s_alias);
148# endif DBM
149}
150\f/*
cdb17311
EA
151** INITALIASES -- initialize for aliasing
152**
153** Very different depending on whether we are running DBM or not.
154**
155** Parameters:
156** aliasfile -- location of aliases.
f4dbf345 157** init -- if set and if DBM, initialize the DBM files.
cdb17311
EA
158**
159** Returns:
160** none.
161**
162** Side Effects:
163** initializes aliases:
164** if DBM: opens the database.
165** if ~DBM: reads the aliases into the symbol table.
166*/
167
f4dbf345
EA
168# define DBMMODE 0666
169
170initaliases(aliasfile, init)
cdb17311 171 char *aliasfile;
f4dbf345 172 bool init;
cdb17311 173{
7338e3d4 174#ifdef DBM
7bdc419c 175 int atcnt;
d6b27179 176 time_t modtime;
9327783c
EA
177 bool automatic = FALSE;
178 char buf[MAXNAME];
7338e3d4
EA
179#endif DBM
180 struct stat stb;
a78155b0
EA
181 static bool initialized = FALSE;
182
183 if (initialized)
184 return;
185 initialized = TRUE;
d6b27179 186
bb09c502 187 if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
7bdc419c 188 {
9327783c
EA
189 if (aliasfile != NULL && init)
190 syserr("Cannot open %s", aliasfile);
7bdc419c 191 NoAlias = TRUE;
70faa7c8 192 errno = 0;
7bdc419c
EA
193 return;
194 }
195
d5fe2834 196# ifdef DBM
7bdc419c
EA
197 /*
198 ** Check to see that the alias file is complete.
199 ** If not, we will assume that someone died, and it is up
200 ** to us to rebuild it.
201 */
202
a1a07282
EA
203 if (!init)
204 dbminit(aliasfile);
560a80d9
EA
205 atcnt = SafeAlias * 2;
206 if (atcnt > 0)
207 {
208 while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
a1a07282
EA
209 {
210 /*
211 ** Reinitialize alias file in case the new
212 ** one is mv'ed in instead of cp'ed in.
213 **
214 ** Only works with new DBM -- old one will
215 ** just consume file descriptors forever.
216 ** If you have a dbmclose() it can be
217 ** added before the sleep(30).
218 */
219
560a80d9 220 sleep(30);
a1a07282
EA
221# ifdef NDBM
222 dbminit(aliasfile);
223# endif NDBM
224 }
560a80d9
EA
225 }
226 else
227 atcnt = 1;
7bdc419c 228
d6b27179
EA
229 /*
230 ** See if the DBM version of the file is out of date with
231 ** the text version. If so, go into 'init' mode automatically.
232 ** This only happens if our effective userid owns the DBM
f9ce44de 233 ** version or if the mode of the database is 666 -- this
d6b27179
EA
234 ** is an attempt to avoid protection problems. Note the
235 ** unpalatable hack to see if the stat succeeded.
236 */
237
d6b27179
EA
238 modtime = stb.st_mtime;
239 (void) strcpy(buf, aliasfile);
240 (void) strcat(buf, ".pag");
241 stb.st_ino = 0;
f12e5a74 242 if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
f4dbf345 243 {
70faa7c8 244 errno = 0;
4871a357 245 if (AutoRebuild && stb.st_ino != 0 &&
7338e3d4 246 ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid()))
d6b27179
EA
247 {
248 init = TRUE;
9327783c 249 automatic = TRUE;
91f69adf 250 message(Arpa_Info, "rebuilding alias database");
2e3062fe
EA
251#ifdef LOG
252 if (LogLevel >= 7)
253 syslog(LOG_INFO, "rebuilding alias database");
254#endif LOG
d6b27179
EA
255 }
256 else
257 {
f12e5a74 258#ifdef LOG
2e3062fe
EA
259 if (LogLevel >= 7)
260 syslog(LOG_INFO, "alias database out of date");
f12e5a74 261#endif LOG
d6b27179
EA
262 message(Arpa_Info, "Warning: alias database out of date");
263 }
264 }
f4dbf345 265
d6b27179
EA
266
267 /*
7bdc419c 268 ** If necessary, load the DBM file.
d6b27179
EA
269 ** If running without DBM, load the symbol table.
270 */
271
f4dbf345 272 if (init)
7bdc419c 273 {
9327783c
EA
274#ifdef LOG
275 if (LogLevel >= 6)
276 {
277 extern char *username();
278
279 syslog(LOG_NOTICE, "alias database %srebuilt by %s",
280 automatic ? "auto" : "", username());
281 }
282#endif LOG
f4dbf345 283 readaliases(aliasfile, TRUE);
7bdc419c 284 }
cdb17311 285# else DBM
f4dbf345
EA
286 readaliases(aliasfile, init);
287# endif DBM
288}
289\f/*
290** READALIASES -- read and process the alias file.
291**
292** This routine implements the part of initaliases that occurs
293** when we are not going to use the DBM stuff.
294**
295** Parameters:
296** aliasfile -- the pathname of the alias file master.
297** init -- if set, initialize the DBM stuff.
298**
299** Returns:
300** none.
301**
302** Side Effects:
303** Reads aliasfile into the symbol table.
304** Optionally, builds the .dir & .pag files.
305*/
306
307static
308readaliases(aliasfile, init)
309 char *aliasfile;
310 bool init;
311{
cdb17311 312 register char *p;
41173b8f 313 char *lhs;
cdb17311
EA
314 char *rhs;
315 bool skipping;
7338e3d4 316 int naliases, bytes, longest;
9e25d1f0 317 FILE *af;
e6e345fa 318 int (*oldsigint)();
7338e3d4 319 ADDRESS al, bl;
9e25d1f0 320 register STAB *s;
7338e3d4 321 char line[BUFSIZ];
cdb17311
EA
322
323 if ((af = fopen(aliasfile, "r")) == NULL)
d916f0ca 324 {
cdb17311 325# ifdef DEBUG
9678c96d 326 if (tTd(27, 1))
9e25d1f0 327 printf("Can't open %s\n", aliasfile);
cdb17311
EA
328# endif
329 errno = 0;
330 NoAlias++;
331 return;
332 }
74c5fe7c 333
e6e345fa
EA
334# ifdef DBM
335# ifdef FLOCK
336 /* see if someone else is rebuilding the alias file already */
337 if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
338 {
339 /* yes, they are -- wait until done and then return */
340 message(Arpa_Info, "Alias file is already being rebuilt");
341 if (OpMode != MD_INITALIAS)
342 {
343 /* wait for other rebuild to complete */
344 (void) flock(fileno(af), LOCK_EX);
345 }
03388044 346 (void) fclose(af);
e6e345fa
EA
347 errno = 0;
348 return;
349 }
350# endif FLOCK
351# endif DBM
352
353 /*
354 ** If initializing, create the new DBM files.
355 */
356
357 if (init)
358 {
359 oldsigint = signal(SIGINT, SIG_IGN);
360 (void) strcpy(line, aliasfile);
361 (void) strcat(line, ".dir");
362 if (close(creat(line, DBMMODE)) < 0)
363 {
364 syserr("cannot make %s", line);
365 (void) signal(SIGINT, oldsigint);
366 return;
367 }
368 (void) strcpy(line, aliasfile);
369 (void) strcat(line, ".pag");
370 if (close(creat(line, DBMMODE)) < 0)
371 {
372 syserr("cannot make %s", line);
373 (void) signal(SIGINT, oldsigint);
374 return;
375 }
1eff167f 376 dbminit(aliasfile);
e6e345fa
EA
377 }
378
74c5fe7c
EA
379 /*
380 ** Read and interpret lines
381 */
382
7338e3d4
EA
383 FileName = aliasfile;
384 LineNumber = 0;
d6b27179 385 naliases = bytes = longest = 0;
cdb17311
EA
386 skipping = FALSE;
387 while (fgets(line, sizeof (line), af) != NULL)
388 {
d6b27179
EA
389 int lhssize, rhssize;
390
7338e3d4 391 LineNumber++;
3312f93c
EA
392 p = index(line, '\n');
393 if (p != NULL)
394 *p = '\0';
cdb17311
EA
395 switch (line[0])
396 {
397 case '#':
cdb17311
EA
398 case '\0':
399 skipping = FALSE;
400 continue;
bdfaa762 401
cdb17311
EA
402 case ' ':
403 case '\t':
404 if (!skipping)
7338e3d4 405 syserr("Non-continuation line starts with space");
cdb17311 406 skipping = TRUE;
bdfaa762 407 continue;
cdb17311
EA
408 }
409 skipping = FALSE;
bdfaa762 410
74c5fe7c
EA
411 /*
412 ** Process the LHS
41173b8f 413 **
74c5fe7c 414 ** Find the final colon, and parse the address.
41173b8f
EA
415 ** It should resolve to a local name.
416 **
417 ** Alternatively, it can be "@hostname" for host
418 ** aliases -- all we do here is map case. Hostname
419 ** need not be a single token.
74c5fe7c
EA
420 */
421
cdb17311 422 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
bdfaa762 423 continue;
41173b8f 424 if (*p != ':')
cdb17311 425 {
1bcdf0a2 426 syserr("%s, line %d: syntax error", aliasfile, lineno);
bdfaa762 427 continue;
cdb17311 428 }
41173b8f
EA
429 *p++ = '\0';
430 if (line[0] == '@')
cdb17311 431 {
41173b8f
EA
432 /* a host alias */
433 makelower(line);
434 lhs = line;
435 }
436 else
437 {
438 /* a user alias */
439 if (parseaddr(line, &al, 1, ':') == NULL)
440 {
441 syserr("illegal alias name");
442 continue;
443 }
444 loweraddr(&al);
445 if (al.q_mailer != LocalMailer)
446 {
447 syserr("cannot alias non-local names");
448 continue;
449 }
450 lhs = al.q_user;
cdb17311 451 }
74c5fe7c
EA
452
453 /*
454 ** Process the RHS.
455 ** 'al' is the internal form of the LHS address.
456 ** 'p' points to the text of the RHS.
1bcdf0a2
EA
457 ** 'p' may begin with a colon (i.e., the
458 ** separator was "::") which will use the
459 ** first address as the person to send
460 ** errors to -- i.e., designates the
461 ** list maintainer.
74c5fe7c
EA
462 */
463
1bcdf0a2
EA
464 if (*p == ':')
465 {
466 ADDRESS *maint;
467
468 while (isspace(*++p))
469 continue;
470 rhs = p;
471 while (*p != '\0' && *p != ',')
472 p++;
473 if (*p != ',')
474 goto syntaxerr;
475 *p++ = '\0';
476 maint = parse(p, (ADDRESS *) NULL, 1);
477 if (maint == NULL)
478 syserr("Illegal list maintainer for list %s", al.q_user);
479 else if (CurEnv->e_returnto == &CurEnv->e_from)
480 {
481 CurEnv->e_returnto = maint;
482 MailBack++;
483 }
484 }
485
cdb17311 486 rhs = p;
cdb17311
EA
487 for (;;)
488 {
489 register char c;
d916f0ca 490
1e963db5 491 if (init && CheckAliases)
cdb17311 492 {
f4dbf345 493 /* do parsing & compression of addresses */
3312f93c 494 while (*p != '\0')
cdb17311 495 {
3312f93c
EA
496 extern char *DelimChar;
497
498 while (isspace(*p) || *p == ',')
c49d661c 499 p++;
3312f93c
EA
500 if (*p == '\0')
501 break;
502 if (parseaddr(p, &bl, -1, ',') == NULL)
503 usrerr("%s... bad address", p);
504 p = DelimChar;
cdb17311 505 }
cdb17311 506 }
f4dbf345 507 else
634c8ff3 508 {
34fe0a9b
EA
509 p = &p[strlen(p)];
510 if (p[-1] == '\n')
511 *--p = '\0';
634c8ff3 512 }
d916f0ca 513
cdb17311 514 /* see if there should be a continuation line */
9e25d1f0
EA
515 c = fgetc(af);
516 if (!feof(af))
74c5fe7c 517 (void) ungetc(c, af);
9e25d1f0 518 if (c != ' ' && c != '\t')
cdb17311
EA
519 break;
520
521 /* read continuation line */
cdb17311
EA
522 if (fgets(p, sizeof line - (p - line), af) == NULL)
523 break;
7338e3d4 524 LineNumber++;
cdb17311 525 }
74c5fe7c
EA
526
527 /*
528 ** Insert alias into symbol table or DBM file
529 */
530
41173b8f 531 lhssize = strlen(lhs) + 1;
d6b27179
EA
532 rhssize = strlen(rhs) + 1;
533
f4dbf345
EA
534# ifdef DBM
535 if (init)
536 {
537 DATUM key, content;
538
d6b27179 539 key.dsize = lhssize;
f4dbf345 540 key.dptr = al.q_user;
d6b27179 541 content.dsize = rhssize;
f4dbf345
EA
542 content.dptr = rhs;
543 store(key, content);
544 }
545 else
546# endif DBM
547 {
548 s = stab(al.q_user, ST_ALIAS, ST_ENTER);
549 s->s_alias = newstr(rhs);
550 }
d6b27179
EA
551
552 /* statistics */
553 naliases++;
554 bytes += lhssize + rhssize;
555 if (rhssize > longest)
556 longest = rhssize;
d916f0ca 557 }
e6e345fa
EA
558
559# ifdef DBM
560 if (init)
561 {
562 /* add the distinquished alias "@" */
563 DATUM key;
564
565 key.dsize = 2;
566 key.dptr = "@";
567 store(key, key);
568
569 /* restore the old signal */
570 (void) signal(SIGINT, oldsigint);
571 }
572# endif DBM
573
574 /* closing the alias file drops the lock */
cdb17311 575 (void) fclose(af);
2654b031 576 CurEnv->e_to = NULL;
7338e3d4 577 FileName = NULL;
91f69adf 578 message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
d6b27179 579 naliases, longest, bytes);
2e3062fe
EA
580# ifdef LOG
581 if (LogLevel >= 8)
582 syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
583 naliases, longest, bytes);
584# endif LOG
b3cbe40f
EA
585}
586\f/*
587** FORWARD -- Try to forward mail
588**
589** This is similar but not identical to aliasing.
590**
b3cbe40f 591** Parameters:
74c5fe7c
EA
592** user -- the name of the user who's mail we would like
593** to forward to. It must have been verified --
594** i.e., the q_home field must have been filled
595** in.
d4f42161
EA
596** sendq -- a pointer to the head of the send queue to
597** put this user's aliases in.
b3cbe40f
EA
598**
599** Returns:
cdb17311 600** none.
b3cbe40f
EA
601**
602** Side Effects:
9e3c0a28 603** New names are added to send queues.
b3cbe40f
EA
604*/
605
d4f42161 606forward(user, sendq)
a530c75f 607 ADDRESS *user;
d4f42161 608 ADDRESS **sendq;
b3cbe40f 609{
4bb4503e 610 char buf[60];
f6a0cc15 611 extern bool safefile();
56678245 612
cdb17311 613# ifdef DEBUG
9678c96d 614 if (tTd(27, 1))
cdb17311
EA
615 printf("forward(%s)\n", user->q_paddr);
616# endif DEBUG
617
179c1218 618 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
cdb17311 619 return;
74c5fe7c
EA
620# ifdef DEBUG
621 if (user->q_home == NULL)
622 syserr("forward: no home");
623# endif DEBUG
56678245 624
56678245 625 /* good address -- look for .forward file in home */
7338e3d4 626 define('z', user->q_home, CurEnv);
a73ae8ac 627 expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
f6a0cc15 628 if (!safefile(buf, user->q_uid, S_IREAD))
cdb17311 629 return;
56678245
EA
630
631 /* we do have an address to forward to -- do it */
d4f42161 632 include(buf, "forwarding", user, sendq);
b3cbe40f 633}
41173b8f
EA
634\f/*
635** MAPHOST -- given a host description, produce a mapping.
636**
637** This is done by looking up the name in the alias file,
638** preceeded by an "@". This can be used for UUCP mapping.
639** For example, a call with {blia, ., UUCP} as arguments
640** might return {ucsfcgl, !, blia, ., UUCP} as the result.
641**
642** We first break the input into three parts -- before the
643** lookup, the lookup itself, and after the lookup. We
644** then do the lookup, concatenate them together, and rescan
645** the result.
646**
647** Parameters:
648** pvp -- the parameter vector to map.
649**
650** Returns:
651** The result of the mapping. If nothing found, it
652** should just concatenate the three parts together and
653** return that.
654**
655** Side Effects:
656** none.
657*/
658
659char **
660maphost(pvp)
661 char **pvp;
662{
663 register char **avp;
664 register char **bvp;
665 char *p;
666 char buf1[MAXNAME];
667 char buf2[MAXNAME];
668 char buf3[MAXNAME];
669 extern char **prescan();
670
671 /*
672 ** Extract the three parts of the input as strings.
673 */
674
675 /* find the part before the lookup */
676 for (bvp = pvp; *bvp != NULL && **bvp != MATCHLOOKUP; bvp++)
677 continue;
678 if (*bvp == NULL)
679 return (pvp);
680 p = *bvp;
681 *bvp = NULL;
682 cataddr(pvp, buf1, sizeof buf1);
683 *bvp++ = p;
684
685 /* find the rest of the lookup */
686 for (avp = bvp; *pvp != NULL && **bvp != MATCHELOOKUP; bvp++)
687 continue;
688 if (*bvp == NULL)
689 return (pvp);
690 p = *bvp;
691 *bvp = NULL;
692 cataddr(avp, buf2, sizeof buf2);
693 *bvp++ = p;
694
695 /* save the part after the lookup */
696 cataddr(bvp, buf3, sizeof buf3);
697
698 /*
699 ** Now look up the middle part.
700 */
701
702 p = aliaslookup(buf2);
703 if (p != NULL)
704 strcpy(buf2, p);
705
706 /*
707 ** Put the three parts back together and break into tokens.
708 */
709
710 strcat(buf1, buf2);
711 strcat(buf1, buf3);
712 avp = prescan(buf1, '\0');
713
714 /* return this mapping */
715 return (avp);
716}