138032Speter/*
2261363Sgshapiro * Copyright (c) 1998-2003 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
564562Sgshapiro * Copyright (c) 1988, 1993
664562Sgshapiro *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1190792Sgshapiro *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
1538032Speter
16266692SgshapiroSM_RCSID("@(#)$Id: alias.c,v 8.221 2013-11-22 20:51:54 ca Exp $")
1738032Speter
1890792Sgshapiro#define SEPARATOR ':'
1964562Sgshapiro# define ALIAS_SPEC_SEPARATORS	" ,/:"
2038032Speter
2164562Sgshapirostatic MAP	*AliasFileMap = NULL;	/* the actual aliases.files map */
2264562Sgshapirostatic int	NAliasFileMaps;	/* the number of entries in AliasFileMap */
2364562Sgshapiro
2490792Sgshapirostatic char	*aliaslookup __P((char *, int *, char *));
2564562Sgshapiro
2690792Sgshapiro/*
2738032Speter**  ALIAS -- Compute aliases.
2838032Speter**
2938032Speter**	Scans the alias file for an alias for the given address.
3038032Speter**	If found, it arranges to deliver to the alias list instead.
3138032Speter**	Uses libdbm database if -DDBM.
3238032Speter**
3338032Speter**	Parameters:
3438032Speter**		a -- address to alias.
3538032Speter**		sendq -- a pointer to the head of the send queue
3638032Speter**			to put the aliases in.
3738032Speter**		aliaslevel -- the current alias nesting depth.
3838032Speter**		e -- the current envelope.
3938032Speter**
4038032Speter**	Returns:
4138032Speter**		none
4238032Speter**
4338032Speter**	Side Effects:
4438032Speter**		Aliases found are expanded.
4538032Speter**
4638032Speter**	Deficiencies:
4738032Speter**		It should complain about names that are aliased to
4838032Speter**			nothing.
4938032Speter*/
5038032Speter
5138032Spetervoid
5238032Speteralias(a, sendq, aliaslevel, e)
5338032Speter	register ADDRESS *a;
5438032Speter	ADDRESS **sendq;
5538032Speter	int aliaslevel;
5638032Speter	register ENVELOPE *e;
5738032Speter{
5838032Speter	register char *p;
5938032Speter	char *owner;
6064562Sgshapiro	auto int status = EX_OK;
6138032Speter	char obuf[MAXNAME + 7];
6238032Speter
6338032Speter	if (tTd(27, 1))
6490792Sgshapiro		sm_dprintf("alias(%s)\n", a->q_user);
6538032Speter
6638032Speter	/* don't realias already aliased names */
6764562Sgshapiro	if (!QS_IS_OK(a->q_state))
6838032Speter		return;
6938032Speter
7038032Speter	if (NoAlias)
7138032Speter		return;
7238032Speter
7338032Speter	e->e_to = a->q_paddr;
7438032Speter
7538032Speter	/*
7638032Speter	**  Look up this name.
7738032Speter	**
7838032Speter	**	If the map was unavailable, we will queue this message
7938032Speter	**	until the map becomes available; otherwise, we could
8038032Speter	**	bounce messages inappropriately.
8138032Speter	*/
8238032Speter
8364562Sgshapiro#if _FFR_REDIRECTEMPTY
8464562Sgshapiro	/*
8564562Sgshapiro	**  envelope <> can't be sent to mailing lists, only owner-
8664562Sgshapiro	**  send spam of this type to owner- of the list
8764562Sgshapiro	**  ----  to stop spam from going to mailing lists!
8864562Sgshapiro	*/
8990792Sgshapiro
9064562Sgshapiro	if (e->e_sender != NULL && *e->e_sender == '\0')
9138032Speter	{
9264562Sgshapiro		/* Look for owner of alias */
93168515Sgshapiro		(void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user);
9490792Sgshapiro		if (aliaslookup(obuf, &status, a->q_host) != NULL)
9564562Sgshapiro		{
9664562Sgshapiro			if (LogLevel > 8)
97120256Sgshapiro				sm_syslog(LOG_WARNING, e->e_id,
9864562Sgshapiro				       "possible spam from <> to list: %s, redirected to %s\n",
9964562Sgshapiro				       a->q_user, obuf);
10090792Sgshapiro			a->q_user = sm_rpool_strdup_x(e->e_rpool, obuf);
10164562Sgshapiro		}
10264562Sgshapiro	}
10364562Sgshapiro#endif /* _FFR_REDIRECTEMPTY */
10464562Sgshapiro
10590792Sgshapiro	p = aliaslookup(a->q_user, &status, a->q_host);
10664562Sgshapiro	if (status == EX_TEMPFAIL || status == EX_UNAVAILABLE)
10764562Sgshapiro	{
10864562Sgshapiro		a->q_state = QS_QUEUEUP;
10938032Speter		if (e->e_message == NULL)
110168515Sgshapiro			e->e_message = sm_rpool_strdup_x(e->e_rpool,
111168515Sgshapiro						"alias database unavailable");
11290792Sgshapiro
11390792Sgshapiro		/* XXX msg only per recipient? */
11490792Sgshapiro		if (a->q_message == NULL)
11590792Sgshapiro			a->q_message = "alias database unavailable";
11638032Speter		return;
11738032Speter	}
11838032Speter	if (p == NULL)
11938032Speter		return;
12038032Speter
12138032Speter	/*
12238032Speter	**  Match on Alias.
12338032Speter	**	Deliver to the target list.
12438032Speter	*/
12538032Speter
12638032Speter	if (tTd(27, 1))
12790792Sgshapiro		sm_dprintf("%s (%s, %s) aliased to %s\n",
12890792Sgshapiro			   a->q_paddr, a->q_host, a->q_user, p);
12938032Speter	if (bitset(EF_VRFYONLY, e->e_flags))
13038032Speter	{
13164562Sgshapiro		a->q_state = QS_VERIFIED;
13238032Speter		return;
13338032Speter	}
13438032Speter	message("aliased to %s", shortenstring(p, MAXSHORTSTR));
13564562Sgshapiro	if (LogLevel > 10)
13638032Speter		sm_syslog(LOG_INFO, e->e_id,
13790792Sgshapiro			  "alias %.100s => %s",
13890792Sgshapiro			  a->q_paddr, shortenstring(p, MAXSHORTSTR));
13938032Speter	a->q_flags &= ~QSELFREF;
14038032Speter	if (tTd(27, 5))
14138032Speter	{
14290792Sgshapiro		sm_dprintf("alias: QS_EXPANDED ");
143132943Sgshapiro		printaddr(sm_debug_file(), a, false);
14438032Speter	}
14564562Sgshapiro	a->q_state = QS_EXPANDED;
14664562Sgshapiro
14764562Sgshapiro	/*
14864562Sgshapiro	**  Always deliver aliased items as the default user.
14964562Sgshapiro	**  Setting q_gid to 0 forces deliver() to use DefUser
15064562Sgshapiro	**  instead of the alias name for the call to initgroups().
15164562Sgshapiro	*/
15264562Sgshapiro
15364562Sgshapiro	a->q_uid = DefUid;
15464562Sgshapiro	a->q_gid = 0;
15564562Sgshapiro	a->q_fullname = NULL;
15664562Sgshapiro	a->q_flags |= QGOODUID|QALIAS;
15764562Sgshapiro
15838032Speter	(void) sendtolist(p, a, sendq, aliaslevel + 1, e);
15990792Sgshapiro
16064562Sgshapiro	if (bitset(QSELFREF, a->q_flags) && QS_IS_EXPANDED(a->q_state))
16164562Sgshapiro		a->q_state = QS_OK;
16238032Speter
16338032Speter	/*
16438032Speter	**  Look for owner of alias
16538032Speter	*/
16638032Speter
16738032Speter	if (strncmp(a->q_user, "owner-", 6) == 0 ||
168168515Sgshapiro	    strlen(a->q_user) > sizeof(obuf) - 7)
169168515Sgshapiro		(void) sm_strlcpy(obuf, "owner-owner", sizeof(obuf));
17038032Speter	else
171168515Sgshapiro		(void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user);
17290792Sgshapiro	owner = aliaslookup(obuf, &status, a->q_host);
17338032Speter	if (owner == NULL)
17438032Speter		return;
17538032Speter
17638032Speter	/* reflect owner into envelope sender */
17738032Speter	if (strpbrk(owner, ",:/|\"") != NULL)
17838032Speter		owner = obuf;
17990792Sgshapiro	a->q_owner = sm_rpool_strdup_x(e->e_rpool, owner);
18038032Speter
18138032Speter	/* announce delivery to this alias; NORECEIPT bit set later */
18238032Speter	if (e->e_xfp != NULL)
18390792Sgshapiro		(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
18490792Sgshapiro				"Message delivered to mailing list %s\n",
18590792Sgshapiro				a->q_paddr);
18638032Speter	e->e_flags |= EF_SENDRECEIPT;
18738032Speter	a->q_flags |= QDELIVERED|QEXPANDED;
18838032Speter}
18990792Sgshapiro/*
19038032Speter**  ALIASLOOKUP -- look up a name in the alias file.
19138032Speter**
19238032Speter**	Parameters:
19338032Speter**		name -- the name to look up.
19438032Speter**		pstat -- a pointer to a place to put the status.
19590792Sgshapiro**		av -- argument for %1 expansion.
19638032Speter**
19738032Speter**	Returns:
19838032Speter**		the value of name.
19938032Speter**		NULL if unknown.
20038032Speter**
20138032Speter**	Side Effects:
20238032Speter**		none.
20338032Speter**
20438032Speter**	Warnings:
20538032Speter**		The return value will be trashed across calls.
20638032Speter*/
20738032Speter
20864562Sgshapirostatic char *
20990792Sgshapiroaliaslookup(name, pstat, av)
21038032Speter	char *name;
21138032Speter	int *pstat;
21290792Sgshapiro	char *av;
21338032Speter{
21438032Speter	static MAP *map = NULL;
21590792Sgshapiro#if _FFR_ALIAS_DETAIL
21690792Sgshapiro	int i;
21790792Sgshapiro	char *argv[4];
21890792Sgshapiro#endif /* _FFR_ALIAS_DETAIL */
21938032Speter
22038032Speter	if (map == NULL)
22138032Speter	{
22238032Speter		STAB *s = stab("aliases", ST_MAP, ST_FIND);
22338032Speter
22438032Speter		if (s == NULL)
22538032Speter			return NULL;
22638032Speter		map = &s->s_map;
22738032Speter	}
22864562Sgshapiro	DYNOPENMAP(map);
22938032Speter
23038032Speter	/* special case POstMastER -- always use lower case */
23190792Sgshapiro	if (sm_strcasecmp(name, "postmaster") == 0)
23238032Speter		name = "postmaster";
23338032Speter
23490792Sgshapiro#if _FFR_ALIAS_DETAIL
23590792Sgshapiro	i = 0;
23690792Sgshapiro	argv[i++] = name;
23790792Sgshapiro	argv[i++] = av;
23890792Sgshapiro
23990792Sgshapiro	/* XXX '+' is hardwired here as delimiter! */
24090792Sgshapiro	if (av != NULL && *av == '+')
24190792Sgshapiro		argv[i++] = av + 1;
24290792Sgshapiro	argv[i++] = NULL;
24390792Sgshapiro	return (*map->map_class->map_lookup)(map, name, argv, pstat);
24490792Sgshapiro#else /* _FFR_ALIAS_DETAIL */
24538032Speter	return (*map->map_class->map_lookup)(map, name, NULL, pstat);
24690792Sgshapiro#endif /* _FFR_ALIAS_DETAIL */
24738032Speter}
24890792Sgshapiro/*
24938032Speter**  SETALIAS -- set up an alias map
25038032Speter**
25138032Speter**	Called when reading configuration file.
25238032Speter**
25338032Speter**	Parameters:
25438032Speter**		spec -- the alias specification
25538032Speter**
25638032Speter**	Returns:
25738032Speter**		none.
25838032Speter*/
25938032Speter
26038032Spetervoid
26138032Spetersetalias(spec)
26238032Speter	char *spec;
26338032Speter{
26438032Speter	register char *p;
26538032Speter	register MAP *map;
26638032Speter	char *class;
26738032Speter	STAB *s;
26838032Speter
26938032Speter	if (tTd(27, 8))
27090792Sgshapiro		sm_dprintf("setalias(%s)\n", spec);
27138032Speter
27238032Speter	for (p = spec; p != NULL; )
27338032Speter	{
27438032Speter		char buf[50];
27538032Speter
27638032Speter		while (isascii(*p) && isspace(*p))
27738032Speter			p++;
27838032Speter		if (*p == '\0')
27938032Speter			break;
28038032Speter		spec = p;
28138032Speter
28238032Speter		if (NAliasFileMaps >= MAXMAPSTACK)
28338032Speter		{
28438032Speter			syserr("Too many alias databases defined, %d max",
28538032Speter				MAXMAPSTACK);
28638032Speter			return;
28738032Speter		}
28838032Speter		if (AliasFileMap == NULL)
28938032Speter		{
29090792Sgshapiro			(void) sm_strlcpy(buf, "aliases.files sequence",
291168515Sgshapiro					  sizeof(buf));
29238032Speter			AliasFileMap = makemapentry(buf);
29338032Speter			if (AliasFileMap == NULL)
29438032Speter			{
29538032Speter				syserr("setalias: cannot create aliases.files map");
29638032Speter				return;
29738032Speter			}
29838032Speter		}
299168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "Alias%d", NAliasFileMaps);
30038032Speter		s = stab(buf, ST_MAP, ST_ENTER);
30138032Speter		map = &s->s_map;
302168515Sgshapiro		memset(map, '\0', sizeof(*map));
30338032Speter		map->map_mname = s->s_name;
30471345Sgshapiro		p = strpbrk(p, ALIAS_SPEC_SEPARATORS);
30564562Sgshapiro		if (p != NULL && *p == SEPARATOR)
30638032Speter		{
30738032Speter			/* map name */
30838032Speter			*p++ = '\0';
30938032Speter			class = spec;
31038032Speter			spec = p;
31138032Speter		}
31238032Speter		else
31338032Speter		{
31438032Speter			class = "implicit";
31538032Speter			map->map_mflags = MF_INCLNULL;
31638032Speter		}
31738032Speter
31838032Speter		/* find end of spec */
31938032Speter		if (p != NULL)
32064562Sgshapiro		{
32190792Sgshapiro			bool quoted = false;
32264562Sgshapiro
32364562Sgshapiro			for (; *p != '\0'; p++)
32464562Sgshapiro			{
32564562Sgshapiro				/*
32664562Sgshapiro				**  Don't break into a quoted string.
32764562Sgshapiro				**  Needed for ldap maps which use
32864562Sgshapiro				**  commas in their specifications.
32964562Sgshapiro				*/
33064562Sgshapiro
33164562Sgshapiro				if (*p == '"')
33264562Sgshapiro					quoted = !quoted;
33364562Sgshapiro				else if (*p == ',' && !quoted)
33464562Sgshapiro					break;
33564562Sgshapiro			}
33664562Sgshapiro
33764562Sgshapiro			/* No more alias specifications follow */
33864562Sgshapiro			if (*p == '\0')
33964562Sgshapiro				p = NULL;
34064562Sgshapiro		}
34138032Speter		if (p != NULL)
34238032Speter			*p++ = '\0';
34338032Speter
34438032Speter		if (tTd(27, 20))
34590792Sgshapiro			sm_dprintf("  map %s:%s %s\n", class, s->s_name, spec);
34638032Speter
34738032Speter		/* look up class */
34838032Speter		s = stab(class, ST_MAPCLASS, ST_FIND);
34938032Speter		if (s == NULL)
35038032Speter		{
35138032Speter			syserr("setalias: unknown alias class %s", class);
35238032Speter		}
35338032Speter		else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
35438032Speter		{
35538032Speter			syserr("setalias: map class %s can't handle aliases",
35638032Speter				class);
35738032Speter		}
35838032Speter		else
35938032Speter		{
36038032Speter			map->map_class = &s->s_mapclass;
36190792Sgshapiro			map->map_mflags |= MF_ALIAS;
36238032Speter			if (map->map_class->map_parse(map, spec))
36338032Speter			{
36490792Sgshapiro				map->map_mflags |= MF_VALID;
36538032Speter				AliasFileMap->map_stack[NAliasFileMaps++] = map;
36638032Speter			}
36738032Speter		}
36838032Speter	}
36938032Speter}
37090792Sgshapiro/*
37138032Speter**  ALIASWAIT -- wait for distinguished @:@ token to appear.
37238032Speter**
37338032Speter**	This can decide to reopen or rebuild the alias file
37438032Speter**
37538032Speter**	Parameters:
37638032Speter**		map -- a pointer to the map descriptor for this alias file.
37738032Speter**		ext -- the filename extension (e.g., ".db") for the
37838032Speter**			database file.
37938032Speter**		isopen -- if set, the database is already open, and we
38038032Speter**			should check for validity; otherwise, we are
38138032Speter**			just checking to see if it should be created.
38238032Speter**
38338032Speter**	Returns:
38490792Sgshapiro**		true -- if the database is open when we return.
38590792Sgshapiro**		false -- if the database is closed when we return.
38638032Speter*/
38738032Speter
38838032Speterbool
38938032Speteraliaswait(map, ext, isopen)
39038032Speter	MAP *map;
39138032Speter	char *ext;
39264562Sgshapiro	bool isopen;
39338032Speter{
39490792Sgshapiro	bool attimeout = false;
39538032Speter	time_t mtime;
39638032Speter	struct stat stb;
39798121Sgshapiro	char buf[MAXPATHLEN];
39838032Speter
39938032Speter	if (tTd(27, 3))
40090792Sgshapiro		sm_dprintf("aliaswait(%s:%s)\n",
40190792Sgshapiro			   map->map_class->map_cname, map->map_file);
40238032Speter	if (bitset(MF_ALIASWAIT, map->map_mflags))
40338032Speter		return isopen;
40438032Speter	map->map_mflags |= MF_ALIASWAIT;
40538032Speter
40638032Speter	if (SafeAlias > 0)
40738032Speter	{
40838032Speter		auto int st;
40990792Sgshapiro		unsigned int sleeptime = 2;
41090792Sgshapiro		unsigned int loopcount = 0;	/* only used for debugging */
41138032Speter		time_t toolong = curtime() + SafeAlias;
41238032Speter
41338032Speter		while (isopen &&
41438032Speter		       map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
41538032Speter		{
41638032Speter			if (curtime() > toolong)
41738032Speter			{
41838032Speter				/* we timed out */
41990792Sgshapiro				attimeout = true;
42038032Speter				break;
42138032Speter			}
42238032Speter
42338032Speter			/*
42438032Speter			**  Close and re-open the alias database in case
42538032Speter			**  the one is mv'ed instead of cp'ed in.
42638032Speter			*/
42738032Speter
42838032Speter			if (tTd(27, 2))
42990792Sgshapiro			{
43090792Sgshapiro				loopcount++;
43190792Sgshapiro				sm_dprintf("aliaswait: sleeping for %u seconds (loopcount = %u)\n",
43290792Sgshapiro					   sleeptime, loopcount);
43390792Sgshapiro			}
43438032Speter
43577349Sgshapiro			map->map_mflags |= MF_CLOSING;
43638032Speter			map->map_class->map_close(map);
43777349Sgshapiro			map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
43864562Sgshapiro			(void) sleep(sleeptime);
43938032Speter			sleeptime *= 2;
44038032Speter			if (sleeptime > 60)
44138032Speter				sleeptime = 60;
44238032Speter			isopen = map->map_class->map_open(map, O_RDONLY);
44338032Speter		}
44438032Speter	}
44538032Speter
44638032Speter	/* see if we need to go into auto-rebuild mode */
44738032Speter	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
44838032Speter	{
44938032Speter		if (tTd(27, 3))
45090792Sgshapiro			sm_dprintf("aliaswait: not rebuildable\n");
45138032Speter		map->map_mflags &= ~MF_ALIASWAIT;
45238032Speter		return isopen;
45338032Speter	}
45438032Speter	if (stat(map->map_file, &stb) < 0)
45538032Speter	{
45638032Speter		if (tTd(27, 3))
45790792Sgshapiro			sm_dprintf("aliaswait: no source file\n");
45838032Speter		map->map_mflags &= ~MF_ALIASWAIT;
45938032Speter		return isopen;
46038032Speter	}
46138032Speter	mtime = stb.st_mtime;
462168515Sgshapiro	if (sm_strlcpyn(buf, sizeof(buf), 2,
463168515Sgshapiro			map->map_file, ext == NULL ? "" : ext) >= sizeof(buf))
46498121Sgshapiro	{
46598121Sgshapiro		if (LogLevel > 3)
46698121Sgshapiro			sm_syslog(LOG_INFO, NOQID,
46798121Sgshapiro				  "alias database %s%s name too long",
46898121Sgshapiro				  map->map_file, ext == NULL ? "" : ext);
46998121Sgshapiro		message("alias database %s%s name too long",
47098121Sgshapiro			map->map_file, ext == NULL ? "" : ext);
47198121Sgshapiro	}
47298121Sgshapiro
47338032Speter	if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
47438032Speter	{
47564562Sgshapiro		if (LogLevel > 3)
47664562Sgshapiro			sm_syslog(LOG_INFO, NOQID,
47790792Sgshapiro				  "alias database %s out of date", buf);
47864562Sgshapiro		message("Warning: alias database %s out of date", buf);
47938032Speter	}
48038032Speter	map->map_mflags &= ~MF_ALIASWAIT;
48138032Speter	return isopen;
48238032Speter}
48390792Sgshapiro/*
48438032Speter**  REBUILDALIASES -- rebuild the alias database.
48538032Speter**
48638032Speter**	Parameters:
48738032Speter**		map -- the database to rebuild.
48838032Speter**		automatic -- set if this was automatically generated.
48938032Speter**
49038032Speter**	Returns:
49190792Sgshapiro**		true if successful; false otherwise.
49238032Speter**
49338032Speter**	Side Effects:
49438032Speter**		Reads the text version of the database, builds the
49538032Speter**		DBM or DB version.
49638032Speter*/
49738032Speter
49838032Speterbool
49938032Speterrebuildaliases(map, automatic)
50038032Speter	register MAP *map;
50138032Speter	bool automatic;
50238032Speter{
50390792Sgshapiro	SM_FILE_T *af;
50490792Sgshapiro	bool nolock = false;
50590792Sgshapiro	bool success = false;
50664562Sgshapiro	long sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK;
50738032Speter	sigfunc_t oldsigint, oldsigquit;
50838032Speter#ifdef SIGTSTP
50938032Speter	sigfunc_t oldsigtstp;
51064562Sgshapiro#endif /* SIGTSTP */
51138032Speter
51238032Speter	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
51390792Sgshapiro		return false;
51438032Speter
51564562Sgshapiro	if (!bitnset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail))
51638032Speter		sff |= SFF_NOWLINK;
51764562Sgshapiro	if (!bitnset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail))
51838032Speter		sff |= SFF_NOGWFILES;
51964562Sgshapiro	if (!bitnset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail))
52038032Speter		sff |= SFF_NOWWFILES;
52138032Speter
52238032Speter	/* try to lock the source file */
52338032Speter	if ((af = safefopen(map->map_file, O_RDWR, 0, sff)) == NULL)
52438032Speter	{
52538032Speter		struct stat stb;
52638032Speter
52738032Speter		if ((errno != EACCES && errno != EROFS) || automatic ||
52838032Speter		    (af = safefopen(map->map_file, O_RDONLY, 0, sff)) == NULL)
52938032Speter		{
53038032Speter			int saveerr = errno;
53138032Speter
53238032Speter			if (tTd(27, 1))
53390792Sgshapiro				sm_dprintf("Can't open %s: %s\n",
53490792Sgshapiro					map->map_file, sm_errstring(saveerr));
53538032Speter			if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
53638032Speter				message("newaliases: cannot open %s: %s",
53790792Sgshapiro					map->map_file, sm_errstring(saveerr));
53838032Speter			errno = 0;
53990792Sgshapiro			return false;
54038032Speter		}
54190792Sgshapiro		nolock = true;
54238032Speter		if (tTd(27, 1) ||
54390792Sgshapiro		    fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &stb) < 0 ||
54438032Speter		    bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode))
54538032Speter			message("warning: cannot lock %s: %s",
54690792Sgshapiro				map->map_file, sm_errstring(errno));
54738032Speter	}
54838032Speter
54938032Speter	/* see if someone else is rebuilding the alias file */
55038032Speter	if (!nolock &&
55190792Sgshapiro	    !lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), map->map_file,
55290792Sgshapiro		      NULL, LOCK_EX|LOCK_NB))
55338032Speter	{
55438032Speter		/* yes, they are -- wait until done */
55538032Speter		message("Alias file %s is locked (maybe being rebuilt)",
55638032Speter			map->map_file);
55738032Speter		if (OpMode != MD_INITALIAS)
55838032Speter		{
55938032Speter			/* wait for other rebuild to complete */
56090792Sgshapiro			(void) lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL),
56190792Sgshapiro					map->map_file, NULL, LOCK_EX);
56238032Speter		}
56390792Sgshapiro		(void) sm_io_close(af, SM_TIME_DEFAULT);
56438032Speter		errno = 0;
56590792Sgshapiro		return false;
56638032Speter	}
56738032Speter
56890792Sgshapiro	oldsigint = sm_signal(SIGINT, SIG_IGN);
56990792Sgshapiro	oldsigquit = sm_signal(SIGQUIT, SIG_IGN);
57038032Speter#ifdef SIGTSTP
57190792Sgshapiro	oldsigtstp = sm_signal(SIGTSTP, SIG_IGN);
57264562Sgshapiro#endif /* SIGTSTP */
57338032Speter
57438032Speter	if (map->map_class->map_open(map, O_RDWR))
57538032Speter	{
57638032Speter		if (LogLevel > 7)
57738032Speter		{
57838032Speter			sm_syslog(LOG_NOTICE, NOQID,
57938032Speter				"alias database %s %srebuilt by %s",
58038032Speter				map->map_file, automatic ? "auto" : "",
58138032Speter				username());
58238032Speter		}
58338032Speter		map->map_mflags |= MF_OPEN|MF_WRITABLE;
58490792Sgshapiro		map->map_pid = CurrentPid;
58590792Sgshapiro		readaliases(map, af, !automatic, true);
58690792Sgshapiro		success = true;
58738032Speter	}
58838032Speter	else
58938032Speter	{
59038032Speter		if (tTd(27, 1))
59190792Sgshapiro			sm_dprintf("Can't create database for %s: %s\n",
59290792Sgshapiro				map->map_file, sm_errstring(errno));
59338032Speter		if (!automatic)
59438032Speter			syserr("Cannot create database for alias file %s",
59538032Speter				map->map_file);
59638032Speter	}
59738032Speter
59838032Speter	/* close the file, thus releasing locks */
59990792Sgshapiro	(void) sm_io_close(af, SM_TIME_DEFAULT);
60038032Speter
60138032Speter	/* add distinguished entries and close the database */
60238032Speter	if (bitset(MF_OPEN, map->map_mflags))
60338032Speter	{
60477349Sgshapiro		map->map_mflags |= MF_CLOSING;
60538032Speter		map->map_class->map_close(map);
60677349Sgshapiro		map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
60738032Speter	}
60838032Speter
60938032Speter	/* restore the old signals */
61090792Sgshapiro	(void) sm_signal(SIGINT, oldsigint);
61190792Sgshapiro	(void) sm_signal(SIGQUIT, oldsigquit);
61290792Sgshapiro#ifdef SIGTSTP
61390792Sgshapiro	(void) sm_signal(SIGTSTP, oldsigtstp);
61490792Sgshapiro#endif /* SIGTSTP */
61538032Speter	return success;
61638032Speter}
61790792Sgshapiro/*
61838032Speter**  READALIASES -- read and process the alias file.
61938032Speter**
62038032Speter**	This routine implements the part of initaliases that occurs
62138032Speter**	when we are not going to use the DBM stuff.
62238032Speter**
62338032Speter**	Parameters:
62438032Speter**		map -- the alias database descriptor.
62538032Speter**		af -- file to read the aliases from.
62664562Sgshapiro**		announcestats -- announce statistics regarding number of
62738032Speter**			aliases, longest alias, etc.
62838032Speter**		logstats -- lot the same info.
62938032Speter**
63038032Speter**	Returns:
63138032Speter**		none.
63238032Speter**
63338032Speter**	Side Effects:
63438032Speter**		Reads aliasfile into the symbol table.
63538032Speter**		Optionally, builds the .dir & .pag files.
63638032Speter*/
63738032Speter
63838032Spetervoid
63938032Speterreadaliases(map, af, announcestats, logstats)
64038032Speter	register MAP *map;
64190792Sgshapiro	SM_FILE_T *af;
64238032Speter	bool announcestats;
64338032Speter	bool logstats;
64438032Speter{
64538032Speter	register char *p;
64638032Speter	char *rhs;
64738032Speter	bool skipping;
64838032Speter	long naliases, bytes, longest;
64938032Speter	ADDRESS al, bl;
65038032Speter	char line[BUFSIZ];
65138032Speter
65238032Speter	/*
65338032Speter	**  Read and interpret lines
65438032Speter	*/
65538032Speter
65638032Speter	FileName = map->map_file;
65738032Speter	LineNumber = 0;
65838032Speter	naliases = bytes = longest = 0;
65990792Sgshapiro	skipping = false;
660249729Sgshapiro	while (sm_io_fgets(af, SM_TIME_DEFAULT, line, sizeof(line)) >= 0)
66138032Speter	{
66238032Speter		int lhssize, rhssize;
66338032Speter		int c;
66438032Speter
66538032Speter		LineNumber++;
66638032Speter		p = strchr(line, '\n');
66790792Sgshapiro
66890792Sgshapiro		/* XXX what if line="a\\" ? */
66938032Speter		while (p != NULL && p > line && p[-1] == '\\')
67038032Speter		{
67138032Speter			p--;
67290792Sgshapiro			if (sm_io_fgets(af, SM_TIME_DEFAULT, p,
673249729Sgshapiro					SPACELEFT(line, p)) < 0)
67438032Speter				break;
67538032Speter			LineNumber++;
67638032Speter			p = strchr(p, '\n');
67738032Speter		}
67838032Speter		if (p != NULL)
67938032Speter			*p = '\0';
68090792Sgshapiro		else if (!sm_io_eof(af))
68138032Speter		{
68264562Sgshapiro			errno = 0;
68364562Sgshapiro			syserr("554 5.3.0 alias line too long");
68438032Speter
68538032Speter			/* flush to end of line */
68690792Sgshapiro			while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) !=
68790792Sgshapiro				SM_IO_EOF && c != '\n')
68838032Speter				continue;
68938032Speter
69038032Speter			/* skip any continuation lines */
69190792Sgshapiro			skipping = true;
69238032Speter			continue;
69338032Speter		}
69438032Speter		switch (line[0])
69538032Speter		{
69638032Speter		  case '#':
69738032Speter		  case '\0':
69890792Sgshapiro			skipping = false;
69938032Speter			continue;
70038032Speter
70138032Speter		  case ' ':
70238032Speter		  case '\t':
70338032Speter			if (!skipping)
70464562Sgshapiro				syserr("554 5.3.5 Non-continuation line starts with space");
70590792Sgshapiro			skipping = true;
70638032Speter			continue;
70738032Speter		}
70890792Sgshapiro		skipping = false;
70938032Speter
71038032Speter		/*
71138032Speter		**  Process the LHS
71238032Speter		**	Find the colon separator, and parse the address.
71338032Speter		**	It should resolve to a local name -- this will
71438032Speter		**	be checked later (we want to optionally do
71538032Speter		**	parsing of the RHS first to maximize error
71638032Speter		**	detection).
71738032Speter		*/
71838032Speter
71938032Speter		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
72038032Speter			continue;
72138032Speter		if (*p++ != ':')
72238032Speter		{
72364562Sgshapiro			syserr("554 5.3.5 missing colon");
72438032Speter			continue;
72538032Speter		}
72690792Sgshapiro		if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv, true)
72790792Sgshapiro		    == NULL)
72838032Speter		{
72964562Sgshapiro			syserr("554 5.3.5 %.40s... illegal alias name", line);
73038032Speter			continue;
73138032Speter		}
73238032Speter
73338032Speter		/*
73438032Speter		**  Process the RHS.
73538032Speter		**	'al' is the internal form of the LHS address.
73638032Speter		**	'p' points to the text of the RHS.
73738032Speter		*/
73838032Speter
73938032Speter		while (isascii(*p) && isspace(*p))
74038032Speter			p++;
74138032Speter		rhs = p;
74238032Speter		for (;;)
74338032Speter		{
74438032Speter			register char *nlp;
74538032Speter
74638032Speter			nlp = &p[strlen(p)];
74771345Sgshapiro			if (nlp > p && nlp[-1] == '\n')
74838032Speter				*--nlp = '\0';
74938032Speter
75038032Speter			if (CheckAliases)
75138032Speter			{
75238032Speter				/* do parsing & compression of addresses */
75338032Speter				while (*p != '\0')
75438032Speter				{
75538032Speter					auto char *delimptr;
75638032Speter
75738032Speter					while ((isascii(*p) && isspace(*p)) ||
75838032Speter								*p == ',')
75938032Speter						p++;
76038032Speter					if (*p == '\0')
76138032Speter						break;
76238032Speter					if (parseaddr(p, &bl, RF_COPYNONE, ',',
76390792Sgshapiro						      &delimptr, CurEnv, true)
76490792Sgshapiro					    == NULL)
76564562Sgshapiro						usrerr("553 5.3.5 %s... bad address", p);
76638032Speter					p = delimptr;
76738032Speter				}
76838032Speter			}
76938032Speter			else
77038032Speter			{
77138032Speter				p = nlp;
77238032Speter			}
77338032Speter
77438032Speter			/* see if there should be a continuation line */
77590792Sgshapiro			c = sm_io_getc(af, SM_TIME_DEFAULT);
77690792Sgshapiro			if (!sm_io_eof(af))
77790792Sgshapiro				(void) sm_io_ungetc(af, SM_TIME_DEFAULT, c);
77838032Speter			if (c != ' ' && c != '\t')
77938032Speter				break;
78038032Speter
78138032Speter			/* read continuation line */
78290792Sgshapiro			if (sm_io_fgets(af, SM_TIME_DEFAULT, p,
783249729Sgshapiro					sizeof(line) - (p-line)) < 0)
78438032Speter				break;
78538032Speter			LineNumber++;
78638032Speter
78738032Speter			/* check for line overflow */
78890792Sgshapiro			if (strchr(p, '\n') == NULL && !sm_io_eof(af))
78938032Speter			{
79064562Sgshapiro				usrerr("554 5.3.5 alias too long");
79190792Sgshapiro				while ((c = sm_io_getc(af, SM_TIME_DEFAULT))
79290792Sgshapiro				       != SM_IO_EOF && c != '\n')
79338032Speter					continue;
79490792Sgshapiro				skipping = true;
79538032Speter				break;
79638032Speter			}
79738032Speter		}
79838032Speter
79938032Speter		if (skipping)
80038032Speter			continue;
80138032Speter
80238032Speter		if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags))
80338032Speter		{
80464562Sgshapiro			syserr("554 5.3.5 %s... cannot alias non-local names",
80538032Speter				al.q_paddr);
80638032Speter			continue;
80738032Speter		}
80838032Speter
80938032Speter		/*
81038032Speter		**  Insert alias into symbol table or database file.
81138032Speter		**
81238032Speter		**	Special case pOStmaStER -- always make it lower case.
81338032Speter		*/
81438032Speter
81590792Sgshapiro		if (sm_strcasecmp(al.q_user, "postmaster") == 0)
81638032Speter			makelower(al.q_user);
81738032Speter
81838032Speter		lhssize = strlen(al.q_user);
81938032Speter		rhssize = strlen(rhs);
82064562Sgshapiro		if (rhssize > 0)
82164562Sgshapiro		{
82264562Sgshapiro			/* is RHS empty (just spaces)? */
82364562Sgshapiro			p = rhs;
82464562Sgshapiro			while (isascii(*p) && isspace(*p))
82564562Sgshapiro				p++;
82664562Sgshapiro		}
82764562Sgshapiro		if (rhssize == 0 || *p == '\0')
82864562Sgshapiro		{
82964562Sgshapiro			syserr("554 5.3.5 %.40s... missing value for alias",
83064562Sgshapiro			       line);
83138032Speter
83264562Sgshapiro		}
83364562Sgshapiro		else
83464562Sgshapiro		{
83564562Sgshapiro			map->map_class->map_store(map, al.q_user, rhs);
83664562Sgshapiro
83764562Sgshapiro			/* statistics */
83864562Sgshapiro			naliases++;
83964562Sgshapiro			bytes += lhssize + rhssize;
84064562Sgshapiro			if (rhssize > longest)
84164562Sgshapiro				longest = rhssize;
84264562Sgshapiro		}
84364562Sgshapiro
84490792Sgshapiro#if 0
84590792Sgshapiro	/*
84690792Sgshapiro	**  address strings are now stored in the envelope rpool,
84790792Sgshapiro	**  and therefore cannot be freed.
84890792Sgshapiro	*/
84938032Speter		if (al.q_paddr != NULL)
85090792Sgshapiro			sm_free(al.q_paddr);  /* disabled */
85138032Speter		if (al.q_host != NULL)
85290792Sgshapiro			sm_free(al.q_host);  /* disabled */
85338032Speter		if (al.q_user != NULL)
85490792Sgshapiro			sm_free(al.q_user);  /* disabled */
85590792Sgshapiro#endif /* 0 */
85638032Speter	}
85738032Speter
85838032Speter	CurEnv->e_to = NULL;
85938032Speter	FileName = NULL;
86038032Speter	if (Verbose || announcestats)
86166494Sgshapiro		message("%s: %ld aliases, longest %ld bytes, %ld bytes total",
86238032Speter			map->map_file, naliases, longest, bytes);
86338032Speter	if (LogLevel > 7 && logstats)
86438032Speter		sm_syslog(LOG_INFO, NOQID,
86566494Sgshapiro			"%s: %ld aliases, longest %ld bytes, %ld bytes total",
86638032Speter			map->map_file, naliases, longest, bytes);
86738032Speter}
86890792Sgshapiro/*
86938032Speter**  FORWARD -- Try to forward mail
87038032Speter**
87138032Speter**	This is similar but not identical to aliasing.
87238032Speter**
87338032Speter**	Parameters:
87438032Speter**		user -- the name of the user who's mail we would like
87538032Speter**			to forward to.  It must have been verified --
87638032Speter**			i.e., the q_home field must have been filled
87738032Speter**			in.
87838032Speter**		sendq -- a pointer to the head of the send queue to
87938032Speter**			put this user's aliases in.
88038032Speter**		aliaslevel -- the current alias nesting depth.
88138032Speter**		e -- the current envelope.
88238032Speter**
88338032Speter**	Returns:
88438032Speter**		none.
88538032Speter**
88638032Speter**	Side Effects:
88738032Speter**		New names are added to send queues.
88838032Speter*/
88938032Speter
89038032Spetervoid
89138032Speterforward(user, sendq, aliaslevel, e)
89238032Speter	ADDRESS *user;
89338032Speter	ADDRESS **sendq;
89438032Speter	int aliaslevel;
89538032Speter	register ENVELOPE *e;
89638032Speter{
89738032Speter	char *pp;
89838032Speter	char *ep;
89938032Speter	bool got_transient;
90038032Speter
90138032Speter	if (tTd(27, 1))
90290792Sgshapiro		sm_dprintf("forward(%s)\n", user->q_paddr);
90338032Speter
90438032Speter	if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
90564562Sgshapiro	    !QS_IS_OK(user->q_state))
90638032Speter		return;
90790792Sgshapiro	if (ForwardPath != NULL && *ForwardPath == '\0')
90890792Sgshapiro		return;
90938032Speter	if (user->q_home == NULL)
91038032Speter	{
91164562Sgshapiro		syserr("554 5.3.0 forward: no home");
91238032Speter		user->q_home = "/no/such/directory";
91338032Speter	}
91438032Speter
91538032Speter	/* good address -- look for .forward file in home */
91690792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'z', user->q_home);
91790792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'u', user->q_user);
91890792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', user->q_host);
91938032Speter	if (ForwardPath == NULL)
92038032Speter		ForwardPath = newstr("\201z/.forward");
92138032Speter
92290792Sgshapiro	got_transient = false;
92338032Speter	for (pp = ForwardPath; pp != NULL; pp = ep)
92438032Speter	{
92538032Speter		int err;
92698121Sgshapiro		char buf[MAXPATHLEN];
92764562Sgshapiro		struct stat st;
92838032Speter
92964562Sgshapiro		ep = strchr(pp, SEPARATOR);
93038032Speter		if (ep != NULL)
93138032Speter			*ep = '\0';
932168515Sgshapiro		expand(pp, buf, sizeof(buf), e);
93338032Speter		if (ep != NULL)
93464562Sgshapiro			*ep++ = SEPARATOR;
93538032Speter		if (buf[0] == '\0')
93638032Speter			continue;
93738032Speter		if (tTd(27, 3))
93890792Sgshapiro			sm_dprintf("forward: trying %s\n", buf);
93938032Speter
94090792Sgshapiro		err = include(buf, true, user, sendq, aliaslevel, e);
94138032Speter		if (err == 0)
94238032Speter			break;
94338032Speter		else if (transienterror(err))
94438032Speter		{
94538032Speter			/* we may have to suspend this message */
94690792Sgshapiro			got_transient = true;
94738032Speter			if (tTd(27, 2))
94890792Sgshapiro				sm_dprintf("forward: transient error on %s\n",
94990792Sgshapiro					   buf);
95038032Speter			if (LogLevel > 2)
95164562Sgshapiro			{
95264562Sgshapiro				char *curhost = CurHostName;
95364562Sgshapiro
95464562Sgshapiro				CurHostName = NULL;
95538032Speter				sm_syslog(LOG_ERR, e->e_id,
95664562Sgshapiro					  "forward %s: transient error: %s",
95790792Sgshapiro					  buf, sm_errstring(err));
95864562Sgshapiro				CurHostName = curhost;
95964562Sgshapiro			}
96064562Sgshapiro
96138032Speter		}
96238032Speter		else
96338032Speter		{
96438032Speter			switch (err)
96538032Speter			{
96638032Speter			  case ENOENT:
96738032Speter				break;
96838032Speter
96964562Sgshapiro			  case E_SM_WWDIR:
97064562Sgshapiro			  case E_SM_GWDIR:
97164562Sgshapiro				/* check if it even exists */
97264562Sgshapiro				if (stat(buf, &st) < 0 && errno == ENOENT)
97364562Sgshapiro				{
97464562Sgshapiro					if (bitnset(DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH,
97564562Sgshapiro						    DontBlameSendmail))
97664562Sgshapiro						break;
97764562Sgshapiro				}
97864562Sgshapiro				/* FALLTHROUGH */
97964562Sgshapiro
98038032Speter#if _FFR_FORWARD_SYSERR
98138032Speter			  case E_SM_NOSLINK:
98238032Speter			  case E_SM_NOHLINK:
98338032Speter			  case E_SM_REGONLY:
98438032Speter			  case E_SM_ISEXEC:
98538032Speter			  case E_SM_WWFILE:
98638032Speter			  case E_SM_GWFILE:
98790792Sgshapiro				syserr("forward: %s: %s", buf, sm_errstring(err));
98838032Speter				break;
98964562Sgshapiro#endif /* _FFR_FORWARD_SYSERR */
99038032Speter
99138032Speter			  default:
99238032Speter				if (LogLevel > (RunAsUid == 0 ? 2 : 10))
99338032Speter					sm_syslog(LOG_WARNING, e->e_id,
99490792Sgshapiro						  "forward %s: %s", buf,
99590792Sgshapiro						  sm_errstring(err));
99638032Speter				if (Verbose)
99738032Speter					message("forward: %s: %s",
99890792Sgshapiro						buf, sm_errstring(err));
99938032Speter				break;
100038032Speter			}
100138032Speter		}
100238032Speter	}
100338032Speter	if (pp == NULL && got_transient)
100438032Speter	{
100538032Speter		/*
100638032Speter		**  There was no successful .forward open and at least one
100738032Speter		**  transient open.  We have to defer this address for
100838032Speter		**  further delivery.
100938032Speter		*/
101038032Speter
101138032Speter		message("transient .forward open error: message queued");
101264562Sgshapiro		user->q_state = QS_QUEUEUP;
101338032Speter		return;
101438032Speter	}
101538032Speter}
1016