* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
static char sccsid
[] = "@(#)alias.c 8.3 (Berkeley) 7/13/93";
MAP
*AliasDB
[MAXALIASDB
+ 1]; /* actual database list */
int NAliasDBs
; /* number of alias databases */
** ALIAS -- Compute aliases.
** Scans the alias file for an alias for the given address.
** If found, it arranges to deliver to the alias list instead.
** Uses libdbm database if -DDBM.
** a -- address to alias.
** sendq -- a pointer to the head of the send queue
** to put the aliases in.
** e -- the current envelope.
** Aliases found are expanded.
** It should complain about names that are aliased to
extern char *aliaslookup();
printf("alias(%s)\n", a
->q_paddr
);
/* don't realias already aliased names */
if (bitset(QDONTSEND
|QBADADDR
|QVERIFIED
, a
->q_flags
))
p
= aliaslookup(a
->q_user
, e
);
** Deliver to the target list.
printf("%s (%s, %s) aliased to %s\n",
a
->q_paddr
, a
->q_host
, a
->q_user
, p
);
if (bitset(EF_VRFYONLY
, e
->e_flags
))
message("aliased to %s", p
);
syslog(LOG_INFO
, "%s: alias %s => %s", e
->e_id
, a
->q_paddr
, p
);
naliases
= sendtolist(p
, a
, sendq
, e
);
if (naliases
> 0 && !bitset(QSELFREF
, a
->q_flags
))
printf("alias: QDONTSEND ");
** Look for owner of alias
(void) strcpy(obuf
, "owner-");
if (strncmp(a
->q_user
, "owner-", 6) == 0)
(void) strcat(obuf
, "owner");
(void) strcat(obuf
, a
->q_user
);
if (!bitnset(M_USR_UPPER
, a
->q_mailer
->m_flags
))
owner
= aliaslookup(obuf
, e
);
if (strchr(owner
, ',') != NULL
)
a
->q_owner
= newstr(owner
);
** ALIASLOOKUP -- look up a name in the alias file.
** name -- the name to look up.
** The return value will be trashed across calls.
for (dbno
= 0; dbno
< NAliasDBs
; dbno
++)
if (!bitset(MF_OPEN
, map
->map_mflags
))
p
= (*map
->map_class
->map_lookup
)(map
, name
, NULL
, &stat
);
** SETALIAS -- set up an alias map
** Called when reading configuration file.
** spec -- the alias specification
printf("setalias(%s)\n", spec
);
for (p
= spec
; p
!= NULL
; )
if (NAliasDBs
>= MAXALIASDB
)
syserr("Too many alias databases defined, %d max", MAXALIASDB
);
(void) sprintf(aname
, "Alias%d", NAliasDBs
);
s
= stab(aname
, ST_MAP
, ST_ENTER
);
AliasDB
[NAliasDBs
] = map
;
if (p
!= NULL
&& *p
== ':')
map
->map_mflags
= MF_OPTIONAL
|MF_INCLNULL
;
s
= stab(class, ST_MAPCLASS
, ST_FIND
);
printf("Unknown alias class %s\n", class);
else if (!bitset(MCF_ALIASOK
, s
->s_mapclass
.map_cflags
))
syserr("setalias: map class %s can't handle aliases",
map
->map_class
= &s
->s_mapclass
;
if (map
->map_class
->map_parse(map
, spec
))
map
->map_mflags
|= MF_VALID
|MF_ALIAS
;
** ALIASWAIT -- wait for distinguished @:@ token to appear.
** This can decide to reopen or rebuild the alias file
printf("aliaswait(%s:%s)\n",
map
->map_class
->map_cname
, map
->map_file
);
map
->map_class
->map_lookup(map
, "@", NULL
, &st
) == NULL
)
** Close and re-open the alias database in case
** the one is mv'ed instead of cp'ed in.
printf("aliaswait: sleeping\n");
map
->map_class
->map_close(map
);
map
->map_class
->map_open(map
, O_RDONLY
);
/* see if we need to go into auto-rebuild mode */
if (!bitset(MCF_REBUILDABLE
, map
->map_class
->map_cflags
))
printf("aliaswait: not rebuildable\n");
if (stat(map
->map_file
, &stb
) < 0)
printf("aliaswait: no source file\n");
(void) strcpy(buf
, map
->map_file
);
if (stat(buf
, &stb
) < 0 || stb
.st_mtime
< mtime
|| atcnt
< 0)
/* database is out of date */
if (AutoRebuild
&& stb
.st_ino
!= 0 && stb
.st_uid
== geteuid())
message("auto-rebuilding alias database %s", buf
);
rebuildaliases(map
, TRUE
);
syslog(LOG_INFO
, "alias database %s out of date",
message("Warning: alias database %s out of date", buf
);
** REBUILDALIASES -- rebuild the alias database.
** map -- the database to rebuild.
** automatic -- set if this was automatically generated.
** Reads the text version of the database, builds the
rebuildaliases(map
, automatic
)
if (!bitset(MCF_REBUILDABLE
, map
->map_class
->map_cflags
))
syslog(LOG_NOTICE
, "alias database %s %srebuilt by %s",
map
->map_file
, automatic
? "auto" : "", username());
/* try to lock the source file */
if ((af
= fopen(map
->map_file
, "r+")) == NULL
)
printf("Can't open %s: %s\n",
map
->map_file
, errstring(errno
));
/* see if someone else is rebuilding the alias file */
if (!lockfile(fileno(af
), map
->map_file
, LOCK_EX
|LOCK_NB
))
/* yes, they are -- wait until done */
message("Alias file %s is already being rebuilt",
if (OpMode
!= MD_INITALIAS
)
/* wait for other rebuild to complete */
(void) lockfile(fileno(af
), map
->map_file
,
oldsigint
= signal(SIGINT
, SIG_IGN
);
if (map
->map_class
->map_open(map
, O_RDWR
))
map
->map_mflags
|= MF_OPEN
|MF_WRITABLE
;
readaliases(map
, af
, automatic
);
printf("Can't create database for %s: %s\n",
map
->map_file
, errstring(errno
));
syserr("Cannot create database for alias file %s",
/* close the file, thus releasing locks */
/* add distinguished entries and close the database */
if (bitset(MF_OPEN
, map
->map_mflags
))
map
->map_class
->map_close(map
);
/* restore the old signal */
(void) signal(SIGINT
, oldsigint
);
** READALIASES -- read and process the alias file.
** This routine implements the part of initaliases that occurs
** when we are not going to use the DBM stuff.
** map -- the alias database descriptor.
** af -- file to read the aliases from.
** automatic -- set if this was an automatic rebuild.
** Reads aliasfile into the symbol table.
** Optionally, builds the .dir & .pag files.
readaliases(map
, af
, automatic
)
long naliases
, bytes
, longest
;
** Read and interpret lines
FileName
= map
->map_file
;
naliases
= bytes
= longest
= 0;
while (fgets(line
, sizeof (line
), af
) != NULL
)
syserr("554 Non-continuation line starts with space");
** Find the colon separator, and parse the address.
** It should resolve to a local name -- this will
** be checked later (we want to optionally do
** parsing of the RHS first to maximize error
for (p
= line
; *p
!= '\0' && *p
!= ':' && *p
!= '\n'; p
++)
syserr("554 missing colon");
if (parseaddr(line
, &al
, 1, ':', NULL
, CurEnv
) == NULL
)
syserr("554 illegal alias name");
** 'al' is the internal form of the LHS address.
** 'p' points to the text of the RHS.
while (isascii(*p
) && isspace(*p
))
/* do parsing & compression of addresses */
while ((isascii(*p
) && isspace(*p
)) ||
if (parseaddr(p
, &bl
, -1, ',', &delimptr
, CurEnv
) == NULL
)
usrerr("553 %s... bad address", p
);
/* see if there should be a continuation line */
if (c
!= ' ' && c
!= '\t')
/* read continuation line */
if (fgets(p
, sizeof line
- (p
- line
), af
) == NULL
)
/* check for line overflow */
if (strchr(p
, '\n') == NULL
)
usrerr("554 alias too long");
if (al
.q_mailer
!= LocalMailer
)
syserr("554 cannot alias non-local names");
** Insert alias into symbol table or DBM file
if (!bitnset(M_USR_UPPER
, al
.q_mailer
->m_flags
))
lhssize
= strlen(al
.q_user
);
map
->map_class
->map_store(map
, al
.q_user
, rhs
);
bytes
+= lhssize
+ rhssize
;
if (Verbose
|| !automatic
)
message("%s: %d aliases, longest %d bytes, %d bytes total",
map
->map_file
, naliases
, longest
, bytes
);
syslog(LOG_INFO
, "%s: %d aliases, longest %d bytes, %d bytes total",
map
->map_file
, naliases
, longest
, bytes
);
** FORWARD -- Try to forward mail
** This is similar but not identical to aliasing.
** user -- the name of the user who's mail we would like
** to forward to. It must have been verified --
** i.e., the q_home field must have been filled
** sendq -- a pointer to the head of the send queue to
** put this user's aliases in.
** New names are added to send queues.
printf("forward(%s)\n", user
->q_paddr
);
if (user
->q_mailer
!= LocalMailer
|| bitset(QBADADDR
, user
->q_flags
))
if (user
->q_home
== NULL
)
syserr("554 forward: no home");
user
->q_home
= "/nosuchdirectory";
/* good address -- look for .forward file in home */
define('z', user
->q_home
, e
);
define('u', user
->q_user
, e
);
define('h', user
->q_host
, e
);
ForwardPath
= newstr("\201z/.forward");
for (pp
= ForwardPath
; pp
!= NULL
; pp
= ep
)
expand(pp
, buf
, &buf
[sizeof buf
- 1], e
);
printf("forward: trying %s\n", buf
);
printf("forward: old uid = %d/%d\n", getuid(), geteuid());
if (saveduid
== 0 && uid
!= 0)
printf("forward: new uid = %d/%d\n", getuid(), geteuid());
err
= include(buf
, TRUE
, user
, sendq
, e
);
if (saveduid
== 0 && uid
!= 0)
if (seteuid(saveduid
) < 0)
syserr("seteuid(%d) failure (real=%d, eff=%d)",
saveduid
, getuid(), geteuid());
printf("forward: reset uid = %d/%d\n", getuid(), geteuid());
/* we have to suspend this message */
printf("forward: transient error on %s\n", buf
);
syslog(LOG_ERR
, "%s: forward %s: transient error: %s",
e
->e_id
, buf
, errstring(err
));
message("%s: %s: message queued", buf
, errstring(err
));
user
->q_flags
|= QQUEUEUP
|QDONTSEND
;