main.c revision 64562
138032Speter/*
264562Sgshapiro * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	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.
1138032Speter *
1238032Speter */
1338032Speter
1438032Speter#ifndef lint
1538032Speterstatic char copyright[] =
1664562Sgshapiro"@(#) Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.\n\
1764562Sgshapiro	All rights reserved.\n\
1838032Speter     Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
1938032Speter     Copyright (c) 1988, 1993\n\
2038032Speter	The Regents of the University of California.  All rights reserved.\n";
2164562Sgshapiro#endif /* ! lint */
2238032Speter
2338032Speter#ifndef lint
2464562Sgshapirostatic char id[] = "@(#)$Id: main.c,v 8.485.4.19 2000/06/29 01:31:02 gshapiro Exp $";
2564562Sgshapiro#endif /* ! lint */
2638032Speter
2738032Speter#define	_DEFINE
2838032Speter
2964562Sgshapiro#include <sendmail.h>
3038032Speter
3164562Sgshapiro
3264562Sgshapiro#if NETINET || NETINET6
3364562Sgshapiro# include <arpa/inet.h>
3464562Sgshapiro#endif /* NETINET || NETINET6 */
3564562Sgshapiro
3664562Sgshapirostatic void	dump_class __P((STAB *, int));
3764562Sgshapirostatic void	obsolete __P((char **));
3864562Sgshapirostatic void	testmodeline __P((char *, ENVELOPE *));
3964562Sgshapiro
4038032Speter/*
4138032Speter**  SENDMAIL -- Post mail to a set of destinations.
4238032Speter**
4338032Speter**	This is the basic mail router.  All user mail programs should
4438032Speter**	call this routine to actually deliver mail.  Sendmail in
4538032Speter**	turn calls a bunch of mail servers that do the real work of
4638032Speter**	delivering the mail.
4738032Speter**
4864562Sgshapiro**	Sendmail is driven by settings read in from /etc/mail/sendmail.cf
4938032Speter**	(read by readcf.c).
5038032Speter**
5138032Speter**	Usage:
5238032Speter**		/usr/lib/sendmail [flags] addr ...
5338032Speter**
5438032Speter**		See the associated documentation for details.
5538032Speter**
5638032Speter**	Author:
5738032Speter**		Eric Allman, UCB/INGRES (until 10/81).
5838032Speter**			     Britton-Lee, Inc., purveyors of fine
5938032Speter**				database computers (11/81 - 10/88).
6038032Speter**			     International Computer Science Institute
6138032Speter**				(11/88 - 9/89).
6238032Speter**			     UCB/Mammoth Project (10/89 - 7/95).
6338032Speter**			     InReference, Inc. (8/95 - 1/97).
6438032Speter**			     Sendmail, Inc. (1/98 - present).
6538032Speter**		The support of the my employers is gratefully acknowledged.
6638032Speter**			Few of them (Britton-Lee in particular) have had
6738032Speter**			anything to gain from my involvement in this project.
6838032Speter*/
6938032Speter
7038032Speter
7138032Speterint		NextMailer;	/* "free" index into Mailer struct */
7238032Speterchar		*FullName;	/* sender's full name */
7338032SpeterENVELOPE	BlankEnvelope;	/* a "blank" envelope */
7464562Sgshapirostatic ENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
7538032SpeterADDRESS		NullAddress =	/* a null address */
7638032Speter		{ "", "", NULL, "" };
7738032Speterchar		*CommandLineArgs;	/* command line args for pid file */
7838032Speterbool		Warn_Q_option = FALSE;	/* warn about Q option use */
7938032Speterchar		**SaveArgv;	/* argument vector for re-execing */
8064562Sgshapirostatic int	MissingFds = 0;	/* bit map of fds missing on startup */
8138032Speter
8238032Speter#ifdef NGROUPS_MAX
8338032SpeterGIDSET_T	InitialGidSet[NGROUPS_MAX];
8464562Sgshapiro#endif /* NGROUPS_MAX */
8538032Speter
8638032Speter#if DAEMON && !SMTP
8738032SpeterERROR %%%%   Cannot have DAEMON mode without SMTP   %%%% ERROR
8838032Speter#endif /* DAEMON && !SMTP */
8938032Speter#if SMTP && !QUEUE
9038032SpeterERROR %%%%   Cannot have SMTP mode without QUEUE   %%%% ERROR
9164562Sgshapiro#endif /* SMTP && !QUEUE */
9238032Speter
9364562Sgshapiro#define MAXCONFIGLEVEL	9	/* highest config version level known */
9438032Speter
9564562Sgshapiro#if SASL
9664562Sgshapirostatic sasl_callback_t srvcallbacks[] =
9764562Sgshapiro{
9864562Sgshapiro	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
9964562Sgshapiro	{	SASL_CB_PROXY_POLICY,	&proxy_policy,	NULL	},
10064562Sgshapiro	{	SASL_CB_LIST_END,	NULL,		NULL	}
10164562Sgshapiro};
10264562Sgshapiro#endif /* SASL */
10364562Sgshapiro
10464562Sgshapiroint SubmitMode;
10564562Sgshapiro
10638032Speterint
10738032Spetermain(argc, argv, envp)
10838032Speter	int argc;
10938032Speter	char **argv;
11038032Speter	char **envp;
11138032Speter{
11238032Speter	register char *p;
11338032Speter	char **av;
11438032Speter	extern char Version[];
11538032Speter	char *ep, *from;
11638032Speter	STAB *st;
11738032Speter	register int i;
11838032Speter	int j;
11964562Sgshapiro	int dp;
12038032Speter	bool safecf = TRUE;
12164562Sgshapiro	BITMAP256 *p_flags = NULL;	/* daemon flags */
12238032Speter	bool warn_C_flag = FALSE;
12364562Sgshapiro	bool auth = TRUE;		/* whether to set e_auth_param */
12438032Speter	char warn_f_flag = '\0';
12538032Speter	bool run_in_foreground = FALSE;	/* -bD mode */
12638032Speter	static bool reenter = FALSE;
12738032Speter	struct passwd *pw;
12838032Speter	struct hostent *hp;
12938032Speter	char *nullserver = NULL;
13064562Sgshapiro	char *authinfo = NULL;
13164562Sgshapiro	char *sysloglabel = NULL;	/* label for syslog */
13238032Speter	bool forged;
13364562Sgshapiro	struct stat traf_st;		/* for TrafficLog FIFO check */
13438032Speter	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
13538032Speter	static char rnamebuf[MAXNAME];	/* holds RealUserName */
13638032Speter	char *emptyenviron[1];
13738032Speter	QUEUE_CHAR *new;
13838032Speter	extern int DtableSize;
13938032Speter	extern int optind;
14038032Speter	extern int opterr;
14138032Speter	extern char *optarg;
14238032Speter	extern char **environ;
14338032Speter
14438032Speter	/*
14538032Speter	**  Check to see if we reentered.
14638032Speter	**	This would normally happen if e_putheader or e_putbody
14738032Speter	**	were NULL when invoked.
14838032Speter	*/
14938032Speter
15038032Speter	if (reenter)
15138032Speter	{
15238032Speter		syserr("main: reentered!");
15338032Speter		abort();
15438032Speter	}
15538032Speter	reenter = TRUE;
15638032Speter
15738032Speter	/* avoid null pointer dereferences */
15838032Speter	TermEscape.te_rv_on = TermEscape.te_rv_off = "";
15938032Speter
16038032Speter	/* do machine-dependent initializations */
16138032Speter	init_md(argc, argv);
16238032Speter
16364562Sgshapiro
16438032Speter	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
16538032Speter	DtableSize = getdtsize();
16638032Speter	if (DtableSize > 256)
16738032Speter		DtableSize = 256;
16838032Speter
16938032Speter	/*
17038032Speter	**  Be sure we have enough file descriptors.
17138032Speter	**	But also be sure that 0, 1, & 2 are open.
17238032Speter	*/
17338032Speter
17438032Speter	fill_fd(STDIN_FILENO, NULL);
17538032Speter	fill_fd(STDOUT_FILENO, NULL);
17638032Speter	fill_fd(STDERR_FILENO, NULL);
17738032Speter
17838032Speter	i = DtableSize;
17938032Speter	while (--i > 0)
18038032Speter	{
18138032Speter		if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
18238032Speter			(void) close(i);
18338032Speter	}
18438032Speter	errno = 0;
18538032Speter
18638032Speter#if LOG
18764562Sgshapiro#  ifdef LOG_MAIL
18838032Speter	openlog("sendmail", LOG_PID, LOG_MAIL);
18964562Sgshapiro#  else /* LOG_MAIL */
19038032Speter	openlog("sendmail", LOG_PID);
19164562Sgshapiro#  endif /* LOG_MAIL */
19264562Sgshapiro#endif /* LOG */
19338032Speter
19438032Speter	if (MissingFds != 0)
19538032Speter	{
19638032Speter		char mbuf[MAXLINE];
19738032Speter
19838032Speter		mbuf[0] = '\0';
19938032Speter		if (bitset(1 << STDIN_FILENO, MissingFds))
20064562Sgshapiro			(void) strlcat(mbuf, ", stdin", sizeof mbuf);
20138032Speter		if (bitset(1 << STDOUT_FILENO, MissingFds))
20264562Sgshapiro			(void) strlcat(mbuf, ", stdout", sizeof mbuf);
20338032Speter		if (bitset(1 << STDERR_FILENO, MissingFds))
20464562Sgshapiro			(void) strlcat(mbuf, ", stderr", sizeof mbuf);
20538032Speter		syserr("File descriptors missing on startup: %s", &mbuf[2]);
20638032Speter	}
20738032Speter
20838032Speter	/* reset status from syserr() calls for missing file descriptors */
20938032Speter	Errors = 0;
21038032Speter	ExitStat = EX_OK;
21138032Speter
21264562Sgshapiro	SubmitMode = SUBMIT_UNKNOWN;
21338032Speter#if XDEBUG
21438032Speter	checkfd012("after openlog");
21564562Sgshapiro#endif /* XDEBUG */
21638032Speter
21764562Sgshapiro	/*
21864562Sgshapiro	**  Seed the random number generator.
21964562Sgshapiro	**  Used for queue file names, picking a queue directory, and
22064562Sgshapiro	**  MX randomization.
22164562Sgshapiro	*/
22264562Sgshapiro
22364562Sgshapiro	seed_random();
22464562Sgshapiro
22538032Speter	tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
22638032Speter
22738032Speter#ifdef NGROUPS_MAX
22838032Speter	/* save initial group set for future checks */
22938032Speter	i = getgroups(NGROUPS_MAX, InitialGidSet);
23038032Speter	if (i == 0)
23138032Speter		InitialGidSet[0] = (GID_T) -1;
23238032Speter	while (i < NGROUPS_MAX)
23338032Speter		InitialGidSet[i++] = InitialGidSet[0];
23464562Sgshapiro#endif /* NGROUPS_MAX */
23538032Speter
23638032Speter	/* drop group id privileges (RunAsUser not yet set) */
23764562Sgshapiro	dp = drop_privileges(FALSE);
23864562Sgshapiro	setstat(dp);
23938032Speter
24064562Sgshapiro# ifdef SIGUSR1
24138032Speter	/* arrange to dump state on user-1 signal */
24264562Sgshapiro	(void) setsignal(SIGUSR1, sigusr1);
24364562Sgshapiro# endif /* SIGUSR1 */
24438032Speter
24538032Speter	/* initialize for setproctitle */
24638032Speter	initsetproctitle(argc, argv, envp);
24738032Speter
24838032Speter	/* Handle any non-getoptable constructions. */
24938032Speter	obsolete(argv);
25038032Speter
25138032Speter	/*
25238032Speter	**  Do a quick prescan of the argument list.
25338032Speter	*/
25438032Speter
25564562Sgshapiro
25638032Speter#if defined(__osf__) || defined(_AIX3)
25764562Sgshapiro# define OPTIONS	"B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:x"
25864562Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */
25938032Speter#if defined(sony_news)
26064562Sgshapiro# define OPTIONS	"B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
26164562Sgshapiro#endif /* defined(sony_news) */
26238032Speter#ifndef OPTIONS
26364562Sgshapiro# define OPTIONS	"B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
26464562Sgshapiro#endif /* ! OPTIONS */
26538032Speter	opterr = 0;
26638032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
26738032Speter	{
26838032Speter		switch (j)
26938032Speter		{
27038032Speter		  case 'd':
27138032Speter			/* hack attack -- see if should use ANSI mode */
27238032Speter			if (strcmp(optarg, "ANSI") == 0)
27338032Speter			{
27438032Speter				TermEscape.te_rv_on = "\033[7m";
27538032Speter				TermEscape.te_rv_off = "\033[0m";
27638032Speter				break;
27738032Speter			}
27838032Speter			tTflag(optarg);
27938032Speter			setbuf(stdout, (char *) NULL);
28038032Speter			break;
28164562Sgshapiro
28264562Sgshapiro		  case 'G':	/* relay (gateway) submission */
28364562Sgshapiro			SubmitMode |= SUBMIT_MTA;
28464562Sgshapiro			break;
28564562Sgshapiro
28664562Sgshapiro		  case 'L':
28764562Sgshapiro			j = min(strlen(optarg), 24) + 1;
28864562Sgshapiro			sysloglabel = xalloc(j);
28964562Sgshapiro			(void) strlcpy(sysloglabel, optarg, j);
29064562Sgshapiro			break;
29164562Sgshapiro
29264562Sgshapiro		  case 'U':	/* initial (user) submission */
29364562Sgshapiro			SubmitMode |= SUBMIT_MSA;
29464562Sgshapiro			break;
29538032Speter		}
29638032Speter	}
29738032Speter	opterr = 1;
29838032Speter
29964562Sgshapiro	if (sysloglabel != NULL)
30064562Sgshapiro	{
30164562Sgshapiro#if LOG
30264562Sgshapiro		closelog();
30364562Sgshapiro#  ifdef LOG_MAIL
30464562Sgshapiro		openlog(sysloglabel, LOG_PID, LOG_MAIL);
30564562Sgshapiro#  else /* LOG_MAIL */
30664562Sgshapiro		openlog(sysloglabel, LOG_PID);
30764562Sgshapiro#  endif /* LOG_MAIL */
30864562Sgshapiro#endif /* LOG */
30964562Sgshapiro	}
31064562Sgshapiro
31164562Sgshapiro
31238032Speter	/* set up the blank envelope */
31338032Speter	BlankEnvelope.e_puthdr = putheader;
31438032Speter	BlankEnvelope.e_putbody = putbody;
31538032Speter	BlankEnvelope.e_xfp = NULL;
31638032Speter	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
31738032Speter	CurEnv = &BlankEnvelope;
31838032Speter	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
31938032Speter
32038032Speter	/*
32138032Speter	**  Set default values for variables.
32238032Speter	**	These cannot be in initialized data space.
32338032Speter	*/
32438032Speter
32538032Speter	setdefaults(&BlankEnvelope);
32638032Speter
32738032Speter	RealUid = getuid();
32838032Speter	RealGid = getgid();
32938032Speter
33038032Speter	pw = sm_getpwuid(RealUid);
33138032Speter	if (pw != NULL)
33238032Speter		(void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
33338032Speter	else
33464562Sgshapiro		(void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
33564562Sgshapiro				(int) RealUid);
33664562Sgshapiro
33738032Speter	RealUserName = rnamebuf;
33838032Speter
33938032Speter	if (tTd(0, 101))
34038032Speter	{
34164562Sgshapiro		dprintf("Version %s\n", Version);
34242575Speter		finis(FALSE, EX_OK);
34338032Speter	}
34438032Speter
34538032Speter	/*
34638032Speter	**  if running non-setuid binary as non-root, pretend
34738032Speter	**  we are the RunAsUid
34838032Speter	*/
34938032Speter	if (RealUid != 0 && geteuid() == RealUid)
35038032Speter	{
35138032Speter		if (tTd(47, 1))
35264562Sgshapiro			dprintf("Non-setuid binary: RunAsUid = RealUid = %d\n",
35338032Speter				(int)RealUid);
35438032Speter		RunAsUid = RealUid;
35538032Speter	}
35638032Speter	else if (geteuid() != 0)
35738032Speter		RunAsUid = geteuid();
35838032Speter
35938032Speter	if (RealUid != 0 && getegid() == RealGid)
36038032Speter		RunAsGid = RealGid;
36138032Speter
36238032Speter	if (tTd(47, 5))
36338032Speter	{
36464562Sgshapiro		dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
36564562Sgshapiro			(int)geteuid(), (int)getuid(),
36664562Sgshapiro			(int)getegid(), (int)getgid());
36764562Sgshapiro		dprintf("main: RunAsUser = %d:%d\n",
36864562Sgshapiro			(int)RunAsUid, (int)RunAsGid);
36938032Speter	}
37038032Speter
37138032Speter	/* save command line arguments */
37264562Sgshapiro	j = 0;
37338032Speter	for (av = argv; *av != NULL; )
37464562Sgshapiro		j += strlen(*av++) + 1;
37538032Speter	SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
37664562Sgshapiro	CommandLineArgs = xalloc(j);
37738032Speter	p = CommandLineArgs;
37838032Speter	for (av = argv, i = 0; *av != NULL; )
37938032Speter	{
38064562Sgshapiro		int h;
38164562Sgshapiro
38238032Speter		SaveArgv[i++] = newstr(*av);
38338032Speter		if (av != argv)
38438032Speter			*p++ = ' ';
38564562Sgshapiro		(void) strlcpy(p, *av++, j);
38664562Sgshapiro		h = strlen(p);
38764562Sgshapiro		p += h;
38864562Sgshapiro		j -= h + 1;
38938032Speter	}
39038032Speter	SaveArgv[i] = NULL;
39138032Speter
39238032Speter	if (tTd(0, 1))
39338032Speter	{
39438032Speter		int ll;
39538032Speter		extern char *CompileOptions[];
39638032Speter
39764562Sgshapiro		dprintf("Version %s\n Compiled with:", Version);
39838032Speter		av = CompileOptions;
39938032Speter		ll = 7;
40038032Speter		while (*av != NULL)
40138032Speter		{
40238032Speter			if (ll + strlen(*av) > 63)
40338032Speter			{
40464562Sgshapiro				dprintf("\n");
40538032Speter				ll = 0;
40638032Speter			}
40738032Speter			if (ll == 0)
40864562Sgshapiro				dprintf("\t\t");
40938032Speter			else
41064562Sgshapiro				dprintf(" ");
41164562Sgshapiro			dprintf("%s", *av);
41238032Speter			ll += strlen(*av++) + 1;
41338032Speter		}
41464562Sgshapiro		dprintf("\n");
41538032Speter	}
41638032Speter	if (tTd(0, 10))
41738032Speter	{
41838032Speter		int ll;
41938032Speter		extern char *OsCompileOptions[];
42038032Speter
42164562Sgshapiro		dprintf("    OS Defines:");
42238032Speter		av = OsCompileOptions;
42338032Speter		ll = 7;
42438032Speter		while (*av != NULL)
42538032Speter		{
42638032Speter			if (ll + strlen(*av) > 63)
42738032Speter			{
42864562Sgshapiro				dprintf("\n");
42938032Speter				ll = 0;
43038032Speter			}
43138032Speter			if (ll == 0)
43264562Sgshapiro				dprintf("\t\t");
43338032Speter			else
43464562Sgshapiro				dprintf(" ");
43564562Sgshapiro			dprintf("%s", *av);
43638032Speter			ll += strlen(*av++) + 1;
43738032Speter		}
43864562Sgshapiro		dprintf("\n");
43938032Speter#ifdef _PATH_UNIX
44064562Sgshapiro		dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
44164562Sgshapiro#endif /* _PATH_UNIX */
44264562Sgshapiro		dprintf(" Def Conf file:\t%s\n", getcfname());
44364562Sgshapiro		dprintf("  Def Pid file:\t%s\n", PidFile);
44438032Speter	}
44538032Speter
44638032Speter	InChannel = stdin;
44738032Speter	OutChannel = stdout;
44838032Speter
44938032Speter	/* clear sendmail's environment */
45038032Speter	ExternalEnviron = environ;
45138032Speter	emptyenviron[0] = NULL;
45238032Speter	environ = emptyenviron;
45338032Speter
45438032Speter	/*
45542575Speter	**  restore any original TZ setting until TimeZoneSpec has been
45642575Speter	**  determined - or early log messages may get bogus time stamps
45738032Speter	*/
45838032Speter	if ((p = getextenv("TZ")) != NULL)
45938032Speter	{
46038032Speter		char *tz;
46138032Speter		int tzlen;
46238032Speter
46338032Speter		tzlen = strlen(p) + 4;
46438032Speter		tz = xalloc(tzlen);
46564562Sgshapiro		(void) snprintf(tz, tzlen, "TZ=%s", p);
46664562Sgshapiro		(void) putenv(tz);
46738032Speter	}
46838032Speter
46938032Speter	/* prime the child environment */
47038032Speter	setuserenv("AGENT", "sendmail");
47138032Speter
47238032Speter	if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
47338032Speter		(void) setsignal(SIGINT, intsig);
47438032Speter	(void) setsignal(SIGTERM, intsig);
47538032Speter	(void) setsignal(SIGPIPE, SIG_IGN);
47638032Speter	OldUmask = umask(022);
47738032Speter	OpMode = MD_DELIVER;
47838032Speter	FullName = getextenv("NAME");
47938032Speter
48038032Speter	/*
48138032Speter	**  Initialize name server if it is going to be used.
48238032Speter	*/
48338032Speter
48438032Speter#if NAMED_BIND
48538032Speter	if (!bitset(RES_INIT, _res.options))
48664562Sgshapiro		(void) res_init();
48764562Sgshapiro
48864562Sgshapiro	/*
48964562Sgshapiro	**  hack to avoid crashes when debugging for the resolver is
49064562Sgshapiro	**  turned on and sfio is used
49164562Sgshapiro	*/
49238032Speter	if (tTd(8, 8))
49364562Sgshapiro# if !SFIO || SFIO_STDIO_COMPAT
49438032Speter		_res.options |= RES_DEBUG;
49564562Sgshapiro# else /* !SFIO || SFIO_STDIO_COMPAT */
49664562Sgshapiro		dprintf("RES_DEBUG not available due to SFIO\n");
49764562Sgshapiro# endif /* !SFIO || SFIO_STDIO_COMPAT */
49838032Speter	else
49938032Speter		_res.options &= ~RES_DEBUG;
50038032Speter# ifdef RES_NOALIASES
50138032Speter	_res.options |= RES_NOALIASES;
50264562Sgshapiro# endif /* RES_NOALIASES */
50364562Sgshapiro	TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
50464562Sgshapiro	TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
50564562Sgshapiro	TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
50664562Sgshapiro	TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
50764562Sgshapiro	TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
50864562Sgshapiro	TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
50964562Sgshapiro#endif /* NAMED_BIND */
51038032Speter
51138032Speter	errno = 0;
51238032Speter	from = NULL;
51338032Speter
51438032Speter	/* initialize some macros, etc. */
51538032Speter	initmacros(CurEnv);
51638032Speter	init_vendor_macros(CurEnv);
51738032Speter
51838032Speter	/* version */
51938032Speter	define('v', Version, CurEnv);
52038032Speter
52138032Speter	/* hostname */
52238032Speter	hp = myhostname(jbuf, sizeof jbuf);
52338032Speter	if (jbuf[0] != '\0')
52438032Speter	{
52538032Speter		struct	utsname	utsname;
52638032Speter
52738032Speter		if (tTd(0, 4))
52864562Sgshapiro			dprintf("canonical name: %s\n", jbuf);
52938032Speter		define('w', newstr(jbuf), CurEnv);	/* must be new string */
53038032Speter		define('j', newstr(jbuf), CurEnv);
53138032Speter		setclass('w', jbuf);
53238032Speter
53338032Speter		p = strchr(jbuf, '.');
53438032Speter		if (p != NULL)
53538032Speter		{
53638032Speter			if (p[1] != '\0')
53738032Speter			{
53838032Speter				define('m', newstr(&p[1]), CurEnv);
53938032Speter			}
54038032Speter			while (p != NULL && strchr(&p[1], '.') != NULL)
54138032Speter			{
54238032Speter				*p = '\0';
54338032Speter				if (tTd(0, 4))
54464562Sgshapiro					dprintf("\ta.k.a.: %s\n", jbuf);
54538032Speter				setclass('w', jbuf);
54638032Speter				*p++ = '.';
54738032Speter				p = strchr(p, '.');
54838032Speter			}
54938032Speter		}
55038032Speter
55138032Speter		if (uname(&utsname) >= 0)
55238032Speter			p = utsname.nodename;
55338032Speter		else
55438032Speter		{
55538032Speter			if (tTd(0, 22))
55664562Sgshapiro				dprintf("uname failed (%s)\n",
55764562Sgshapiro					errstring(errno));
55838032Speter			makelower(jbuf);
55938032Speter			p = jbuf;
56038032Speter		}
56138032Speter		if (tTd(0, 4))
56264562Sgshapiro			dprintf(" UUCP nodename: %s\n", p);
56338032Speter		p = newstr(p);
56438032Speter		define('k', p, CurEnv);
56538032Speter		setclass('k', p);
56638032Speter		setclass('w', p);
56738032Speter	}
56838032Speter	if (hp != NULL)
56938032Speter	{
57038032Speter		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
57138032Speter		{
57238032Speter			if (tTd(0, 4))
57364562Sgshapiro				dprintf("\ta.k.a.: %s\n", *av);
57438032Speter			setclass('w', *av);
57538032Speter		}
57664562Sgshapiro#if NETINET || NETINET6
57764562Sgshapiro		for (i = 0; hp->h_addr_list[i] != NULL; i++)
57838032Speter		{
57964562Sgshapiro# if NETINET6
58064562Sgshapiro			char *addr;
58164562Sgshapiro			char buf6[INET6_ADDRSTRLEN];
58264562Sgshapiro			struct in6_addr ia6;
58364562Sgshapiro# endif /* NETINET6 */
58464562Sgshapiro# if NETINET
58564562Sgshapiro			struct in_addr ia;
58664562Sgshapiro# endif /* NETINET */
58764562Sgshapiro			char ipbuf[103];
58864562Sgshapiro
58964562Sgshapiro			ipbuf[0] = '\0';
59064562Sgshapiro			switch (hp->h_addrtype)
59138032Speter			{
59264562Sgshapiro# if NETINET
59364562Sgshapiro			  case AF_INET:
59464562Sgshapiro				if (hp->h_length != INADDRSZ)
59564562Sgshapiro					break;
59638032Speter
59764562Sgshapiro				memmove(&ia, hp->h_addr_list[i], INADDRSZ);
59864562Sgshapiro				(void) snprintf(ipbuf,	 sizeof ipbuf,
59964562Sgshapiro						"[%.100s]", inet_ntoa(ia));
60064562Sgshapiro				break;
60164562Sgshapiro# endif /* NETINET */
60264562Sgshapiro
60364562Sgshapiro# if NETINET6
60464562Sgshapiro			  case AF_INET6:
60564562Sgshapiro				if (hp->h_length != IN6ADDRSZ)
60664562Sgshapiro					break;
60764562Sgshapiro
60864562Sgshapiro				memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
60964562Sgshapiro				addr = anynet_ntop(&ia6, buf6, sizeof buf6);
61064562Sgshapiro				if (addr != NULL)
61164562Sgshapiro					(void) snprintf(ipbuf, sizeof ipbuf,
61264562Sgshapiro							"[%.100s]", addr);
61364562Sgshapiro				break;
61464562Sgshapiro# endif /* NETINET6 */
61538032Speter			}
61664562Sgshapiro			if (ipbuf[0] == '\0')
61764562Sgshapiro				break;
61864562Sgshapiro
61964562Sgshapiro			if (tTd(0, 4))
62064562Sgshapiro				dprintf("\ta.k.a.: %s\n", ipbuf);
62164562Sgshapiro			setclass('w', ipbuf);
62238032Speter		}
62364562Sgshapiro#endif /* NETINET || NETINET6 */
62438032Speter	}
62538032Speter
62638032Speter	/* current time */
62738032Speter	define('b', arpadate((char *) NULL), CurEnv);
62864562Sgshapiro	/* current load average */
62964562Sgshapiro	CurrentLA = sm_getla(CurEnv);
63038032Speter
63138032Speter	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
63238032Speter	QueueLimitSender = (QUEUE_CHAR *) NULL;
63338032Speter	QueueLimitId = (QUEUE_CHAR *) NULL;
63438032Speter
63538032Speter	/*
63642575Speter	**  Crack argv.
63738032Speter	*/
63838032Speter
63938032Speter	av = argv;
64038032Speter	p = strrchr(*av, '/');
64138032Speter	if (p++ == NULL)
64238032Speter		p = *av;
64338032Speter	if (strcmp(p, "newaliases") == 0)
64438032Speter		OpMode = MD_INITALIAS;
64538032Speter	else if (strcmp(p, "mailq") == 0)
64638032Speter		OpMode = MD_PRINT;
64738032Speter	else if (strcmp(p, "smtpd") == 0)
64838032Speter		OpMode = MD_DAEMON;
64938032Speter	else if (strcmp(p, "hoststat") == 0)
65038032Speter		OpMode = MD_HOSTSTAT;
65138032Speter	else if (strcmp(p, "purgestat") == 0)
65238032Speter		OpMode = MD_PURGESTAT;
65338032Speter
65438032Speter	optind = 1;
65538032Speter	while ((j = getopt(argc, argv, OPTIONS)) != -1)
65638032Speter	{
65738032Speter		switch (j)
65838032Speter		{
65938032Speter		  case 'b':	/* operations mode */
66038032Speter			switch (j = *optarg)
66138032Speter			{
66238032Speter			  case MD_DAEMON:
66338032Speter			  case MD_FGDAEMON:
66464562Sgshapiro#if !DAEMON
66538032Speter				usrerr("Daemon mode not implemented");
66638032Speter				ExitStat = EX_USAGE;
66738032Speter				break;
66864562Sgshapiro#endif /* !DAEMON */
66938032Speter			  case MD_SMTP:
67064562Sgshapiro#if !SMTP
67138032Speter				usrerr("I don't speak SMTP");
67238032Speter				ExitStat = EX_USAGE;
67338032Speter				break;
67464562Sgshapiro#endif /* !SMTP */
67538032Speter
67638032Speter			  case MD_INITALIAS:
67738032Speter			  case MD_DELIVER:
67838032Speter			  case MD_VERIFY:
67938032Speter			  case MD_TEST:
68038032Speter			  case MD_PRINT:
68138032Speter			  case MD_HOSTSTAT:
68238032Speter			  case MD_PURGESTAT:
68338032Speter			  case MD_ARPAFTP:
68438032Speter				OpMode = j;
68538032Speter				break;
68638032Speter
68738032Speter			  case MD_FREEZE:
68838032Speter				usrerr("Frozen configurations unsupported");
68938032Speter				ExitStat = EX_USAGE;
69038032Speter				break;
69138032Speter
69238032Speter			  default:
69338032Speter				usrerr("Invalid operation mode %c", j);
69438032Speter				ExitStat = EX_USAGE;
69538032Speter				break;
69638032Speter			}
69738032Speter			break;
69838032Speter
69938032Speter		  case 'B':	/* body type */
70038032Speter			CurEnv->e_bodytype = optarg;
70138032Speter			break;
70238032Speter
70338032Speter		  case 'C':	/* select configuration file (already done) */
70438032Speter			if (RealUid != 0)
70538032Speter				warn_C_flag = TRUE;
70638032Speter			ConfFile = optarg;
70764562Sgshapiro			dp = drop_privileges(TRUE);
70864562Sgshapiro			setstat(dp);
70938032Speter			safecf = FALSE;
71038032Speter			break;
71138032Speter
71238032Speter		  case 'd':	/* debugging -- already done */
71338032Speter			break;
71438032Speter
71538032Speter		  case 'f':	/* from address */
71638032Speter		  case 'r':	/* obsolete -f flag */
71738032Speter			if (from != NULL)
71838032Speter			{
71938032Speter				usrerr("More than one \"from\" person");
72038032Speter				ExitStat = EX_USAGE;
72138032Speter				break;
72238032Speter			}
72338032Speter			from = newstr(denlstring(optarg, TRUE, TRUE));
72438032Speter			if (strcmp(RealUserName, from) != 0)
72538032Speter				warn_f_flag = j;
72638032Speter			break;
72738032Speter
72838032Speter		  case 'F':	/* set full name */
72938032Speter			FullName = newstr(optarg);
73038032Speter			break;
73138032Speter
73264562Sgshapiro		  case 'G':	/* relay (gateway) submission */
73364562Sgshapiro			/* already set */
73464562Sgshapiro			break;
73564562Sgshapiro
73638032Speter		  case 'h':	/* hop count */
73764562Sgshapiro			CurEnv->e_hopcount = (short) strtol(optarg, &ep, 10);
73838032Speter			if (*ep)
73938032Speter			{
74038032Speter				usrerr("Bad hop count (%s)", optarg);
74138032Speter				ExitStat = EX_USAGE;
74238032Speter			}
74338032Speter			break;
74464562Sgshapiro
74564562Sgshapiro		  case 'L':	/* program label */
74664562Sgshapiro			/* already set */
74764562Sgshapiro			break;
74864562Sgshapiro
74938032Speter		  case 'n':	/* don't alias */
75038032Speter			NoAlias = TRUE;
75138032Speter			break;
75238032Speter
75338032Speter		  case 'N':	/* delivery status notifications */
75438032Speter			DefaultNotify |= QHASNOTIFY;
75564562Sgshapiro			define(macid("{dsn_notify}", NULL),
75664562Sgshapiro			       newstr(optarg), CurEnv);
75738032Speter			if (strcasecmp(optarg, "never") == 0)
75838032Speter				break;
75938032Speter			for (p = optarg; p != NULL; optarg = p)
76038032Speter			{
76138032Speter				p = strchr(p, ',');
76238032Speter				if (p != NULL)
76338032Speter					*p++ = '\0';
76438032Speter				if (strcasecmp(optarg, "success") == 0)
76538032Speter					DefaultNotify |= QPINGONSUCCESS;
76638032Speter				else if (strcasecmp(optarg, "failure") == 0)
76738032Speter					DefaultNotify |= QPINGONFAILURE;
76838032Speter				else if (strcasecmp(optarg, "delay") == 0)
76938032Speter					DefaultNotify |= QPINGONDELAY;
77038032Speter				else
77138032Speter				{
77238032Speter					usrerr("Invalid -N argument");
77338032Speter					ExitStat = EX_USAGE;
77438032Speter				}
77538032Speter			}
77638032Speter			break;
77738032Speter
77838032Speter		  case 'o':	/* set option */
77938032Speter			setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
78038032Speter			break;
78138032Speter
78238032Speter		  case 'O':	/* set option (long form) */
78338032Speter			setoption(' ', optarg, FALSE, TRUE, CurEnv);
78438032Speter			break;
78538032Speter
78638032Speter		  case 'p':	/* set protocol */
78738032Speter			p = strchr(optarg, ':');
78838032Speter			if (p != NULL)
78938032Speter			{
79038032Speter				*p++ = '\0';
79138032Speter				if (*p != '\0')
79238032Speter				{
79338032Speter					ep = xalloc(strlen(p) + 1);
79438032Speter					cleanstrcpy(ep, p, MAXNAME);
79538032Speter					define('s', ep, CurEnv);
79638032Speter				}
79738032Speter			}
79838032Speter			if (*optarg != '\0')
79938032Speter			{
80038032Speter				ep = xalloc(strlen(optarg) + 1);
80138032Speter				cleanstrcpy(ep, optarg, MAXNAME);
80238032Speter				define('r', ep, CurEnv);
80338032Speter			}
80438032Speter			break;
80538032Speter
80638032Speter		  case 'q':	/* run queue files at intervals */
80764562Sgshapiro#if QUEUE
80864562Sgshapiro			/* sanity check */
80964562Sgshapiro			if (OpMode != MD_DELIVER &&
81064562Sgshapiro			    OpMode != MD_DAEMON &&
81164562Sgshapiro			    OpMode != MD_FGDAEMON &&
81264562Sgshapiro			    OpMode != MD_PRINT &&
81364562Sgshapiro			    OpMode != MD_QUEUERUN)
81464562Sgshapiro			{
81564562Sgshapiro				usrerr("Can not use -q with -b%c", OpMode);
81664562Sgshapiro				ExitStat = EX_USAGE;
81764562Sgshapiro				break;
81864562Sgshapiro			}
81964562Sgshapiro
82064562Sgshapiro			/* don't override -bd, -bD or -bp */
82164562Sgshapiro			if (OpMode == MD_DELIVER)
82264562Sgshapiro				OpMode = MD_QUEUERUN;
82364562Sgshapiro
82438032Speter			FullName = NULL;
82564562Sgshapiro
82638032Speter			switch (optarg[0])
82738032Speter			{
82838032Speter			  case 'I':
82964562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
83038032Speter				new->queue_match = newstr(&optarg[1]);
83138032Speter				new->queue_next = QueueLimitId;
83238032Speter				QueueLimitId = new;
83338032Speter				break;
83438032Speter
83538032Speter			  case 'R':
83664562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
83738032Speter				new->queue_match = newstr(&optarg[1]);
83838032Speter				new->queue_next = QueueLimitRecipient;
83938032Speter				QueueLimitRecipient = new;
84038032Speter				break;
84138032Speter
84238032Speter			  case 'S':
84364562Sgshapiro				new = (QUEUE_CHAR *) xalloc(sizeof *new);
84438032Speter				new->queue_match = newstr(&optarg[1]);
84538032Speter				new->queue_next = QueueLimitSender;
84638032Speter				QueueLimitSender = new;
84738032Speter				break;
84838032Speter
84938032Speter			  default:
85064562Sgshapiro				i = Errors;
85138032Speter				QueueIntvl = convtime(optarg, 'm');
85264562Sgshapiro
85364562Sgshapiro				/* check for bad conversion */
85464562Sgshapiro				if (i < Errors)
85564562Sgshapiro					ExitStat = EX_USAGE;
85638032Speter				break;
85738032Speter			}
85864562Sgshapiro#else /* QUEUE */
85938032Speter			usrerr("I don't know about queues");
86038032Speter			ExitStat = EX_USAGE;
86164562Sgshapiro#endif /* QUEUE */
86238032Speter			break;
86338032Speter
86438032Speter		  case 'R':	/* DSN RET: what to return */
86538032Speter			if (bitset(EF_RET_PARAM, CurEnv->e_flags))
86638032Speter			{
86738032Speter				usrerr("Duplicate -R flag");
86838032Speter				ExitStat = EX_USAGE;
86938032Speter				break;
87038032Speter			}
87138032Speter			CurEnv->e_flags |= EF_RET_PARAM;
87238032Speter			if (strcasecmp(optarg, "hdrs") == 0)
87338032Speter				CurEnv->e_flags |= EF_NO_BODY_RETN;
87438032Speter			else if (strcasecmp(optarg, "full") != 0)
87538032Speter			{
87638032Speter				usrerr("Invalid -R value");
87738032Speter				ExitStat = EX_USAGE;
87838032Speter			}
87964562Sgshapiro			define(macid("{dsn_ret}", NULL),
88064562Sgshapiro			       newstr(optarg), CurEnv);
88138032Speter			break;
88238032Speter
88338032Speter		  case 't':	/* read recipients from message */
88438032Speter			GrabTo = TRUE;
88538032Speter			break;
88638032Speter
88738032Speter		  case 'U':	/* initial (user) submission */
88864562Sgshapiro			/* already set */
88938032Speter			break;
89038032Speter
89138032Speter		  case 'V':	/* DSN ENVID: set "original" envelope id */
89238032Speter			if (!xtextok(optarg))
89338032Speter			{
89438032Speter				usrerr("Invalid syntax in -V flag");
89538032Speter				ExitStat = EX_USAGE;
89638032Speter			}
89738032Speter			else
89864562Sgshapiro			{
89938032Speter				CurEnv->e_envid = newstr(optarg);
90064562Sgshapiro				define(macid("{dsn_envid}", NULL),
90164562Sgshapiro				       newstr(optarg), CurEnv);
90264562Sgshapiro			}
90338032Speter			break;
90438032Speter
90538032Speter		  case 'X':	/* traffic log file */
90664562Sgshapiro			dp = drop_privileges(TRUE);
90764562Sgshapiro			setstat(dp);
90864562Sgshapiro			if (stat(optarg, &traf_st) == 0 &&
90964562Sgshapiro			    S_ISFIFO(traf_st.st_mode))
91064562Sgshapiro				TrafficLogFile = fopen(optarg, "w");
91164562Sgshapiro			else
91264562Sgshapiro				TrafficLogFile = fopen(optarg, "a");
91338032Speter			if (TrafficLogFile == NULL)
91438032Speter			{
91538032Speter				syserr("cannot open %s", optarg);
91638032Speter				ExitStat = EX_CANTCREAT;
91738032Speter				break;
91838032Speter			}
91964562Sgshapiro#if HASSETVBUF
92064562Sgshapiro			(void) setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
92164562Sgshapiro#else /* HASSETVBUF */
92264562Sgshapiro			(void) setlinebuf(TrafficLogFile);
92364562Sgshapiro#endif /* HASSETVBUF */
92438032Speter			break;
92538032Speter
92638032Speter			/* compatibility flags */
92738032Speter		  case 'c':	/* connect to non-local mailers */
92838032Speter		  case 'i':	/* don't let dot stop me */
92938032Speter		  case 'm':	/* send to me too */
93038032Speter		  case 'T':	/* set timeout interval */
93138032Speter		  case 'v':	/* give blow-by-blow description */
93238032Speter			setoption(j, "T", FALSE, TRUE, CurEnv);
93338032Speter			break;
93438032Speter
93538032Speter		  case 'e':	/* error message disposition */
93638032Speter		  case 'M':	/* define macro */
93738032Speter			setoption(j, optarg, FALSE, TRUE, CurEnv);
93838032Speter			break;
93938032Speter
94038032Speter		  case 's':	/* save From lines in headers */
94138032Speter			setoption('f', "T", FALSE, TRUE, CurEnv);
94238032Speter			break;
94338032Speter
94464562Sgshapiro#ifdef DBM
94538032Speter		  case 'I':	/* initialize alias DBM file */
94638032Speter			OpMode = MD_INITALIAS;
94738032Speter			break;
94864562Sgshapiro#endif /* DBM */
94938032Speter
95064562Sgshapiro#if defined(__osf__) || defined(_AIX3)
95138032Speter		  case 'x':	/* random flag that OSF/1 & AIX mailx passes */
95238032Speter			break;
95364562Sgshapiro#endif /* defined(__osf__) || defined(_AIX3) */
95464562Sgshapiro#if defined(sony_news)
95538032Speter		  case 'E':
95638032Speter		  case 'J':	/* ignore flags for Japanese code conversion
95764562Sgshapiro				   implemented on Sony NEWS */
95838032Speter			break;
95964562Sgshapiro#endif /* defined(sony_news) */
96038032Speter
96138032Speter		  default:
96242575Speter			finis(TRUE, EX_USAGE);
96338032Speter			break;
96438032Speter		}
96538032Speter	}
96638032Speter	av += optind;
96738032Speter
96864562Sgshapiro	if (bitset(SUBMIT_MTA, SubmitMode) &&
96964562Sgshapiro	    bitset(SUBMIT_MSA, SubmitMode))
97064562Sgshapiro	{
97164562Sgshapiro		/* sanity check */
97264562Sgshapiro		errno = 0;	/* reset to avoid bogus error messages */
97364562Sgshapiro		syserr("Cannot use both -G and -U together");
97464562Sgshapiro	}
97564562Sgshapiro	else if (bitset(SUBMIT_MTA, SubmitMode))
97664562Sgshapiro		define(macid("{daemon_flags}", NULL), "CC f", CurEnv);
97764562Sgshapiro	else if (bitset(SUBMIT_MSA, SubmitMode))
97864562Sgshapiro	{
97964562Sgshapiro		define(macid("{daemon_flags}", NULL), "c u", CurEnv);
98064562Sgshapiro
98164562Sgshapiro		/* check for wrong OpMode */
98264562Sgshapiro		if (OpMode != MD_DELIVER && OpMode != MD_SMTP)
98364562Sgshapiro		{
98464562Sgshapiro			errno = 0;	/* reset to avoid bogus error msgs */
98564562Sgshapiro			syserr("Cannot use -U and -b%c", OpMode);
98664562Sgshapiro		}
98764562Sgshapiro	}
98864562Sgshapiro	else
98964562Sgshapiro	{
99064562Sgshapiro#if _FFR_DEFAULT_SUBMIT_TO_MSA
99164562Sgshapiro		define(macid("{daemon_flags}", NULL), "c u", CurEnv);
99264562Sgshapiro#else /* _FFR_DEFAULT_SUBMIT_TO_MSA */
99364562Sgshapiro		/* EMPTY */
99464562Sgshapiro#endif /* _FFR_DEFAULT_SUBMIT_TO_MSA */
99564562Sgshapiro	}
99664562Sgshapiro
99738032Speter	/*
99838032Speter	**  Do basic initialization.
99938032Speter	**	Read system control file.
100038032Speter	**	Extract special fields for local use.
100138032Speter	*/
100238032Speter
100338032Speter	/* set up ${opMode} for use in config file */
100438032Speter	{
100538032Speter		char mbuf[2];
100638032Speter
100738032Speter		mbuf[0] = OpMode;
100838032Speter		mbuf[1] = '\0';
100938032Speter		define(MID_OPMODE, newstr(mbuf), CurEnv);
101038032Speter	}
101138032Speter
101238032Speter#if XDEBUG
101338032Speter	checkfd012("before readcf");
101464562Sgshapiro#endif /* XDEBUG */
101538032Speter	vendor_pre_defaults(CurEnv);
101664562Sgshapiro
101738032Speter	readcf(getcfname(), safecf, CurEnv);
101838032Speter	ConfigFileRead = TRUE;
101938032Speter	vendor_post_defaults(CurEnv);
102038032Speter
102138032Speter	/* Enforce use of local time (null string overrides this) */
102238032Speter	if (TimeZoneSpec == NULL)
102338032Speter		unsetenv("TZ");
102438032Speter	else if (TimeZoneSpec[0] != '\0')
102538032Speter		setuserenv("TZ", TimeZoneSpec);
102638032Speter	else
102738032Speter		setuserenv("TZ", NULL);
102838032Speter	tzset();
102938032Speter
103038032Speter	/* avoid denial-of-service attacks */
103138032Speter	resetlimits();
103238032Speter
103338032Speter	if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
103438032Speter	{
103538032Speter		/* drop privileges -- daemon mode done after socket/bind */
103664562Sgshapiro		dp = drop_privileges(FALSE);
103764562Sgshapiro		setstat(dp);
103838032Speter	}
103938032Speter
104064562Sgshapiro#if NAMED_BIND
104164562Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
104264562Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
104364562Sgshapiro#endif /* NAMED_BIND */
104464562Sgshapiro
104538032Speter	/*
104638032Speter	**  Find our real host name for future logging.
104738032Speter	*/
104838032Speter
104964562Sgshapiro	authinfo = getauthinfo(STDIN_FILENO, &forged);
105064562Sgshapiro	define('_', authinfo, CurEnv);
105138032Speter
105238032Speter	/* suppress error printing if errors mailed back or whatever */
105338032Speter	if (CurEnv->e_errormode != EM_PRINT)
105438032Speter		HoldErrs = TRUE;
105538032Speter
105638032Speter	/* set up the $=m class now, after .cf has a chance to redefine $m */
105738032Speter	expand("\201m", jbuf, sizeof jbuf, CurEnv);
105838032Speter	setclass('m', jbuf);
105938032Speter
106038032Speter	/* probe interfaces and locate any additional names */
106138032Speter	if (!DontProbeInterfaces)
106238032Speter		load_if_names();
106338032Speter
106438032Speter	if (tTd(0, 1))
106538032Speter	{
106664562Sgshapiro		dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
106764562Sgshapiro		dprintf("\n      (short domain name) $w = ");
106838032Speter		xputs(macvalue('w', CurEnv));
106964562Sgshapiro		dprintf("\n  (canonical domain name) $j = ");
107038032Speter		xputs(macvalue('j', CurEnv));
107164562Sgshapiro		dprintf("\n         (subdomain name) $m = ");
107238032Speter		xputs(macvalue('m', CurEnv));
107364562Sgshapiro		dprintf("\n              (node name) $k = ");
107438032Speter		xputs(macvalue('k', CurEnv));
107564562Sgshapiro		dprintf("\n========================================================\n\n");
107638032Speter	}
107738032Speter
107838032Speter	/*
107938032Speter	**  Do more command line checking -- these are things that
108038032Speter	**  have to modify the results of reading the config file.
108138032Speter	*/
108238032Speter
108338032Speter	/* process authorization warnings from command line */
108438032Speter	if (warn_C_flag)
108538032Speter		auth_warning(CurEnv, "Processed by %s with -C %s",
108638032Speter			RealUserName, ConfFile);
108764562Sgshapiro	if (Warn_Q_option && !wordinclass(RealUserName, 't'))
108838032Speter		auth_warning(CurEnv, "Processed from queue %s", QueueDir);
108938032Speter
109038032Speter	/* check body type for legality */
109138032Speter	if (CurEnv->e_bodytype == NULL)
109264562Sgshapiro		/* EMPTY */
109338032Speter		/* nothing */ ;
109438032Speter	else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
109538032Speter		SevenBitInput = TRUE;
109638032Speter	else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
109738032Speter		SevenBitInput = FALSE;
109838032Speter	else
109938032Speter	{
110038032Speter		usrerr("Illegal body type %s", CurEnv->e_bodytype);
110138032Speter		CurEnv->e_bodytype = NULL;
110238032Speter	}
110338032Speter
110438032Speter	/* tweak default DSN notifications */
110538032Speter	if (DefaultNotify == 0)
110638032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
110738032Speter
110838032Speter	/* be sure we don't pick up bogus HOSTALIASES environment variable */
110964562Sgshapiro	if (OpMode == MD_QUEUERUN && RealUid != 0)
111038032Speter		(void) unsetenv("HOSTALIASES");
111138032Speter
111238032Speter	/* check for sane configuration level */
111338032Speter	if (ConfigLevel > MAXCONFIGLEVEL)
111438032Speter	{
111538032Speter		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
111638032Speter			ConfigLevel, Version, MAXCONFIGLEVEL);
111738032Speter	}
111838032Speter
111938032Speter	/* need MCI cache to have persistence */
112038032Speter	if (HostStatDir != NULL && MaxMciCache == 0)
112138032Speter	{
112238032Speter		HostStatDir = NULL;
112338032Speter		printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
112438032Speter	}
112538032Speter
112638032Speter	/* need HostStatusDir in order to have SingleThreadDelivery */
112738032Speter	if (SingleThreadDelivery && HostStatDir == NULL)
112838032Speter	{
112938032Speter		SingleThreadDelivery = FALSE;
113038032Speter		printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n");
113138032Speter	}
113238032Speter
113338032Speter	/* check for permissions */
113442575Speter	if ((OpMode == MD_DAEMON ||
113542575Speter	     OpMode == MD_FGDAEMON ||
113642575Speter	     OpMode == MD_PURGESTAT) &&
113742575Speter	    RealUid != 0 &&
113842575Speter	    RealUid != TrustedUid)
113938032Speter	{
114038032Speter		if (LogLevel > 1)
114138032Speter			sm_syslog(LOG_ALERT, NOQID,
114264562Sgshapiro				  "user %d attempted to %s",
114364562Sgshapiro				  RealUid,
114464562Sgshapiro				  OpMode != MD_PURGESTAT ? "run daemon"
114564562Sgshapiro							 : "purge host status");
114638032Speter		usrerr("Permission denied");
114742575Speter		finis(FALSE, EX_USAGE);
114838032Speter	}
114953696Speter	if (OpMode == MD_INITALIAS &&
115053696Speter	    RealUid != 0 &&
115153696Speter	    RealUid != TrustedUid &&
115253696Speter	    !wordinclass(RealUserName, 't'))
115353696Speter	{
115453696Speter		if (LogLevel > 1)
115553696Speter			sm_syslog(LOG_ALERT, NOQID,
115653696Speter				  "user %d attempted to rebuild the alias map",
115753696Speter				  RealUid);
115864562Sgshapiro		usrerr("Permission denied");
115964562Sgshapiro		finis(FALSE, EX_USAGE);
116064562Sgshapiro	}
116138032Speter
116238032Speter	if (MeToo)
116338032Speter		BlankEnvelope.e_flags |= EF_METOO;
116438032Speter
116538032Speter	switch (OpMode)
116638032Speter	{
116738032Speter	  case MD_TEST:
116838032Speter		/* don't have persistent host status in test mode */
116938032Speter		HostStatDir = NULL;
117038032Speter		if (Verbose == 0)
117138032Speter			Verbose = 2;
117238032Speter		CurEnv->e_errormode = EM_PRINT;
117338032Speter		HoldErrs = FALSE;
117438032Speter		break;
117538032Speter
117638032Speter	  case MD_VERIFY:
117738032Speter		CurEnv->e_errormode = EM_PRINT;
117838032Speter		HoldErrs = FALSE;
117938032Speter		/* arrange to exit cleanly on hangup signal */
118038032Speter		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
118164562Sgshapiro			(void) setsignal(SIGHUP, intsig);
118238032Speter		break;
118338032Speter
118438032Speter	  case MD_FGDAEMON:
118538032Speter		run_in_foreground = TRUE;
118638032Speter		OpMode = MD_DAEMON;
118764562Sgshapiro		/* FALLTHROUGH */
118838032Speter
118938032Speter	  case MD_DAEMON:
119038032Speter		vendor_daemon_setup(CurEnv);
119138032Speter
119238032Speter		/* remove things that don't make sense in daemon mode */
119338032Speter		FullName = NULL;
119438032Speter		GrabTo = FALSE;
119538032Speter
119638032Speter		/* arrange to restart on hangup signal */
119738032Speter		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
119838032Speter			sm_syslog(LOG_WARNING, NOQID,
119964562Sgshapiro				  "daemon invoked without full pathname; kill -1 won't work");
120064562Sgshapiro		(void) setsignal(SIGHUP, sighup);
120138032Speter
120238032Speter		/* workaround: can't seem to release the signal in the parent */
120364562Sgshapiro		(void) releasesignal(SIGHUP);
120438032Speter		break;
120538032Speter
120638032Speter	  case MD_INITALIAS:
120738032Speter		Verbose = 2;
120838032Speter		CurEnv->e_errormode = EM_PRINT;
120938032Speter		HoldErrs = FALSE;
121064562Sgshapiro		/* FALLTHROUGH */
121138032Speter
121238032Speter	  default:
121338032Speter		/* arrange to exit cleanly on hangup signal */
121438032Speter		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
121564562Sgshapiro			(void) setsignal(SIGHUP, intsig);
121638032Speter		break;
121738032Speter	}
121838032Speter
121938032Speter	/* special considerations for FullName */
122038032Speter	if (FullName != NULL)
122138032Speter	{
122238032Speter		char *full = NULL;
122338032Speter
122438032Speter		/* full names can't have newlines */
122564562Sgshapiro		if (strchr(FullName, '\n') != NULL)
122638032Speter		{
122738032Speter			FullName = full = newstr(denlstring(FullName, TRUE, TRUE));
122838032Speter		}
122938032Speter		/* check for characters that may have to be quoted */
123038032Speter		if (!rfc822_string(FullName))
123138032Speter		{
123238032Speter			/*
123338032Speter			**  Quote a full name with special characters
123438032Speter			**  as a comment so crackaddr() doesn't destroy
123538032Speter			**  the name portion of the address.
123638032Speter			*/
123738032Speter			FullName = addquotes(FullName);
123838032Speter			if (full != NULL)
123938032Speter				free(full);
124038032Speter		}
124138032Speter	}
124238032Speter
124338032Speter	/* do heuristic mode adjustment */
124438032Speter	if (Verbose)
124538032Speter	{
124638032Speter		/* turn off noconnect option */
124738032Speter		setoption('c', "F", TRUE, FALSE, CurEnv);
124838032Speter
124938032Speter		/* turn on interactive delivery */
125038032Speter		setoption('d', "", TRUE, FALSE, CurEnv);
125138032Speter	}
125238032Speter
125342575Speter#ifdef VENDOR_CODE
125442575Speter	/* check for vendor mismatch */
125542575Speter	if (VendorCode != VENDOR_CODE)
125642575Speter	{
125742575Speter		message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
125842575Speter			getvendor(VENDOR_CODE), getvendor(VendorCode));
125942575Speter	}
126064562Sgshapiro#endif /* VENDOR_CODE */
126164562Sgshapiro
126238032Speter	/* check for out of date configuration level */
126338032Speter	if (ConfigLevel < MAXCONFIGLEVEL)
126438032Speter	{
126538032Speter		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
126638032Speter			Version, MAXCONFIGLEVEL, ConfigLevel);
126738032Speter	}
126838032Speter
126938032Speter	if (ConfigLevel < 3)
127038032Speter		UseErrorsTo = TRUE;
127138032Speter
127238032Speter	/* set options that were previous macros */
127338032Speter	if (SmtpGreeting == NULL)
127438032Speter	{
127538032Speter		if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
127638032Speter			SmtpGreeting = newstr(p);
127738032Speter		else
127838032Speter			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
127938032Speter	}
128038032Speter	if (UnixFromLine == NULL)
128138032Speter	{
128238032Speter		if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
128338032Speter			UnixFromLine = newstr(p);
128438032Speter		else
128538032Speter			UnixFromLine = "From \201g  \201d";
128638032Speter	}
128764562Sgshapiro	SmtpError[0] = '\0';
128838032Speter
128938032Speter	/* our name for SMTP codes */
129038032Speter	expand("\201j", jbuf, sizeof jbuf, CurEnv);
129138032Speter	MyHostName = jbuf;
129238032Speter	if (strchr(jbuf, '.') == NULL)
129338032Speter		message("WARNING: local host name (%s) is not qualified; fix $j in config file",
129438032Speter			jbuf);
129538032Speter
129638032Speter	/* make certain that this name is part of the $=w class */
129738032Speter	setclass('w', MyHostName);
129838032Speter
129938032Speter	/* the indices of built-in mailers */
130038032Speter	st = stab("local", ST_MAILER, ST_FIND);
130138032Speter	if (st != NULL)
130238032Speter		LocalMailer = st->s_mailer;
130338032Speter	else if (OpMode != MD_TEST || !warn_C_flag)
130438032Speter		syserr("No local mailer defined");
130538032Speter
130638032Speter	st = stab("prog", ST_MAILER, ST_FIND);
130738032Speter	if (st == NULL)
130838032Speter		syserr("No prog mailer defined");
130938032Speter	else
131038032Speter	{
131138032Speter		ProgMailer = st->s_mailer;
131238032Speter		clrbitn(M_MUSER, ProgMailer->m_flags);
131338032Speter	}
131438032Speter
131538032Speter	st = stab("*file*", ST_MAILER, ST_FIND);
131638032Speter	if (st == NULL)
131738032Speter		syserr("No *file* mailer defined");
131838032Speter	else
131938032Speter	{
132038032Speter		FileMailer = st->s_mailer;
132138032Speter		clrbitn(M_MUSER, FileMailer->m_flags);
132238032Speter	}
132338032Speter
132438032Speter	st = stab("*include*", ST_MAILER, ST_FIND);
132538032Speter	if (st == NULL)
132638032Speter		syserr("No *include* mailer defined");
132738032Speter	else
132838032Speter		InclMailer = st->s_mailer;
132938032Speter
133038032Speter	if (ConfigLevel < 6)
133138032Speter	{
133238032Speter		/* heuristic tweaking of local mailer for back compat */
133338032Speter		if (LocalMailer != NULL)
133438032Speter		{
133538032Speter			setbitn(M_ALIASABLE, LocalMailer->m_flags);
133638032Speter			setbitn(M_HASPWENT, LocalMailer->m_flags);
133738032Speter			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
133838032Speter			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
133938032Speter			setbitn(M_CHECKPROG, LocalMailer->m_flags);
134038032Speter			setbitn(M_CHECKFILE, LocalMailer->m_flags);
134138032Speter			setbitn(M_CHECKUDB, LocalMailer->m_flags);
134238032Speter		}
134338032Speter		if (ProgMailer != NULL)
134438032Speter			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
134538032Speter		if (FileMailer != NULL)
134638032Speter			setbitn(M_RUNASRCPT, FileMailer->m_flags);
134738032Speter	}
134838032Speter	if (ConfigLevel < 7)
134938032Speter	{
135038032Speter		if (LocalMailer != NULL)
135138032Speter			setbitn(M_VRFY250, LocalMailer->m_flags);
135238032Speter		if (ProgMailer != NULL)
135338032Speter			setbitn(M_VRFY250, ProgMailer->m_flags);
135438032Speter		if (FileMailer != NULL)
135538032Speter			setbitn(M_VRFY250, FileMailer->m_flags);
135638032Speter	}
135738032Speter
135838032Speter	/* MIME Content-Types that cannot be transfer encoded */
135938032Speter	setclass('n', "multipart/signed");
136038032Speter
136138032Speter	/* MIME message/xxx subtypes that can be treated as messages */
136238032Speter	setclass('s', "rfc822");
136338032Speter
136438032Speter	/* MIME Content-Transfer-Encodings that can be encoded */
136538032Speter	setclass('e', "7bit");
136638032Speter	setclass('e', "8bit");
136738032Speter	setclass('e', "binary");
136838032Speter
136938032Speter#ifdef USE_B_CLASS
137038032Speter	/* MIME Content-Types that should be treated as binary */
137138032Speter	setclass('b', "image");
137238032Speter	setclass('b', "audio");
137338032Speter	setclass('b', "video");
137438032Speter	setclass('b', "application/octet-stream");
137564562Sgshapiro#endif /* USE_B_CLASS */
137638032Speter
137742575Speter	/* MIME headers which have fields to check for overflow */
137842575Speter	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition");
137942575Speter	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type");
138042575Speter
138142575Speter	/* MIME headers to check for length overflow */
138242575Speter	setclass(macid("{checkMIMETextHeaders}", NULL), "content-description");
138342575Speter
138442575Speter	/* MIME headers to check for overflow and rebalance */
138542575Speter	setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition");
138642575Speter	setclass(macid("{checkMIMEHeaders}", NULL), "content-id");
138742575Speter	setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding");
138842575Speter	setclass(macid("{checkMIMEHeaders}", NULL), "content-type");
138942575Speter	setclass(macid("{checkMIMEHeaders}", NULL), "mime-version");
139042575Speter
139164562Sgshapiro	/* Macros to save in the qf file -- don't remove any */
139264562Sgshapiro	setclass(macid("{persistentMacros}", NULL), "r");
139364562Sgshapiro	setclass(macid("{persistentMacros}", NULL), "s");
139464562Sgshapiro	setclass(macid("{persistentMacros}", NULL), "_");
139564562Sgshapiro	setclass(macid("{persistentMacros}", NULL), "{if_addr}");
139664562Sgshapiro	setclass(macid("{persistentMacros}", NULL), "{daemon_flags}");
139764562Sgshapiro	setclass(macid("{persistentMacros}", NULL), "{client_flags}");
139864562Sgshapiro
139938032Speter	/* operate in queue directory */
140038032Speter	if (QueueDir == NULL)
140138032Speter	{
140238032Speter		if (OpMode != MD_TEST)
140338032Speter		{
140438032Speter			syserr("QueueDirectory (Q) option must be set");
140538032Speter			ExitStat = EX_CONFIG;
140638032Speter		}
140738032Speter	}
140838032Speter	else
140938032Speter	{
141064562Sgshapiro		/*
141164562Sgshapiro		**  If multiple queues wildcarded, use one for
141264562Sgshapiro		**  the daemon's home. Note that this preconditions
141364562Sgshapiro		**  a wildcarded QueueDir to a real pathname.
141464562Sgshapiro		*/
141564562Sgshapiro
141664562Sgshapiro		if (OpMode != MD_TEST)
141764562Sgshapiro			multiqueue_cache();
141838032Speter	}
141938032Speter
142038032Speter	/* check host status directory for validity */
142138032Speter	if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
142238032Speter	{
142338032Speter		/* cannot use this value */
142438032Speter		if (tTd(0, 2))
142564562Sgshapiro			dprintf("Cannot use HostStatusDirectory = %s: %s\n",
142638032Speter				HostStatDir, errstring(errno));
142738032Speter		HostStatDir = NULL;
142838032Speter	}
142938032Speter
143064562Sgshapiro#if QUEUE
143164562Sgshapiro	if (OpMode == MD_QUEUERUN && RealUid != 0 &&
143264562Sgshapiro	    bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
143338032Speter	{
143438032Speter		struct stat stbuf;
143538032Speter
143638032Speter		/* check to see if we own the queue directory */
143738032Speter		if (stat(".", &stbuf) < 0)
143838032Speter			syserr("main: cannot stat %s", QueueDir);
143938032Speter		if (stbuf.st_uid != RealUid)
144038032Speter		{
144138032Speter			/* nope, really a botch */
144238032Speter			usrerr("You do not have permission to process the queue");
144342575Speter			finis(FALSE, EX_NOPERM);
144438032Speter		}
144538032Speter	}
144664562Sgshapiro#endif /* QUEUE */
144738032Speter
144864562Sgshapiro#if _FFR_MILTER
144964562Sgshapiro	/* sanity checks on milter filters */
145064562Sgshapiro	if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
145164562Sgshapiro		milter_parse_list(InputFilterList, InputFilters, MAXFILTERS);
145264562Sgshapiro#endif /* _FFR_MILTER */
145364562Sgshapiro
145438032Speter	/* if we've had errors so far, exit now */
145538032Speter	if (ExitStat != EX_OK && OpMode != MD_TEST)
145642575Speter		finis(FALSE, ExitStat);
145738032Speter
145838032Speter#if XDEBUG
145938032Speter	checkfd012("before main() initmaps");
146064562Sgshapiro#endif /* XDEBUG */
146138032Speter
146238032Speter	/*
146338032Speter	**  Do operation-mode-dependent initialization.
146438032Speter	*/
146538032Speter
146638032Speter	switch (OpMode)
146738032Speter	{
146838032Speter	  case MD_PRINT:
146938032Speter		/* print the queue */
147038032Speter#if QUEUE
147138032Speter		dropenvelope(CurEnv, TRUE);
147264562Sgshapiro		(void) setsignal(SIGPIPE, quiesce);
147338032Speter		printqueue();
147442575Speter		finis(FALSE, EX_OK);
147538032Speter#else /* QUEUE */
147638032Speter		usrerr("No queue to print");
147764562Sgshapiro		finis(FALSE, EX_UNAVAILABLE);
147838032Speter#endif /* QUEUE */
147942575Speter		break;
148038032Speter
148138032Speter	  case MD_HOSTSTAT:
148264562Sgshapiro		(void) setsignal(SIGPIPE, quiesce);
148364562Sgshapiro		(void) mci_traverse_persistent(mci_print_persistent, NULL);
148442575Speter		finis(FALSE, EX_OK);
148564562Sgshapiro		break;
148638032Speter
148738032Speter	  case MD_PURGESTAT:
148864562Sgshapiro		(void) mci_traverse_persistent(mci_purge_persistent, NULL);
148942575Speter		finis(FALSE, EX_OK);
149064562Sgshapiro		break;
149138032Speter
149238032Speter	  case MD_INITALIAS:
149342575Speter		/* initialize maps */
149464562Sgshapiro		initmaps();
149542575Speter		finis(FALSE, ExitStat);
149642575Speter		break;
149738032Speter
149838032Speter	  case MD_SMTP:
149938032Speter	  case MD_DAEMON:
150038032Speter		/* reset DSN parameters */
150138032Speter		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
150264562Sgshapiro		define(macid("{dsn_notify}", NULL), NULL, CurEnv);
150338032Speter		CurEnv->e_envid = NULL;
150464562Sgshapiro		define(macid("{dsn_envid}", NULL), NULL, CurEnv);
150538032Speter		CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
150664562Sgshapiro		define(macid("{dsn_ret}", NULL), NULL, CurEnv);
150738032Speter
150842575Speter		/* don't open maps for daemon -- done below in child */
150938032Speter		break;
151038032Speter	}
151138032Speter
151238032Speter	if (tTd(0, 15))
151338032Speter	{
151438032Speter		/* print configuration table (or at least part of it) */
151538032Speter		if (tTd(0, 90))
151638032Speter			printrules();
151738032Speter		for (i = 0; i < MAXMAILERS; i++)
151838032Speter		{
151938032Speter			if (Mailer[i] != NULL)
152038032Speter				printmailer(Mailer[i]);
152138032Speter		}
152238032Speter	}
152338032Speter
152438032Speter	/*
152538032Speter	**  Switch to the main envelope.
152638032Speter	*/
152738032Speter
152838032Speter	CurEnv = newenvelope(&MainEnvelope, CurEnv);
152938032Speter	MainEnvelope.e_flags = BlankEnvelope.e_flags;
153038032Speter
153138032Speter	/*
153238032Speter	**  If test mode, read addresses from stdin and process.
153338032Speter	*/
153438032Speter
153538032Speter	if (OpMode == MD_TEST)
153638032Speter	{
153738032Speter		char buf[MAXLINE];
153838032Speter
153938032Speter		if (isatty(fileno(stdin)))
154038032Speter			Verbose = 2;
154138032Speter
154238032Speter		if (Verbose)
154338032Speter		{
154438032Speter			printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
154538032Speter			printf("Enter <ruleset> <address>\n");
154638032Speter		}
154738032Speter		if (setjmp(TopFrame) > 0)
154838032Speter			printf("\n");
154938032Speter		(void) setsignal(SIGINT, intindebug);
155038032Speter		for (;;)
155138032Speter		{
155238032Speter			if (Verbose == 2)
155338032Speter				printf("> ");
155438032Speter			(void) fflush(stdout);
155538032Speter			if (fgets(buf, sizeof buf, stdin) == NULL)
155664562Sgshapiro				testmodeline("/quit", CurEnv);
155738032Speter			p = strchr(buf, '\n');
155838032Speter			if (p != NULL)
155938032Speter				*p = '\0';
156038032Speter			if (Verbose < 2)
156138032Speter				printf("> %s\n", buf);
156238032Speter			testmodeline(buf, CurEnv);
156338032Speter		}
156438032Speter	}
156538032Speter
156664562Sgshapiro#if SMTP
156764562Sgshapiro# if STARTTLS
156838032Speter	/*
156964562Sgshapiro	**  basic TLS initialization
157064562Sgshapiro	**  ignore result for now
157164562Sgshapiro	*/
157264562Sgshapiro	SSL_library_init();
157364562Sgshapiro	SSL_load_error_strings();
157464562Sgshapiro#  if 0
157564562Sgshapiro	/* this is currently a macro for SSL_library_init */
157664562Sgshapiro	SSLeay_add_ssl_algorithms();
157764562Sgshapiro#  endif /* 0 */
157864562Sgshapiro
157964562Sgshapiro	/* initialize PRNG */
158064562Sgshapiro	tls_rand_init(RandFile, 7);
158164562Sgshapiro
158264562Sgshapiro# endif /* STARTTLS */
158364562Sgshapiro#endif /* SMTP */
158464562Sgshapiro
158564562Sgshapiro#if QUEUE
158664562Sgshapiro	/*
158738032Speter	**  If collecting stuff from the queue, go start doing that.
158838032Speter	*/
158938032Speter
159064562Sgshapiro	if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
159138032Speter	{
159264562Sgshapiro# if SMTP
159364562Sgshapiro#  if STARTTLS
159464562Sgshapiro		{
159564562Sgshapiro			/* init TLS for client, ignore result for now */
159664562Sgshapiro			(void) initclttls();
159764562Sgshapiro		}
159864562Sgshapiro#  endif /* STARTTLS */
159964562Sgshapiro# endif /* SMTP */
160038032Speter		(void) runqueue(FALSE, Verbose);
160142575Speter		finis(TRUE, ExitStat);
160238032Speter	}
160364562Sgshapiro#endif /* QUEUE */
160438032Speter
160538032Speter	/*
160638032Speter	**  If a daemon, wait for a request.
160738032Speter	**	getrequests will always return in a child.
160838032Speter	**	If we should also be processing the queue, start
160938032Speter	**		doing it in background.
161038032Speter	**	We check for any errors that might have happened
161138032Speter	**		during startup.
161238032Speter	*/
161338032Speter
161438032Speter	if (OpMode == MD_DAEMON || QueueIntvl != 0)
161538032Speter	{
161638032Speter		char dtype[200];
161738032Speter
161838032Speter		if (!run_in_foreground && !tTd(99, 100))
161938032Speter		{
162038032Speter			/* put us in background */
162138032Speter			i = fork();
162238032Speter			if (i < 0)
162338032Speter				syserr("daemon: cannot fork");
162438032Speter			if (i != 0)
162542575Speter				finis(FALSE, EX_OK);
162638032Speter
162738032Speter			/* disconnect from our controlling tty */
162838032Speter			disconnect(2, CurEnv);
162938032Speter		}
163038032Speter
163138032Speter		dtype[0] = '\0';
163238032Speter		if (OpMode == MD_DAEMON)
163364562Sgshapiro			(void) strlcat(dtype, "+SMTP", sizeof dtype);
163438032Speter		if (QueueIntvl != 0)
163538032Speter		{
163664562Sgshapiro			(void) strlcat(dtype, "+queueing@", sizeof dtype);
163764562Sgshapiro			(void) strlcat(dtype, pintvl(QueueIntvl, TRUE),
163864562Sgshapiro				       sizeof dtype);
163938032Speter		}
164038032Speter		if (tTd(0, 1))
164164562Sgshapiro			(void) strlcat(dtype, "+debugging", sizeof dtype);
164238032Speter
164338032Speter		sm_syslog(LOG_INFO, NOQID,
164464562Sgshapiro			  "starting daemon (%s): %s", Version, dtype + 1);
164538032Speter#ifdef XLA
164638032Speter		xla_create_file();
164764562Sgshapiro#endif /* XLA */
164838032Speter
164964562Sgshapiro		/* save daemon type in a macro for possible PidFile use */
165064562Sgshapiro		define(macid("{daemon_info}", NULL),
165164562Sgshapiro		       newstr(dtype + 1), &BlankEnvelope);
165264562Sgshapiro
165364562Sgshapiro		/* save queue interval in a macro for possible PidFile use */
165464562Sgshapiro		define(macid("{queue_interval}", NULL),
165564562Sgshapiro		       newstr(pintvl(QueueIntvl, TRUE)), CurEnv);
165664562Sgshapiro
165764562Sgshapiro#if QUEUE
165864562Sgshapiro		if (QueueIntvl != 0)
165938032Speter		{
166038032Speter			(void) runqueue(TRUE, FALSE);
166138032Speter			if (OpMode != MD_DAEMON)
166238032Speter			{
166364562Sgshapiro				/* write the pid to file */
166464562Sgshapiro				log_sendmail_pid(CurEnv);
166538032Speter				for (;;)
166638032Speter				{
166764562Sgshapiro					(void) pause();
166838032Speter					if (DoQueueRun)
166938032Speter						(void) runqueue(TRUE, FALSE);
167038032Speter				}
167138032Speter			}
167238032Speter		}
167364562Sgshapiro#endif /* QUEUE */
167438032Speter		dropenvelope(CurEnv, TRUE);
167538032Speter
167638032Speter#if DAEMON
167764562Sgshapiro# if STARTTLS
167864562Sgshapiro		/* init TLS for server, ignore result for now */
167964562Sgshapiro		(void) initsrvtls();
168064562Sgshapiro# endif /* STARTTLS */
168164562Sgshapiro		p_flags = getrequests(CurEnv);
168238032Speter
168338032Speter		/* drop privileges */
168438032Speter		(void) drop_privileges(FALSE);
168538032Speter
168638032Speter		/* at this point we are in a child: reset state */
168738032Speter		(void) newenvelope(CurEnv, CurEnv);
168838032Speter
168938032Speter		/*
169038032Speter		**  Get authentication data
169138032Speter		*/
169238032Speter
169364562Sgshapiro		authinfo = getauthinfo(fileno(InChannel), &forged);
169464562Sgshapiro		define('_', authinfo, &BlankEnvelope);
169538032Speter#endif /* DAEMON */
169638032Speter	}
169738032Speter
169864562Sgshapiro	if (LogLevel > 9)
169964562Sgshapiro	{
170064562Sgshapiro		/* log connection information */
170164562Sgshapiro		sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo);
170264562Sgshapiro	}
170364562Sgshapiro
170464562Sgshapiro#if SMTP
170538032Speter	/*
170638032Speter	**  If running SMTP protocol, start collecting and executing
170738032Speter	**  commands.  This will never return.
170838032Speter	*/
170938032Speter
171038032Speter	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
171138032Speter	{
171238032Speter		char pbuf[20];
171338032Speter
171438032Speter		/*
171538032Speter		**  Save some macros for check_* rulesets.
171638032Speter		*/
171738032Speter
171838032Speter		if (forged)
171938032Speter		{
172038032Speter			char ipbuf[103];
172138032Speter
172264562Sgshapiro			(void) snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
172364562Sgshapiro					anynet_ntoa(&RealHostAddr));
172438032Speter			define(macid("{client_name}", NULL),
172538032Speter			       newstr(ipbuf), &BlankEnvelope);
172664562Sgshapiro			define(macid("{client_resolve}", NULL),
172764562Sgshapiro			       "FORGED", &BlankEnvelope);
172838032Speter		}
172938032Speter		else
173064562Sgshapiro			define(macid("{client_name}", NULL), RealHostName,
173164562Sgshapiro			       &BlankEnvelope);
173238032Speter		define(macid("{client_addr}", NULL),
173338032Speter		       newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
173464562Sgshapiro		(void)sm_getla(&BlankEnvelope);
173538032Speter
173664562Sgshapiro		switch(RealHostAddr.sa.sa_family)
173764562Sgshapiro		{
173864562Sgshapiro# if NETINET
173964562Sgshapiro		  case AF_INET:
174064562Sgshapiro			(void) snprintf(pbuf, sizeof pbuf, "%d",
174164562Sgshapiro					RealHostAddr.sin.sin_port);
174264562Sgshapiro			break;
174364562Sgshapiro# endif /* NETINET */
174464562Sgshapiro# if NETINET6
174564562Sgshapiro		  case AF_INET6:
174664562Sgshapiro			(void) snprintf(pbuf, sizeof pbuf, "%d",
174764562Sgshapiro					RealHostAddr.sin6.sin6_port);
174864562Sgshapiro			break;
174964562Sgshapiro# endif /* NETINET6 */
175064562Sgshapiro		  default:
175164562Sgshapiro			(void) snprintf(pbuf, sizeof pbuf, "0");
175264562Sgshapiro			break;
175364562Sgshapiro		}
175464562Sgshapiro		define(macid("{client_port}", NULL),
175564562Sgshapiro		       newstr(pbuf), &BlankEnvelope);
175642575Speter
175764562Sgshapiro#if SASL
175864562Sgshapiro		/* give a syserr or just disable AUTH ? */
175964562Sgshapiro		if (sasl_server_init(srvcallbacks, "Sendmail") != SASL_OK)
176064562Sgshapiro			syserr("!sasl_server_init failed!");
176164562Sgshapiro#endif /* SASL */
176264562Sgshapiro
176338032Speter		if (OpMode == MD_DAEMON)
176438032Speter		{
176538032Speter			/* validate the connection */
176638032Speter			HoldErrs = TRUE;
176738032Speter			nullserver = validate_connection(&RealHostAddr,
176838032Speter							 RealHostName, CurEnv);
176938032Speter			HoldErrs = FALSE;
177038032Speter		}
177164562Sgshapiro		else if (p_flags == NULL)
177264562Sgshapiro		{
177364562Sgshapiro			p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
177464562Sgshapiro			clrbitmap(p_flags);
177564562Sgshapiro		}
177664562Sgshapiro# if STARTTLS
177764562Sgshapiro		if (OpMode == MD_SMTP)
177864562Sgshapiro			(void) initsrvtls();
177964562Sgshapiro# endif /* STARTTLS */
178064562Sgshapiro		smtp(nullserver, *p_flags, CurEnv);
178138032Speter	}
178264562Sgshapiro#endif /* SMTP */
178338032Speter
178438032Speter	clearenvelope(CurEnv, FALSE);
178538032Speter	if (OpMode == MD_VERIFY)
178638032Speter	{
178764562Sgshapiro		set_delivery_mode(SM_VERIFY, CurEnv);
178838032Speter		PostMasterCopy = NULL;
178938032Speter	}
179038032Speter	else
179138032Speter	{
179238032Speter		/* interactive -- all errors are global */
179338032Speter		CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
179438032Speter	}
179538032Speter
179638032Speter	/*
179738032Speter	**  Do basic system initialization and set the sender
179838032Speter	*/
179938032Speter
180038032Speter	initsys(CurEnv);
180164562Sgshapiro	define(macid("{ntries}", NULL), "0", CurEnv);
180264562Sgshapiro	setsender(from, CurEnv, NULL, '\0', FALSE);
180364562Sgshapiro	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
180464562Sgshapiro	    (!bitnset(M_LOCALMAILER, CurEnv->e_from.q_mailer->m_flags) ||
180564562Sgshapiro	     strcmp(CurEnv->e_from.q_user, RealUserName) != 0))
180664562Sgshapiro	{
180738032Speter		auth_warning(CurEnv, "%s set sender to %s using -%c",
180838032Speter			RealUserName, from, warn_f_flag);
180964562Sgshapiro#if SASL
181064562Sgshapiro		auth = FALSE;
181164562Sgshapiro#endif /* SASL */
181264562Sgshapiro	}
181364562Sgshapiro	if (auth)
181464562Sgshapiro	{
181564562Sgshapiro		char *fv;
181664562Sgshapiro
181764562Sgshapiro		/* set the initial sender for AUTH= to $f@$j */
181864562Sgshapiro		fv = macvalue('f', CurEnv);
181964562Sgshapiro		if (fv == NULL || *fv == '\0')
182064562Sgshapiro			CurEnv->e_auth_param = NULL;
182164562Sgshapiro		else
182264562Sgshapiro		{
182364562Sgshapiro			if (strchr(fv, '@') == NULL)
182464562Sgshapiro			{
182564562Sgshapiro				i = strlen(fv) + strlen(macvalue('j', CurEnv))
182664562Sgshapiro				    + 2;
182764562Sgshapiro				p = xalloc(i);
182864562Sgshapiro				(void) snprintf(p, i, "%s@%s", fv,
182964562Sgshapiro						macvalue('j', CurEnv));
183064562Sgshapiro			}
183164562Sgshapiro			else
183264562Sgshapiro				p = newstr(fv);
183364562Sgshapiro			CurEnv->e_auth_param = newstr(xtextify(p, NULL));
183464562Sgshapiro		}
183564562Sgshapiro	}
183638032Speter	if (macvalue('s', CurEnv) == NULL)
183738032Speter		define('s', RealHostName, CurEnv);
183838032Speter
183938032Speter	if (*av == NULL && !GrabTo)
184038032Speter	{
184164562Sgshapiro		CurEnv->e_to = NULL;
184238032Speter		CurEnv->e_flags |= EF_GLOBALERRS;
184364562Sgshapiro		HoldErrs = FALSE;
184438032Speter		usrerr("Recipient names must be specified");
184538032Speter
184638032Speter		/* collect body for UUCP return */
184738032Speter		if (OpMode != MD_VERIFY)
184838032Speter			collect(InChannel, FALSE, NULL, CurEnv);
184964562Sgshapiro		finis(TRUE, EX_USAGE);
185038032Speter	}
185138032Speter
185238032Speter	/*
185338032Speter	**  Scan argv and deliver the message to everyone.
185438032Speter	*/
185538032Speter
185638032Speter	sendtoargv(av, CurEnv);
185738032Speter
185838032Speter	/* if we have had errors sofar, arrange a meaningful exit stat */
185938032Speter	if (Errors > 0 && ExitStat == EX_OK)
186038032Speter		ExitStat = EX_USAGE;
186138032Speter
186238032Speter#if _FFR_FIX_DASHT
186338032Speter	/*
186438032Speter	**  If using -t, force not sending to argv recipients, even
186538032Speter	**  if they are mentioned in the headers.
186638032Speter	*/
186738032Speter
186838032Speter	if (GrabTo)
186938032Speter	{
187038032Speter		ADDRESS *q;
187164562Sgshapiro
187238032Speter		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
187364562Sgshapiro			q->q_state = QS_REMOVED;
187438032Speter	}
187564562Sgshapiro#endif /* _FFR_FIX_DASHT */
187638032Speter
187738032Speter	/*
187838032Speter	**  Read the input mail.
187938032Speter	*/
188038032Speter
188138032Speter	CurEnv->e_to = NULL;
188238032Speter	if (OpMode != MD_VERIFY || GrabTo)
188338032Speter	{
188464562Sgshapiro		int savederrors = Errors;
188538032Speter		long savedflags = CurEnv->e_flags & EF_FATALERRS;
188638032Speter
188738032Speter		CurEnv->e_flags |= EF_GLOBALERRS;
188838032Speter		CurEnv->e_flags &= ~EF_FATALERRS;
188964562Sgshapiro		Errors = 0;
189064562Sgshapiro		buffer_errors();
189138032Speter		collect(InChannel, FALSE, NULL, CurEnv);
189238032Speter
189364562Sgshapiro		/* header checks failed */
189464562Sgshapiro		if (Errors > 0)
189564562Sgshapiro		{
189664562Sgshapiro			/* Log who the mail would have gone to */
189764562Sgshapiro			if (LogLevel > 8 && CurEnv->e_message != NULL &&
189864562Sgshapiro			    !GrabTo)
189964562Sgshapiro			{
190064562Sgshapiro				ADDRESS *a;
190164562Sgshapiro
190264562Sgshapiro				for (a = CurEnv->e_sendqueue;
190364562Sgshapiro				     a != NULL;
190464562Sgshapiro				     a = a->q_next)
190564562Sgshapiro				{
190664562Sgshapiro					if (!QS_IS_UNDELIVERED(a->q_state))
190764562Sgshapiro						continue;
190864562Sgshapiro
190964562Sgshapiro					CurEnv->e_to = a->q_paddr;
191064562Sgshapiro					logdelivery(NULL, NULL, NULL,
191164562Sgshapiro						    CurEnv->e_message,
191264562Sgshapiro						    NULL, (time_t) 0, CurEnv);
191364562Sgshapiro				}
191464562Sgshapiro				CurEnv->e_to = NULL;
191564562Sgshapiro			}
191664562Sgshapiro			flush_errors(TRUE);
191764562Sgshapiro			finis(TRUE, ExitStat);
191864562Sgshapiro			/* NOTREACHED */
191964562Sgshapiro			return -1;
192064562Sgshapiro		}
192164562Sgshapiro
192238032Speter		/* bail out if message too large */
192338032Speter		if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
192438032Speter		{
192564562Sgshapiro			finis(TRUE, ExitStat != EX_OK ? ExitStat : EX_DATAERR);
192664562Sgshapiro			/* NOTREACHED */
192738032Speter			return -1;
192838032Speter		}
192964562Sgshapiro		Errors = savederrors;
193038032Speter		CurEnv->e_flags |= savedflags;
193138032Speter	}
193238032Speter	errno = 0;
193338032Speter
193438032Speter	if (tTd(1, 1))
193564562Sgshapiro		dprintf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
193638032Speter
193738032Speter	/*
193838032Speter	**  Actually send everything.
193938032Speter	**	If verifying, just ack.
194038032Speter	*/
194138032Speter
194264562Sgshapiro	CurEnv->e_from.q_state = QS_SENDER;
194338032Speter	if (tTd(1, 5))
194438032Speter	{
194564562Sgshapiro		dprintf("main: QS_SENDER ");
194638032Speter		printaddr(&CurEnv->e_from, FALSE);
194738032Speter	}
194838032Speter	CurEnv->e_to = NULL;
194964562Sgshapiro	CurrentLA = sm_getla(CurEnv);
195038032Speter	GrabTo = FALSE;
195164562Sgshapiro#if NAMED_BIND
195264562Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
195364562Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
195464562Sgshapiro#endif /* NAMED_BIND */
195538032Speter	sendall(CurEnv, SM_DEFAULT);
195638032Speter
195738032Speter	/*
195838032Speter	**  All done.
195938032Speter	**	Don't send return error message if in VERIFY mode.
196038032Speter	*/
196138032Speter
196242575Speter	finis(TRUE, ExitStat);
196364562Sgshapiro	/* NOTREACHED */
196464562Sgshapiro	return ExitStat;
196538032Speter}
196638032Speter
196742575Speter/* ARGSUSED */
196842575SpeterSIGFUNC_DECL
196942575Speterquiesce(sig)
197042575Speter	int sig;
197142575Speter{
197264562Sgshapiro	clear_events();
197342575Speter	finis(FALSE, EX_OK);
197442575Speter}
197538032Speter
197638032Speter/* ARGSUSED */
197738032SpeterSIGFUNC_DECL
197838032Speterintindebug(sig)
197938032Speter	int sig;
198038032Speter{
198138032Speter	longjmp(TopFrame, 1);
198238032Speter	return SIGFUNC_RETURN;
198338032Speter}
198438032Speter/*
198538032Speter**  FINIS -- Clean up and exit.
198638032Speter**
198738032Speter**	Parameters:
198842575Speter**		drop -- whether or not to drop CurEnv envelope
198942575Speter**		exitstat -- exit status to use for exit() call
199038032Speter**
199138032Speter**	Returns:
199238032Speter**		never
199338032Speter**
199438032Speter**	Side Effects:
199538032Speter**		exits sendmail
199638032Speter*/
199738032Speter
199838032Spetervoid
199942575Speterfinis(drop, exitstat)
200042575Speter	bool drop;
200142575Speter	volatile int exitstat;
200238032Speter{
200342575Speter
200438032Speter	if (tTd(2, 1))
200538032Speter	{
200664562Sgshapiro		dprintf("\n====finis: stat %d e_id=%s e_flags=",
200742575Speter			exitstat,
200838032Speter			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
200938032Speter		printenvflags(CurEnv);
201038032Speter	}
201138032Speter	if (tTd(2, 9))
201238032Speter		printopenfds(FALSE);
201338032Speter
201438032Speter	/* if we fail in finis(), just exit */
201538032Speter	if (setjmp(TopFrame) != 0)
201638032Speter	{
201738032Speter		/* failed -- just give it up */
201838032Speter		goto forceexit;
201938032Speter	}
202038032Speter
202138032Speter	/* clean up temp files */
202238032Speter	CurEnv->e_to = NULL;
202364562Sgshapiro	if (drop)
202464562Sgshapiro	{
202564562Sgshapiro		if (CurEnv->e_id != NULL)
202664562Sgshapiro			dropenvelope(CurEnv, TRUE);
202764562Sgshapiro		else
202864562Sgshapiro			poststats(StatFile);
202964562Sgshapiro	}
203038032Speter
203138032Speter	/* flush any cached connections */
203238032Speter	mci_flush(TRUE, NULL);
203338032Speter
203442575Speter	/* close maps belonging to this pid */
203542575Speter	closemaps();
203642575Speter
203764562Sgshapiro#if USERDB
203842575Speter	/* close UserDatabase */
203942575Speter	_udbx_close();
204064562Sgshapiro#endif /* USERDB */
204142575Speter
204264562Sgshapiro#ifdef XLA
204338032Speter	/* clean up extended load average stuff */
204438032Speter	xla_all_end();
204564562Sgshapiro#endif /* XLA */
204638032Speter
204738032Speter	/* and exit */
204838032Speter  forceexit:
204938032Speter	if (LogLevel > 78)
205038032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id,
205164562Sgshapiro			  "finis, pid=%d",
205264562Sgshapiro			  getpid());
205342575Speter	if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
205442575Speter		exitstat = EX_OK;
205538032Speter
205664562Sgshapiro	sync_queue_time();
205764562Sgshapiro
205838032Speter	/* reset uid for process accounting */
205938032Speter	endpwent();
206064562Sgshapiro	(void) setuid(RealUid);
206142575Speter	exit(exitstat);
206238032Speter}
206338032Speter/*
206438032Speter**  INTSIG -- clean up on interrupt
206538032Speter**
206664562Sgshapiro**	This just arranges to exit.  It pessimizes in that it
206738032Speter**	may resend a message.
206838032Speter**
206938032Speter**	Parameters:
207038032Speter**		none.
207138032Speter**
207238032Speter**	Returns:
207338032Speter**		none.
207438032Speter**
207538032Speter**	Side Effects:
207638032Speter**		Unlocks the current job.
207738032Speter*/
207838032Speter
207938032Speter/* ARGSUSED */
208038032SpeterSIGFUNC_DECL
208138032Speterintsig(sig)
208238032Speter	int sig;
208338032Speter{
208464562Sgshapiro	bool drop = FALSE;
208564562Sgshapiro
208664562Sgshapiro	clear_events();
208764562Sgshapiro	if (sig != 0 && LogLevel > 79)
208838032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
208938032Speter	FileName = NULL;
209042575Speter	closecontrolsocket(TRUE);
209138032Speter#ifdef XLA
209238032Speter	xla_all_end();
209364562Sgshapiro#endif /* XLA */
209464562Sgshapiro
209564562Sgshapiro	/* Clean-up on aborted stdin message submission */
209664562Sgshapiro	if (CurEnv->e_id != NULL &&
209764562Sgshapiro	    (OpMode == MD_SMTP ||
209864562Sgshapiro	     OpMode == MD_DELIVER ||
209964562Sgshapiro	     OpMode == MD_ARPAFTP))
210064562Sgshapiro	{
210164562Sgshapiro		register ADDRESS *q;
210264562Sgshapiro
210364562Sgshapiro		/* don't return an error indication */
210464562Sgshapiro		CurEnv->e_to = NULL;
210564562Sgshapiro		CurEnv->e_flags &= ~EF_FATALERRS;
210664562Sgshapiro		CurEnv->e_flags |= EF_CLRQUEUE;
210764562Sgshapiro
210864562Sgshapiro		/*
210964562Sgshapiro		**  Spin through the addresses and
211064562Sgshapiro		**  mark them dead to prevent bounces
211164562Sgshapiro		*/
211264562Sgshapiro
211364562Sgshapiro		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
211464562Sgshapiro			q->q_state = QS_DONTSEND;
211564562Sgshapiro
211664562Sgshapiro		/* and don't try to deliver the partial message either */
211764562Sgshapiro		if (InChild)
211864562Sgshapiro			ExitStat = EX_QUIT;
211964562Sgshapiro
212064562Sgshapiro		drop = TRUE;
212164562Sgshapiro	}
212264562Sgshapiro	else
212364562Sgshapiro		unlockqueue(CurEnv);
212464562Sgshapiro
212564562Sgshapiro	finis(drop, EX_OK);
212638032Speter}
212738032Speter/*
212838032Speter**  INITMACROS -- initialize the macro system
212938032Speter**
213038032Speter**	This just involves defining some macros that are actually
213138032Speter**	used internally as metasymbols to be themselves.
213238032Speter**
213338032Speter**	Parameters:
213438032Speter**		none.
213538032Speter**
213638032Speter**	Returns:
213738032Speter**		none.
213838032Speter**
213938032Speter**	Side Effects:
214038032Speter**		initializes several macros to be themselves.
214138032Speter*/
214238032Speter
214338032Speterstruct metamac	MetaMacros[] =
214438032Speter{
214538032Speter	/* LHS pattern matching characters */
214638032Speter	{ '*', MATCHZANY },	{ '+', MATCHANY },	{ '-', MATCHONE },
214738032Speter	{ '=', MATCHCLASS },	{ '~', MATCHNCLASS },
214838032Speter
214938032Speter	/* these are RHS metasymbols */
215038032Speter	{ '#', CANONNET },	{ '@', CANONHOST },	{ ':', CANONUSER },
215138032Speter	{ '>', CALLSUBR },
215238032Speter
215338032Speter	/* the conditional operations */
215438032Speter	{ '?', CONDIF },	{ '|', CONDELSE },	{ '.', CONDFI },
215538032Speter
215638032Speter	/* the hostname lookup characters */
215738032Speter	{ '[', HOSTBEGIN },	{ ']', HOSTEND },
215838032Speter	{ '(', LOOKUPBEGIN },	{ ')', LOOKUPEND },
215938032Speter
216038032Speter	/* miscellaneous control characters */
216138032Speter	{ '&', MACRODEXPAND },
216238032Speter
216338032Speter	{ '\0' }
216438032Speter};
216538032Speter
216638032Speter#define MACBINDING(name, mid) \
216738032Speter		stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
216838032Speter		MacroName[mid] = name;
216938032Speter
217038032Spetervoid
217138032Speterinitmacros(e)
217238032Speter	register ENVELOPE *e;
217338032Speter{
217438032Speter	register struct metamac *m;
217538032Speter	register int c;
217638032Speter	char buf[5];
217738032Speter	extern char *MacroName[256];
217838032Speter
217938032Speter	for (m = MetaMacros; m->metaname != '\0'; m++)
218038032Speter	{
218138032Speter		buf[0] = m->metaval;
218238032Speter		buf[1] = '\0';
218338032Speter		define(m->metaname, newstr(buf), e);
218438032Speter	}
218538032Speter	buf[0] = MATCHREPL;
218638032Speter	buf[2] = '\0';
218738032Speter	for (c = '0'; c <= '9'; c++)
218838032Speter	{
218938032Speter		buf[1] = c;
219038032Speter		define(c, newstr(buf), e);
219138032Speter	}
219238032Speter
219338032Speter	/* set defaults for some macros sendmail will use later */
219438032Speter	define('n', "MAILER-DAEMON", e);
219538032Speter
219638032Speter	/* set up external names for some internal macros */
219738032Speter	MACBINDING("opMode", MID_OPMODE);
219838032Speter	/*XXX should probably add equivalents for all short macros here XXX*/
219938032Speter}
220038032Speter/*
220138032Speter**  DISCONNECT -- remove our connection with any foreground process
220238032Speter**
220338032Speter**	Parameters:
220438032Speter**		droplev -- how "deeply" we should drop the line.
220538032Speter**			0 -- ignore signals, mail back errors, make sure
220638032Speter**			     output goes to stdout.
220764562Sgshapiro**			1 -- also, make stdout go to /dev/null.
220838032Speter**			2 -- also, disconnect from controlling terminal
220938032Speter**			     (only for daemon mode).
221038032Speter**		e -- the current envelope.
221138032Speter**
221238032Speter**	Returns:
221338032Speter**		none
221438032Speter**
221538032Speter**	Side Effects:
221638032Speter**		Trys to insure that we are immune to vagaries of
221738032Speter**		the controlling tty.
221838032Speter*/
221938032Speter
222038032Spetervoid
222138032Speterdisconnect(droplev, e)
222238032Speter	int droplev;
222338032Speter	register ENVELOPE *e;
222438032Speter{
222538032Speter	int fd;
222638032Speter
222738032Speter	if (tTd(52, 1))
222864562Sgshapiro		dprintf("disconnect: In %d Out %d, e=%lx\n",
222938032Speter			fileno(InChannel), fileno(OutChannel), (u_long) e);
223038032Speter	if (tTd(52, 100))
223138032Speter	{
223264562Sgshapiro		dprintf("don't\n");
223338032Speter		return;
223438032Speter	}
223538032Speter	if (LogLevel > 93)
223638032Speter		sm_syslog(LOG_DEBUG, e->e_id,
223764562Sgshapiro			  "disconnect level %d",
223864562Sgshapiro			  droplev);
223938032Speter
224038032Speter	/* be sure we don't get nasty signals */
224138032Speter	(void) setsignal(SIGINT, SIG_IGN);
224238032Speter	(void) setsignal(SIGQUIT, SIG_IGN);
224338032Speter
224438032Speter	/* we can't communicate with our caller, so.... */
224538032Speter	HoldErrs = TRUE;
224638032Speter	CurEnv->e_errormode = EM_MAIL;
224738032Speter	Verbose = 0;
224838032Speter	DisConnected = TRUE;
224938032Speter
225038032Speter	/* all input from /dev/null */
225138032Speter	if (InChannel != stdin)
225238032Speter	{
225338032Speter		(void) fclose(InChannel);
225438032Speter		InChannel = stdin;
225538032Speter	}
225638032Speter	if (freopen("/dev/null", "r", stdin) == NULL)
225738032Speter		sm_syslog(LOG_ERR, e->e_id,
225838032Speter			  "disconnect: freopen(\"/dev/null\") failed: %s",
225938032Speter			  errstring(errno));
226038032Speter
226138032Speter	/* output to the transcript */
226238032Speter	if (OutChannel != stdout)
226338032Speter	{
226438032Speter		(void) fclose(OutChannel);
226538032Speter		OutChannel = stdout;
226638032Speter	}
226738032Speter	if (droplev > 0)
226838032Speter	{
226964562Sgshapiro		fd = open("/dev/null", O_WRONLY, 0666);
227064562Sgshapiro		if (fd == -1)
227164562Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
227264562Sgshapiro				  "disconnect: open(\"/dev/null\") failed: %s",
227364562Sgshapiro				  errstring(errno));
227438032Speter		(void) fflush(stdout);
227564562Sgshapiro		(void) dup2(fd, STDOUT_FILENO);
227664562Sgshapiro		(void) dup2(fd, STDERR_FILENO);
227764562Sgshapiro		(void) close(fd);
227838032Speter	}
227938032Speter
228038032Speter	/* drop our controlling TTY completely if possible */
228138032Speter	if (droplev > 1)
228238032Speter	{
228338032Speter		(void) setsid();
228438032Speter		errno = 0;
228538032Speter	}
228638032Speter
228738032Speter#if XDEBUG
228838032Speter	checkfd012("disconnect");
228964562Sgshapiro#endif /* XDEBUG */
229038032Speter
229138032Speter	if (LogLevel > 71)
229238032Speter		sm_syslog(LOG_DEBUG, e->e_id,
229364562Sgshapiro			  "in background, pid=%d",
229464562Sgshapiro			  getpid());
229538032Speter
229638032Speter	errno = 0;
229738032Speter}
229838032Speter
229938032Speterstatic void
230038032Speterobsolete(argv)
230138032Speter	char *argv[];
230238032Speter{
230338032Speter	register char *ap;
230438032Speter	register char *op;
230538032Speter
230638032Speter	while ((ap = *++argv) != NULL)
230738032Speter	{
230838032Speter		/* Return if "--" or not an option of any form. */
230938032Speter		if (ap[0] != '-' || ap[1] == '-')
231038032Speter			return;
231138032Speter
231238032Speter		/* skip over options that do have a value */
231338032Speter		op = strchr(OPTIONS, ap[1]);
231438032Speter		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
231538032Speter		    ap[1] != 'd' &&
231638032Speter#if defined(sony_news)
231738032Speter		    ap[1] != 'E' && ap[1] != 'J' &&
231864562Sgshapiro#endif /* defined(sony_news) */
231938032Speter		    argv[1] != NULL && argv[1][0] != '-')
232038032Speter		{
232138032Speter			argv++;
232238032Speter			continue;
232338032Speter		}
232438032Speter
232538032Speter		/* If -C doesn't have an argument, use sendmail.cf. */
232638032Speter#define	__DEFPATH	"sendmail.cf"
232738032Speter		if (ap[1] == 'C' && ap[2] == '\0')
232838032Speter		{
232938032Speter			*argv = xalloc(sizeof(__DEFPATH) + 2);
233064562Sgshapiro			(void) snprintf(argv[0], sizeof(__DEFPATH) + 2, "-C%s",
233164562Sgshapiro					__DEFPATH);
233238032Speter		}
233338032Speter
233438032Speter		/* If -q doesn't have an argument, run it once. */
233538032Speter		if (ap[1] == 'q' && ap[2] == '\0')
233638032Speter			*argv = "-q0";
233738032Speter
233838032Speter		/* if -d doesn't have an argument, use 0-99.1 */
233938032Speter		if (ap[1] == 'd' && ap[2] == '\0')
234038032Speter			*argv = "-d0-99.1";
234138032Speter
234264562Sgshapiro#if defined(sony_news)
234338032Speter		/* if -E doesn't have an argument, use -EC */
234438032Speter		if (ap[1] == 'E' && ap[2] == '\0')
234538032Speter			*argv = "-EC";
234638032Speter
234738032Speter		/* if -J doesn't have an argument, use -JJ */
234838032Speter		if (ap[1] == 'J' && ap[2] == '\0')
234938032Speter			*argv = "-JJ";
235064562Sgshapiro#endif /* defined(sony_news) */
235138032Speter	}
235238032Speter}
235338032Speter/*
235438032Speter**  AUTH_WARNING -- specify authorization warning
235538032Speter**
235638032Speter**	Parameters:
235738032Speter**		e -- the current envelope.
235838032Speter**		msg -- the text of the message.
235938032Speter**		args -- arguments to the message.
236038032Speter**
236138032Speter**	Returns:
236238032Speter**		none.
236338032Speter*/
236438032Speter
236538032Spetervoid
236638032Speter#ifdef __STDC__
236738032Speterauth_warning(register ENVELOPE *e, const char *msg, ...)
236864562Sgshapiro#else /* __STDC__ */
236938032Speterauth_warning(e, msg, va_alist)
237038032Speter	register ENVELOPE *e;
237138032Speter	const char *msg;
237238032Speter	va_dcl
237364562Sgshapiro#endif /* __STDC__ */
237438032Speter{
237538032Speter	char buf[MAXLINE];
237638032Speter	VA_LOCAL_DECL
237738032Speter
237838032Speter	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
237938032Speter	{
238038032Speter		register char *p;
238138032Speter		static char hostbuf[48];
238238032Speter
238338032Speter		if (hostbuf[0] == '\0')
238438032Speter			(void) myhostname(hostbuf, sizeof hostbuf);
238538032Speter
238638032Speter		(void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
238738032Speter		p = &buf[strlen(buf)];
238838032Speter		VA_START(msg);
238938032Speter		vsnprintf(p, SPACELEFT(buf, p), msg, ap);
239038032Speter		VA_END;
239164562Sgshapiro		addheader("X-Authentication-Warning", buf, 0, &e->e_header);
239238032Speter		if (LogLevel > 3)
239338032Speter			sm_syslog(LOG_INFO, e->e_id,
239464562Sgshapiro				  "Authentication-Warning: %.400s",
239564562Sgshapiro				  buf);
239638032Speter	}
239738032Speter}
239838032Speter/*
239938032Speter**  GETEXTENV -- get from external environment
240038032Speter**
240138032Speter**	Parameters:
240238032Speter**		envar -- the name of the variable to retrieve
240338032Speter**
240438032Speter**	Returns:
240538032Speter**		The value, if any.
240638032Speter*/
240738032Speter
240838032Speterchar *
240938032Spetergetextenv(envar)
241038032Speter	const char *envar;
241138032Speter{
241238032Speter	char **envp;
241338032Speter	int l;
241438032Speter
241538032Speter	l = strlen(envar);
241638032Speter	for (envp = ExternalEnviron; *envp != NULL; envp++)
241738032Speter	{
241838032Speter		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
241938032Speter			return &(*envp)[l + 1];
242038032Speter	}
242138032Speter	return NULL;
242238032Speter}
242338032Speter/*
242438032Speter**  SETUSERENV -- set an environment in the propogated environment
242538032Speter**
242638032Speter**	Parameters:
242738032Speter**		envar -- the name of the environment variable.
242838032Speter**		value -- the value to which it should be set.  If
242938032Speter**			null, this is extracted from the incoming
243038032Speter**			environment.  If that is not set, the call
243138032Speter**			to setuserenv is ignored.
243238032Speter**
243338032Speter**	Returns:
243438032Speter**		none.
243538032Speter*/
243638032Speter
243738032Spetervoid
243838032Spetersetuserenv(envar, value)
243938032Speter	const char *envar;
244038032Speter	const char *value;
244138032Speter{
244264562Sgshapiro	int i, l;
244338032Speter	char **evp = UserEnviron;
244438032Speter	char *p;
244538032Speter
244638032Speter	if (value == NULL)
244738032Speter	{
244838032Speter		value = getextenv(envar);
244938032Speter		if (value == NULL)
245038032Speter			return;
245138032Speter	}
245238032Speter
245364562Sgshapiro	i = strlen(envar) + 1;
245464562Sgshapiro	l = strlen(value) + i + 1;
245564562Sgshapiro	p = (char *) xalloc(l);
245664562Sgshapiro	(void) snprintf(p, l, "%s=%s", envar, value);
245738032Speter
245838032Speter	while (*evp != NULL && strncmp(*evp, p, i) != 0)
245938032Speter		evp++;
246038032Speter	if (*evp != NULL)
246138032Speter	{
246238032Speter		*evp++ = p;
246338032Speter	}
246438032Speter	else if (evp < &UserEnviron[MAXUSERENVIRON])
246538032Speter	{
246638032Speter		*evp++ = p;
246738032Speter		*evp = NULL;
246838032Speter	}
246938032Speter
247038032Speter	/* make sure it is in our environment as well */
247138032Speter	if (putenv(p) < 0)
247238032Speter		syserr("setuserenv: putenv(%s) failed", p);
247338032Speter}
247438032Speter/*
247538032Speter**  DUMPSTATE -- dump state
247638032Speter**
247738032Speter**	For debugging.
247838032Speter*/
247938032Speter
248038032Spetervoid
248138032Speterdumpstate(when)
248238032Speter	char *when;
248338032Speter{
248438032Speter	register char *j = macvalue('j', CurEnv);
248538032Speter	int rs;
248664562Sgshapiro	extern int NextMacroId;
248738032Speter
248838032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id,
248964562Sgshapiro		  "--- dumping state on %s: $j = %s ---",
249064562Sgshapiro		  when,
249164562Sgshapiro		  j == NULL ? "<NULL>" : j);
249238032Speter	if (j != NULL)
249338032Speter	{
249438032Speter		if (!wordinclass(j, 'w'))
249538032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id,
249664562Sgshapiro				  "*** $j not in $=w ***");
249738032Speter	}
249838032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
249964562Sgshapiro	sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)\n",
250064562Sgshapiro		  NextMacroId, MAXMACROID);
250138032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
250238032Speter	printopenfds(TRUE);
250338032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
250438032Speter	mci_dump_all(TRUE);
250538032Speter	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
250638032Speter	if (rs > 0)
250738032Speter	{
250864562Sgshapiro		int status;
250938032Speter		register char **pvp;
251038032Speter		char *pv[MAXATOM + 1];
251138032Speter
251238032Speter		pv[0] = NULL;
251364562Sgshapiro		status = rewrite(pv, rs, 0, CurEnv);
251438032Speter		sm_syslog(LOG_DEBUG, CurEnv->e_id,
251564562Sgshapiro			  "--- ruleset debug_dumpstate returns stat %d, pv: ---",
251664562Sgshapiro			  status);
251738032Speter		for (pvp = pv; *pvp != NULL; pvp++)
251838032Speter			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
251938032Speter	}
252038032Speter	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
252138032Speter}
252238032Speter
252338032Speter
252438032Speter/* ARGSUSED */
252538032SpeterSIGFUNC_DECL
252638032Spetersigusr1(sig)
252738032Speter	int sig;
252838032Speter{
252938032Speter	dumpstate("user signal");
253038032Speter	return SIGFUNC_RETURN;
253138032Speter}
253238032Speter
253338032Speter
253438032Speter/* ARGSUSED */
253538032SpeterSIGFUNC_DECL
253638032Spetersighup(sig)
253738032Speter	int sig;
253838032Speter{
253964562Sgshapiro	int i;
254064562Sgshapiro	extern int DtableSize;
254164562Sgshapiro
254264562Sgshapiro	clear_events();
254364562Sgshapiro	(void) alarm(0);
254438032Speter	if (SaveArgv[0][0] != '/')
254538032Speter	{
254638032Speter		if (LogLevel > 3)
254764562Sgshapiro			sm_syslog(LOG_INFO, NOQID,
254864562Sgshapiro				  "could not restart: need full path");
254942575Speter		finis(FALSE, EX_OSFILE);
255038032Speter	}
255138032Speter	if (LogLevel > 3)
255264562Sgshapiro		sm_syslog(LOG_INFO, NOQID, "restarting %s %s",
255364562Sgshapiro			  sig == 0 ? "due to control command" : "on signal",
255464562Sgshapiro			  SaveArgv[0]);
255564562Sgshapiro
255664562Sgshapiro	/* Control socket restart? */
255764562Sgshapiro	if (sig != 0)
255864562Sgshapiro		(void) releasesignal(SIGHUP);
255964562Sgshapiro
256042575Speter	closecontrolsocket(TRUE);
256138032Speter	if (drop_privileges(TRUE) != EX_OK)
256238032Speter	{
256338032Speter		if (LogLevel > 0)
256464562Sgshapiro			sm_syslog(LOG_ALERT, NOQID,
256564562Sgshapiro				  "could not set[ug]id(%d, %d): %m",
256664562Sgshapiro				  RunAsUid, RunAsGid);
256742575Speter		finis(FALSE, EX_OSERR);
256838032Speter	}
256964562Sgshapiro
257064562Sgshapiro	/* arrange for all the files to be closed */
257164562Sgshapiro	for (i = 3; i < DtableSize; i++)
257264562Sgshapiro	{
257364562Sgshapiro		register int j;
257464562Sgshapiro
257564562Sgshapiro		if ((j = fcntl(i, F_GETFD, 0)) != -1)
257664562Sgshapiro			(void) fcntl(i, F_SETFD, j | FD_CLOEXEC);
257764562Sgshapiro	}
257864562Sgshapiro
257964562Sgshapiro	(void) execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
258038032Speter	if (LogLevel > 0)
258164562Sgshapiro		sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m",
258264562Sgshapiro			  SaveArgv[0]);
258342575Speter	finis(FALSE, EX_OSFILE);
258438032Speter}
258538032Speter/*
258638032Speter**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
258738032Speter**
258838032Speter**	Parameters:
258938032Speter**		to_real_uid -- if set, drop to the real uid instead
259038032Speter**			of the RunAsUser.
259138032Speter**
259238032Speter**	Returns:
259338032Speter**		EX_OSERR if the setuid failed.
259438032Speter**		EX_OK otherwise.
259538032Speter*/
259638032Speter
259738032Speterint
259838032Speterdrop_privileges(to_real_uid)
259938032Speter	bool to_real_uid;
260038032Speter{
260138032Speter	int rval = EX_OK;
260238032Speter	GIDSET_T emptygidset[1];
260338032Speter
260438032Speter	if (tTd(47, 1))
260564562Sgshapiro		dprintf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
260664562Sgshapiro			(int)to_real_uid, (int)RealUid,
260764562Sgshapiro			(int)RealGid, (int)RunAsUid, (int)RunAsGid);
260838032Speter
260938032Speter	if (to_real_uid)
261038032Speter	{
261138032Speter		RunAsUserName = RealUserName;
261238032Speter		RunAsUid = RealUid;
261338032Speter		RunAsGid = RealGid;
261438032Speter	}
261538032Speter
261638032Speter	/* make sure no one can grab open descriptors for secret files */
261738032Speter	endpwent();
261838032Speter
261938032Speter	/* reset group permissions; these can be set later */
262038032Speter	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
262138032Speter	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
262264562Sgshapiro	{
262364562Sgshapiro		syserr("drop_privileges: setgroups(1, %d) failed",
262464562Sgshapiro		       (int)emptygidset[0]);
262538032Speter		rval = EX_OSERR;
262664562Sgshapiro	}
262738032Speter
262838032Speter	/* reset primary group and user id */
262938032Speter	if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
263064562Sgshapiro	{
263164562Sgshapiro		syserr("drop_privileges: setgid(%d) failed", (int)RunAsGid);
263238032Speter		rval = EX_OSERR;
263364562Sgshapiro	}
263464562Sgshapiro	if (to_real_uid || RunAsUid != 0)
263564562Sgshapiro	{
263664562Sgshapiro		uid_t euid = geteuid();
263764562Sgshapiro
263864562Sgshapiro		if (setuid(RunAsUid) < 0)
263964562Sgshapiro		{
264064562Sgshapiro			syserr("drop_privileges: setuid(%d) failed",
264164562Sgshapiro			       (int)RunAsUid);
264264562Sgshapiro			rval = EX_OSERR;
264364562Sgshapiro		}
264464562Sgshapiro		else if (RunAsUid != 0 && setuid(0) == 0)
264564562Sgshapiro		{
264664562Sgshapiro			/*
264764562Sgshapiro			**  Believe it or not, the Linux capability model
264864562Sgshapiro			**  allows a non-root process to override setuid()
264964562Sgshapiro			**  on a process running as root and prevent that
265064562Sgshapiro			**  process from dropping privileges.
265164562Sgshapiro			*/
265264562Sgshapiro
265364562Sgshapiro			syserr("drop_privileges: setuid(0) succeeded (when it should not)");
265464562Sgshapiro			rval = EX_OSERR;
265564562Sgshapiro		}
265664562Sgshapiro		else if (RunAsUid != euid && setuid(euid) == 0)
265764562Sgshapiro		{
265864562Sgshapiro			/*
265964562Sgshapiro			**  Some operating systems will keep the saved-uid
266064562Sgshapiro			**  if a non-root effective-uid calls setuid(real-uid)
266164562Sgshapiro			**  making it possible to set it back again later.
266264562Sgshapiro			*/
266364562Sgshapiro
266464562Sgshapiro			syserr("drop_privileges: Unable to drop non-root set-user-id privileges");
266564562Sgshapiro			rval = EX_OSERR;
266664562Sgshapiro		}
266764562Sgshapiro	}
266838032Speter	if (tTd(47, 5))
266938032Speter	{
267064562Sgshapiro		dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
267164562Sgshapiro			(int)geteuid(), (int)getuid(),
267264562Sgshapiro			(int)getegid(), (int)getgid());
267364562Sgshapiro		dprintf("drop_privileges: RunAsUser = %d:%d\n",
267464562Sgshapiro			(int)RunAsUid, (int)RunAsGid);
267564562Sgshapiro		if (tTd(47, 10))
267664562Sgshapiro			dprintf("drop_privileges: rval = %d\n", rval);
267738032Speter	}
267838032Speter	return rval;
267938032Speter}
268038032Speter/*
268138032Speter**  FILL_FD -- make sure a file descriptor has been properly allocated
268238032Speter**
268338032Speter**	Used to make sure that stdin/out/err are allocated on startup
268438032Speter**
268538032Speter**	Parameters:
268638032Speter**		fd -- the file descriptor to be filled.
268738032Speter**		where -- a string used for logging.  If NULL, this is
268838032Speter**			being called on startup, and logging should
268938032Speter**			not be done.
269038032Speter**
269138032Speter**	Returns:
269238032Speter**		none
269338032Speter*/
269438032Speter
269538032Spetervoid
269638032Speterfill_fd(fd, where)
269738032Speter	int fd;
269838032Speter	char *where;
269938032Speter{
270038032Speter	int i;
270138032Speter	struct stat stbuf;
270238032Speter
270338032Speter	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
270438032Speter		return;
270538032Speter
270638032Speter	if (where != NULL)
270738032Speter		syserr("fill_fd: %s: fd %d not open", where, fd);
270838032Speter	else
270938032Speter		MissingFds |= 1 << fd;
271038032Speter	i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
271138032Speter	if (i < 0)
271238032Speter	{
271338032Speter		syserr("!fill_fd: %s: cannot open /dev/null",
271438032Speter			where == NULL ? "startup" : where);
271538032Speter	}
271638032Speter	if (fd != i)
271738032Speter	{
271838032Speter		(void) dup2(i, fd);
271938032Speter		(void) close(i);
272038032Speter	}
272138032Speter}
272238032Speter/*
272338032Speter**  TESTMODELINE -- process a test mode input line
272438032Speter**
272538032Speter**	Parameters:
272638032Speter**		line -- the input line.
272738032Speter**		e -- the current environment.
272838032Speter**	Syntax:
272938032Speter**		#  a comment
273038032Speter**		.X process X as a configuration line
273138032Speter**		=X dump a configuration item (such as mailers)
273238032Speter**		$X dump a macro or class
273338032Speter**		/X try an activity
273438032Speter**		X  normal process through rule set X
273538032Speter*/
273638032Speter
273764562Sgshapirostatic void
273838032Spetertestmodeline(line, e)
273938032Speter	char *line;
274038032Speter	ENVELOPE *e;
274138032Speter{
274238032Speter	register char *p;
274338032Speter	char *q;
274438032Speter	auto char *delimptr;
274538032Speter	int mid;
274638032Speter	int i, rs;
274738032Speter	STAB *map;
274838032Speter	char **s;
274938032Speter	struct rewrite *rw;
275038032Speter	ADDRESS a;
275138032Speter	static int tryflags = RF_COPYNONE;
275238032Speter	char exbuf[MAXLINE];
275364562Sgshapiro	extern u_char TokTypeNoC[];
275438032Speter
275564562Sgshapiro#if _FFR_ADDR_TYPE
275664562Sgshapiro	define(macid("{addr_type}", NULL), "e r", e);
275764562Sgshapiro#endif /* _FFR_ADDR_TYPE */
275838032Speter	switch (line[0])
275938032Speter	{
276038032Speter	  case '#':
276164562Sgshapiro	  case '\0':
276238032Speter		return;
276338032Speter
276438032Speter	  case '?':
276564562Sgshapiro		help("-bt", e);
276638032Speter		return;
276738032Speter
276838032Speter	  case '.':		/* config-style settings */
276938032Speter		switch (line[1])
277038032Speter		{
277138032Speter		  case 'D':
277238032Speter			mid = macid(&line[2], &delimptr);
277338032Speter			if (mid == '\0')
277438032Speter				return;
277538032Speter			translate_dollars(delimptr);
277638032Speter			define(mid, newstr(delimptr), e);
277738032Speter			break;
277838032Speter
277938032Speter		  case 'C':
278038032Speter			if (line[2] == '\0')	/* not to call syserr() */
278138032Speter				return;
278238032Speter
278338032Speter			mid = macid(&line[2], &delimptr);
278438032Speter			if (mid == '\0')
278538032Speter				return;
278638032Speter			translate_dollars(delimptr);
278738032Speter			expand(delimptr, exbuf, sizeof exbuf, e);
278838032Speter			p = exbuf;
278938032Speter			while (*p != '\0')
279038032Speter			{
279138032Speter				register char *wd;
279238032Speter				char delim;
279338032Speter
279438032Speter				while (*p != '\0' && isascii(*p) && isspace(*p))
279538032Speter					p++;
279638032Speter				wd = p;
279738032Speter				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
279838032Speter					p++;
279938032Speter				delim = *p;
280038032Speter				*p = '\0';
280138032Speter				if (wd[0] != '\0')
280238032Speter					setclass(mid, wd);
280338032Speter				*p = delim;
280438032Speter			}
280538032Speter			break;
280638032Speter
280738032Speter		  case '\0':
280838032Speter			printf("Usage: .[DC]macro value(s)\n");
280938032Speter			break;
281038032Speter
281138032Speter		  default:
281238032Speter			printf("Unknown \".\" command %s\n", line);
281338032Speter			break;
281438032Speter		}
281538032Speter		return;
281638032Speter
281738032Speter	  case '=':		/* config-style settings */
281838032Speter		switch (line[1])
281938032Speter		{
282038032Speter		  case 'S':		/* dump rule set */
282138032Speter			rs = strtorwset(&line[2], NULL, ST_FIND);
282238032Speter			if (rs < 0)
282338032Speter			{
282438032Speter				printf("Undefined ruleset %s\n", &line[2]);
282538032Speter				return;
282638032Speter			}
282738032Speter			rw = RewriteRules[rs];
282838032Speter			if (rw == NULL)
282938032Speter				return;
283038032Speter			do
283138032Speter			{
283264562Sgshapiro				(void) putchar('R');
283338032Speter				s = rw->r_lhs;
283438032Speter				while (*s != NULL)
283538032Speter				{
283638032Speter					xputs(*s++);
283764562Sgshapiro					(void) putchar(' ');
283838032Speter				}
283964562Sgshapiro				(void) putchar('\t');
284064562Sgshapiro				(void) putchar('\t');
284138032Speter				s = rw->r_rhs;
284238032Speter				while (*s != NULL)
284338032Speter				{
284438032Speter					xputs(*s++);
284564562Sgshapiro					(void) putchar(' ');
284638032Speter				}
284764562Sgshapiro				(void) putchar('\n');
284838032Speter			} while ((rw = rw->r_next) != NULL);
284938032Speter			break;
285038032Speter
285138032Speter		  case 'M':
285238032Speter			for (i = 0; i < MAXMAILERS; i++)
285338032Speter			{
285438032Speter				if (Mailer[i] != NULL)
285538032Speter					printmailer(Mailer[i]);
285638032Speter			}
285738032Speter			break;
285838032Speter
285938032Speter		  case '\0':
286038032Speter			printf("Usage: =Sruleset or =M\n");
286138032Speter			break;
286238032Speter
286338032Speter		  default:
286438032Speter			printf("Unknown \"=\" command %s\n", line);
286538032Speter			break;
286638032Speter		}
286738032Speter		return;
286838032Speter
286938032Speter	  case '-':		/* set command-line-like opts */
287038032Speter		switch (line[1])
287138032Speter		{
287238032Speter		  case 'd':
287338032Speter			tTflag(&line[2]);
287438032Speter			break;
287538032Speter
287638032Speter		  case '\0':
287738032Speter			printf("Usage: -d{debug arguments}\n");
287838032Speter			break;
287938032Speter
288038032Speter		  default:
288138032Speter			printf("Unknown \"-\" command %s\n", line);
288238032Speter			break;
288338032Speter		}
288438032Speter		return;
288538032Speter
288638032Speter	  case '$':
288738032Speter		if (line[1] == '=')
288838032Speter		{
288938032Speter			mid = macid(&line[2], NULL);
289038032Speter			if (mid != '\0')
289138032Speter				stabapply(dump_class, mid);
289238032Speter			return;
289338032Speter		}
289438032Speter		mid = macid(&line[1], NULL);
289538032Speter		if (mid == '\0')
289638032Speter			return;
289738032Speter		p = macvalue(mid, e);
289838032Speter		if (p == NULL)
289938032Speter			printf("Undefined\n");
290038032Speter		else
290138032Speter		{
290238032Speter			xputs(p);
290338032Speter			printf("\n");
290438032Speter		}
290538032Speter		return;
290638032Speter
290738032Speter	  case '/':		/* miscellaneous commands */
290838032Speter		p = &line[strlen(line)];
290938032Speter		while (--p >= line && isascii(*p) && isspace(*p))
291038032Speter			*p = '\0';
291138032Speter		p = strpbrk(line, " \t");
291238032Speter		if (p != NULL)
291338032Speter		{
291438032Speter			while (isascii(*p) && isspace(*p))
291538032Speter				*p++ = '\0';
291638032Speter		}
291738032Speter		else
291838032Speter			p = "";
291938032Speter		if (line[1] == '\0')
292038032Speter		{
292138032Speter			printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
292238032Speter			return;
292338032Speter		}
292464562Sgshapiro		if (strcasecmp(&line[1], "quit") == 0)
292564562Sgshapiro		{
292664562Sgshapiro			CurEnv->e_id = NULL;
292764562Sgshapiro			finis(TRUE, ExitStat);
292864562Sgshapiro		}
292938032Speter		if (strcasecmp(&line[1], "mx") == 0)
293038032Speter		{
293138032Speter#if NAMED_BIND
293238032Speter			/* look up MX records */
293338032Speter			int nmx;
293438032Speter			auto int rcode;
293538032Speter			char *mxhosts[MAXMXHOSTS + 1];
293638032Speter
293738032Speter			if (*p == '\0')
293838032Speter			{
293938032Speter				printf("Usage: /mx address\n");
294038032Speter				return;
294138032Speter			}
294264562Sgshapiro			nmx = getmxrr(p, mxhosts, NULL, FALSE, &rcode);
294338032Speter			printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
294438032Speter			for (i = 0; i < nmx; i++)
294538032Speter				printf("\t%s\n", mxhosts[i]);
294664562Sgshapiro#else /* NAMED_BIND */
294738032Speter			printf("No MX code compiled in\n");
294864562Sgshapiro#endif /* NAMED_BIND */
294938032Speter		}
295038032Speter		else if (strcasecmp(&line[1], "canon") == 0)
295138032Speter		{
295238032Speter			char host[MAXHOSTNAMELEN];
295338032Speter
295438032Speter			if (*p == '\0')
295538032Speter			{
295638032Speter				printf("Usage: /canon address\n");
295738032Speter				return;
295838032Speter			}
295964562Sgshapiro			else if (strlcpy(host, p, sizeof host) >= sizeof host)
296038032Speter			{
296138032Speter				printf("Name too long\n");
296238032Speter				return;
296338032Speter			}
296464562Sgshapiro			(void) getcanonname(host, sizeof host, HasWildcardMX);
296538032Speter			printf("getcanonname(%s) returns %s\n", p, host);
296638032Speter		}
296738032Speter		else if (strcasecmp(&line[1], "map") == 0)
296838032Speter		{
296938032Speter			auto int rcode = EX_OK;
297038032Speter			char *av[2];
297138032Speter
297238032Speter			if (*p == '\0')
297338032Speter			{
297438032Speter				printf("Usage: /map mapname key\n");
297538032Speter				return;
297638032Speter			}
297738032Speter			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
297838032Speter				continue;
297938032Speter			if (*q == '\0')
298038032Speter			{
298138032Speter				printf("No key specified\n");
298238032Speter				return;
298338032Speter			}
298438032Speter			*q++ = '\0';
298538032Speter			map = stab(p, ST_MAP, ST_FIND);
298638032Speter			if (map == NULL)
298738032Speter			{
298838032Speter				printf("Map named \"%s\" not found\n", p);
298938032Speter				return;
299038032Speter			}
299164562Sgshapiro			if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
299264562Sgshapiro			    !openmap(&(map->s_map)))
299338032Speter			{
299438032Speter				printf("Map named \"%s\" not open\n", p);
299538032Speter				return;
299638032Speter			}
299738032Speter			printf("map_lookup: %s (%s) ", p, q);
299838032Speter			av[0] = q;
299938032Speter			av[1] = NULL;
300038032Speter			p = (*map->s_map.map_class->map_lookup)
300138032Speter					(&map->s_map, q, av, &rcode);
300238032Speter			if (p == NULL)
300338032Speter				printf("no match (%d)\n", rcode);
300438032Speter			else
300538032Speter				printf("returns %s (%d)\n", p, rcode);
300638032Speter		}
300738032Speter		else if (strcasecmp(&line[1], "try") == 0)
300838032Speter		{
300938032Speter			MAILER *m;
301064562Sgshapiro			STAB *st;
301138032Speter			auto int rcode = EX_OK;
301238032Speter
301338032Speter			q = strpbrk(p, " \t");
301438032Speter			if (q != NULL)
301538032Speter			{
301638032Speter				while (isascii(*q) && isspace(*q))
301738032Speter					*q++ = '\0';
301838032Speter			}
301938032Speter			if (q == NULL || *q == '\0')
302038032Speter			{
302138032Speter				printf("Usage: /try mailer address\n");
302238032Speter				return;
302338032Speter			}
302464562Sgshapiro			st = stab(p, ST_MAILER, ST_FIND);
302564562Sgshapiro			if (st == NULL)
302638032Speter			{
302738032Speter				printf("Unknown mailer %s\n", p);
302838032Speter				return;
302938032Speter			}
303064562Sgshapiro			m = st->s_mailer;
303138032Speter			printf("Trying %s %s address %s for mailer %s\n",
303238032Speter				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
303338032Speter				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
303438032Speter				q, p);
303538032Speter			p = remotename(q, m, tryflags, &rcode, CurEnv);
303638032Speter			printf("Rcode = %d, addr = %s\n",
303738032Speter				rcode, p == NULL ? "<NULL>" : p);
303838032Speter			e->e_to = NULL;
303938032Speter		}
304038032Speter		else if (strcasecmp(&line[1], "tryflags") == 0)
304138032Speter		{
304238032Speter			if (*p == '\0')
304338032Speter			{
304438032Speter				printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
304538032Speter				return;
304638032Speter			}
304738032Speter			for (; *p != '\0'; p++)
304838032Speter			{
304938032Speter				switch (*p)
305038032Speter				{
305138032Speter				  case 'H':
305238032Speter				  case 'h':
305338032Speter					tryflags |= RF_HEADERADDR;
305438032Speter					break;
305538032Speter
305638032Speter				  case 'E':
305738032Speter				  case 'e':
305838032Speter					tryflags &= ~RF_HEADERADDR;
305938032Speter					break;
306038032Speter
306138032Speter				  case 'S':
306238032Speter				  case 's':
306338032Speter					tryflags |= RF_SENDERADDR;
306438032Speter					break;
306538032Speter
306638032Speter				  case 'R':
306738032Speter				  case 'r':
306838032Speter					tryflags &= ~RF_SENDERADDR;
306938032Speter					break;
307038032Speter				}
307138032Speter			}
307264562Sgshapiro#if _FFR_ADDR_TYPE
307364562Sgshapiro			exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
307464562Sgshapiro			exbuf[1] = ' ';
307564562Sgshapiro			exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
307664562Sgshapiro			exbuf[3] = '\0';
307764562Sgshapiro			define(macid("{addr_type}", NULL), newstr(exbuf), e);
307864562Sgshapiro#endif /* _FFR_ADDR_TYPE */
307938032Speter		}
308038032Speter		else if (strcasecmp(&line[1], "parse") == 0)
308138032Speter		{
308238032Speter			if (*p == '\0')
308338032Speter			{
308438032Speter				printf("Usage: /parse address\n");
308538032Speter				return;
308638032Speter			}
308738032Speter			q = crackaddr(p);
308838032Speter			printf("Cracked address = ");
308938032Speter			xputs(q);
309038032Speter			printf("\nParsing %s %s address\n",
309138032Speter				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
309238032Speter				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
309338032Speter			if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
309438032Speter				printf("Cannot parse\n");
309538032Speter			else if (a.q_host != NULL && a.q_host[0] != '\0')
309638032Speter				printf("mailer %s, host %s, user %s\n",
309738032Speter					a.q_mailer->m_name, a.q_host, a.q_user);
309838032Speter			else
309938032Speter				printf("mailer %s, user %s\n",
310038032Speter					a.q_mailer->m_name, a.q_user);
310138032Speter			e->e_to = NULL;
310238032Speter		}
310338032Speter		else
310438032Speter		{
310538032Speter			printf("Unknown \"/\" command %s\n", line);
310638032Speter		}
310738032Speter		return;
310838032Speter	}
310938032Speter
311038032Speter	for (p = line; isascii(*p) && isspace(*p); p++)
311138032Speter		continue;
311238032Speter	q = p;
311338032Speter	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
311438032Speter		p++;
311538032Speter	if (*p == '\0')
311638032Speter	{
311738032Speter		printf("No address!\n");
311838032Speter		return;
311938032Speter	}
312038032Speter	*p = '\0';
312138032Speter	if (invalidaddr(p + 1, NULL))
312238032Speter		return;
312338032Speter	do
312438032Speter	{
312538032Speter		register char **pvp;
312638032Speter		char pvpbuf[PSBUFSIZE];
312738032Speter
312838032Speter		pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
312964562Sgshapiro			      &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL);
313038032Speter		if (pvp == NULL)
313138032Speter			continue;
313238032Speter		p = q;
313338032Speter		while (*p != '\0')
313438032Speter		{
313564562Sgshapiro			int status;
313638032Speter
313738032Speter			rs = strtorwset(p, NULL, ST_FIND);
313838032Speter			if (rs < 0)
313938032Speter			{
314038032Speter				printf("Undefined ruleset %s\n", p);
314138032Speter				break;
314238032Speter			}
314364562Sgshapiro			status = rewrite(pvp, rs, 0, e);
314464562Sgshapiro			if (status != EX_OK)
314538032Speter				printf("== Ruleset %s (%d) status %d\n",
314664562Sgshapiro					p, rs, status);
314738032Speter			while (*p != '\0' && *p++ != ',')
314838032Speter				continue;
314938032Speter		}
315038032Speter	} while (*(p = delimptr) != '\0');
315138032Speter}
315238032Speter
315364562Sgshapirostatic void
315438032Speterdump_class(s, id)
315538032Speter	register STAB *s;
315638032Speter	int id;
315738032Speter{
315838032Speter	if (s->s_type != ST_CLASS)
315938032Speter		return;
316038032Speter	if (bitnset(id & 0xff, s->s_class))
316138032Speter		printf("%s\n", s->s_name);
316238032Speter}
3163