main.c revision 120256
1309260Scognet/*
2309260Scognet * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers.
3309260Scognet *	All rights reserved.
4309260Scognet * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5309260Scognet * Copyright (c) 1988, 1993
6309260Scognet *	The Regents of the University of California.  All rights reserved.
7309260Scognet *
8309260Scognet * By using this file, you agree to the terms and conditions set
9309260Scognet * forth in the LICENSE file which can be found at the top level of
10309260Scognet * the sendmail distribution.
11309260Scognet *
12309260Scognet */
13309260Scognet
14309260Scognet#define _DEFINE
15309260Scognet#include <sendmail.h>
16309260Scognet#include <sm/xtrap.h>
17309260Scognet#include <sm/signal.h>
18309260Scognet
19309260Scognet#ifndef lint
20309260ScognetSM_UNUSED(static char copyright[]) =
21309260Scognet"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
22309260Scognet	All rights reserved.\n\
23309260Scognet     Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
24309260Scognet     Copyright (c) 1988, 1993\n\
25309260Scognet	The Regents of the University of California.  All rights reserved.\n";
26309260Scognet#endif /* ! lint */
27309260Scognet
28309260ScognetSM_RCSID("@(#)$Id: main.c,v 8.887.2.27 2003/08/04 17:23:37 ca Exp $")
29309260Scognet
30309260Scognet
31309260Scognet#if NETINET || NETINET6
32309260Scognet# include <arpa/inet.h>
33309260Scognet#endif /* NETINET || NETINET6 */
34309260Scognet
35309260Scognet/* for getcfname() */
36309260Scognet#include <sendmail/pathnames.h>
37309260Scognet
38309260Scognetstatic SM_DEBUG_T
39309260ScognetDebugNoPRestart = SM_DEBUG_INITIALIZER("no_persistent_restart",
40309260Scognet	"@(#)$Debug: no_persistent_restart - don't restart, log only $");
41309260Scognet
42309260Scognetstatic void	dump_class __P((STAB *, int));
43309260Scognetstatic void	obsolete __P((char **));
44309260Scognetstatic void	testmodeline __P((char *, ENVELOPE *));
45309260Scognetstatic char	*getextenv __P((const char *));
46309260Scognetstatic void	sm_printoptions __P((char **));
47309260Scognetstatic SIGFUNC_DECL	intindebug __P((int));
48309260Scognetstatic SIGFUNC_DECL	sighup __P((int));
49309260Scognetstatic SIGFUNC_DECL	sigpipe __P((int));
50309260Scognetstatic SIGFUNC_DECL	sigterm __P((int));
51309260Scognet#ifdef SIGUSR1
52309260Scognetstatic SIGFUNC_DECL	sigusr1 __P((int));
53309260Scognet#endif /* SIGUSR1 */
54309260Scognet
55309260Scognet/*
56309260Scognet**  SENDMAIL -- Post mail to a set of destinations.
57309260Scognet**
58309260Scognet**	This is the basic mail router.  All user mail programs should
59309260Scognet**	call this routine to actually deliver mail.  Sendmail in
60309260Scognet**	turn calls a bunch of mail servers that do the real work of
61309260Scognet**	delivering the mail.
62309260Scognet**
63309260Scognet**	Sendmail is driven by settings read in from /etc/mail/sendmail.cf
64309260Scognet**	(read by readcf.c).
65309260Scognet**
66309260Scognet**	Usage:
67309260Scognet**		/usr/lib/sendmail [flags] addr ...
68309260Scognet**
69309260Scognet**		See the associated documentation for details.
70309260Scognet**
71309260Scognet**	Authors:
72309260Scognet**		Eric Allman, UCB/INGRES (until 10/81).
73309260Scognet**			     Britton-Lee, Inc., purveyors of fine
74309260Scognet**				database computers (11/81 - 10/88).
75309260Scognet**			     International Computer Science Institute
76309260Scognet**				(11/88 - 9/89).
77309260Scognet**			     UCB/Mammoth Project (10/89 - 7/95).
78309260Scognet**			     InReference, Inc. (8/95 - 1/97).
79309260Scognet**			     Sendmail, Inc. (1/98 - present).
80309260Scognet**		The support of my employers is gratefully acknowledged.
81309260Scognet**			Few of them (Britton-Lee in particular) have had
82309260Scognet**			anything to gain from my involvement in this project.
83309260Scognet**
84309260Scognet**		Gregory Neil Shapiro,
85309260Scognet**			Worcester Polytechnic Institute	(until 3/98).
86309260Scognet**			Sendmail, Inc. (3/98 - present).
87309260Scognet**
88309260Scognet**		Claus Assmann,
89309260Scognet**			Sendmail, Inc. (12/98 - present).
90309260Scognet*/
91309260Scognet
92309260Scognetchar		*FullName;	/* sender's full name */
93309260ScognetENVELOPE	BlankEnvelope;	/* a "blank" envelope */
94309260Scognetstatic ENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
95309260ScognetADDRESS		NullAddress =	/* a null address */
96309260Scognet		{ "", "", NULL, "" };
97309260Scognetchar		*CommandLineArgs;	/* command line args for pid file */
98309260Scognetbool		Warn_Q_option = false;	/* warn about Q option use */
99309260Scognetstatic int	MissingFds = 0;	/* bit map of fds missing on startup */
100309260Scognetchar		*Mbdb = "pw";	/* mailbox database defaults to /etc/passwd */
101309260Scognet
102309260Scognet#ifdef NGROUPS_MAX
103309260ScognetGIDSET_T	InitialGidSet[NGROUPS_MAX];
104309260Scognet#endif /* NGROUPS_MAX */
105309260Scognet
106309260Scognet#define MAXCONFIGLEVEL	10	/* highest config version level known */
107309260Scognet
108309260Scognet#if SASL
109309260Scognetstatic sasl_callback_t srvcallbacks[] =
110309260Scognet{
111309260Scognet	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
112309260Scognet	{	SASL_CB_PROXY_POLICY,	&proxy_policy,	NULL	},
113309260Scognet	{	SASL_CB_LIST_END,	NULL,		NULL	}
114309260Scognet};
115309260Scognet#endif /* SASL */
116309260Scognet
117309260Scognetunsigned int	SubmitMode;
118309260Scognetint		SyslogPrefixLen; /* estimated length of syslog prefix */
119309260Scognet#define PIDLEN		6	/* pid length for computing SyslogPrefixLen */
120309260Scognet#ifndef SL_FUDGE
121309260Scognet# define SL_FUDGE	10	/* fudge offset for SyslogPrefixLen */
122309260Scognet#endif /* ! SL_FUDGE */
123309260Scognet#define SLDLL		8	/* est. length of default syslog label */
124309260Scognet
125309260Scognet
126309260Scognet/* Some options are dangerous to allow users to use in non-submit mode */
127309260Scognet#define CHECK_AGAINST_OPMODE(cmd)					\
128339597Savg{									\
129309260Scognet	if (extraprivs &&						\
130309260Scognet	    OpMode != MD_DELIVER && OpMode != MD_SMTP &&		\
131309260Scognet	    OpMode != MD_ARPAFTP &&					\
132309260Scognet	    OpMode != MD_VERIFY && OpMode != MD_TEST)			\
133309260Scognet	{								\
134309260Scognet		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
135309260Scognet				     "WARNING: Ignoring submission mode -%c option (not in submission mode)\n", \
136339597Savg		       (cmd));						\
137309260Scognet		break;							\
138309260Scognet	}								\
139309260Scognet	if (extraprivs && queuerun)					\
140309260Scognet	{								\
141309260Scognet		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,		\
142309260Scognet				     "WARNING: Ignoring submission mode -%c option with -q\n", \
143339597Savg		       (cmd));						\
144309260Scognet		break;							\
145309260Scognet	}								\
146339597Savg}
147309260Scognet
148309260Scognetint
149339597Savgmain(argc, argv, envp)
150309260Scognet	int argc;
151309260Scognet	char **argv;
152309260Scognet	char **envp;
153309260Scognet{
154309260Scognet	register char *p;
155309260Scognet	char **av;
156309260Scognet	extern char Version[];
157309260Scognet	char *ep, *from;
158309260Scognet	STAB *st;
159309260Scognet	register int i;
160309260Scognet	int j;
161309260Scognet	int dp;
162339597Savg	int fill_errno;
163309260Scognet	int qgrp = NOQGRP;		/* queue group to process */
164339597Savg	bool safecf = true;
165309260Scognet	BITMAP256 *p_flags = NULL;	/* daemon flags */
166309260Scognet	bool warn_C_flag = false;
167339597Savg	bool auth = true;		/* whether to set e_auth_param */
168309260Scognet	char warn_f_flag = '\0';
169309260Scognet	bool run_in_foreground = false;	/* -bD mode */
170309260Scognet	bool queuerun = false, debug = false;
171309260Scognet	struct passwd *pw;
172339597Savg	struct hostent *hp;
173309260Scognet	char *nullserver = NULL;
174339597Savg	char *authinfo = NULL;
175309260Scognet	char *sysloglabel = NULL;	/* label for syslog */
176309260Scognet	char *conffile = NULL;		/* name of .cf file */
177309260Scognet	char *queuegroup = NULL;	/* queue group to process */
178339597Savg#if _FFR_QUARANTINE
179309260Scognet	char *quarantining = NULL;	/* quarantine queue items? */
180339597Savg#endif /* _FFR_QUARANTINE */
181309260Scognet	bool extraprivs;
182309260Scognet	bool forged, negate;
183339650Savg	bool queuepersistent = false;	/* queue runner process runs forever */
184339650Savg	bool foregroundqueue = false;	/* queue run in foreground */
185339650Savg	bool save_val;			/* to save some bool var. */
186339650Savg	int cftype;			/* which cf file to use? */
187339650Savg	static time_t starttime = 0;	/* when was process started */
188339650Savg	struct stat traf_st;		/* for TrafficLog FIFO check */
189309260Scognet	char buf[MAXLINE];
190339650Savg	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
191339597Savg	static char rnamebuf[MAXNAME];	/* holds RealUserName */
192309260Scognet	char *emptyenviron[1];
193309260Scognet#if STARTTLS
194309260Scognet	bool tls_ok;
195339597Savg#endif /* STARTTLS */
196309260Scognet	QUEUE_CHAR *new;
197309260Scognet	ENVELOPE *e;
198339597Savg	extern int DtableSize;
199339650Savg	extern int optind;
200339597Savg	extern int opterr;
201309260Scognet	extern char *optarg;
202309260Scognet	extern char **environ;
203309260Scognet#if SASL
204309260Scognet	extern void sm_sasl_init __P((void));
205309260Scognet#endif /* SASL */
206339597Savg
207339597Savg#if USE_ENVIRON
208309260Scognet	envp = environ;
209309260Scognet#endif /* USE_ENVIRON */
210339650Savg
211339650Savg	/* turn off profiling */
212339650Savg	SM_PROF(0);
213339650Savg
214309260Scognet	/* install default exception handler */
215339597Savg	sm_exc_newthread(fatal_error);
216309260Scognet
217309260Scognet	/* set the default in/out channel so errors reported to screen */
218309260Scognet	InChannel = smioin;
219309260Scognet	OutChannel = smioout;
220309260Scognet
221309260Scognet	/*
222339597Savg	**  Check to see if we reentered.
223339597Savg	**	This would normally happen if e_putheader or e_putbody
224339597Savg	**	were NULL when invoked.
225309260Scognet	*/
226309260Scognet
227309260Scognet	if (starttime != 0)
228309260Scognet	{
229309260Scognet		syserr("main: reentered!");
230309260Scognet		abort();
231309260Scognet	}
232339597Savg	starttime = curtime();
233339597Savg
234309260Scognet	/* avoid null pointer dereferences */
235309260Scognet	TermEscape.te_rv_on = TermEscape.te_rv_off = "";
236309260Scognet
237339597Savg	RealUid = getuid();
238309260Scognet	RealGid = getgid();
239309260Scognet
240309260Scognet	/* Check if sendmail is running with extra privs */
241339597Savg	extraprivs = (RealUid != 0 &&
242309260Scognet		      (geteuid() != getuid() || getegid() != getgid()));
243309260Scognet
244309260Scognet	CurrentPid = getpid();
245309260Scognet
246309260Scognet	/* get whatever .cf file is right for the opmode */
247309260Scognet	cftype = SM_GET_RIGHT_CF;
248339597Savg
249339597Savg	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
250309260Scognet	DtableSize = getdtsize();
251339597Savg	if (DtableSize > 256)
252309260Scognet		DtableSize = 256;
253309260Scognet
254309260Scognet	/*
255309260Scognet	**  Be sure we have enough file descriptors.
256339597Savg	**	But also be sure that 0, 1, & 2 are open.
257309260Scognet	*/
258339597Savg
259309260Scognet	/* reset errno and fill_errno; the latter is used way down below */
260309260Scognet	errno = fill_errno = 0;
261309260Scognet	fill_fd(STDIN_FILENO, NULL);
262309260Scognet	if (errno != 0)
263309260Scognet		fill_errno = errno;
264309260Scognet	fill_fd(STDOUT_FILENO, NULL);
265309260Scognet	if (errno != 0)
266309260Scognet		fill_errno = errno;
267309260Scognet	fill_fd(STDERR_FILENO, NULL);
268309260Scognet	if (errno != 0)
269309260Scognet		fill_errno = errno;
270309260Scognet
271309260Scognet	i = DtableSize;
272339597Savg	while (--i > 0)
273309260Scognet	{
274339597Savg		if (i != STDIN_FILENO && i != STDOUT_FILENO &&
275309260Scognet		    i != STDERR_FILENO)
276309260Scognet			(void) close(i);
277309260Scognet	}
278339597Savg	errno = 0;
279309260Scognet
280339597Savg#if LOG
281339597Savg# ifndef SM_LOG_STR
282339597Savg#  define SM_LOG_STR	"sendmail"
283309260Scognet# endif /* ! SM_LOG_STR */
284309260Scognet#  ifdef LOG_MAIL
285309260Scognet	openlog(SM_LOG_STR, LOG_PID, LOG_MAIL);
286339597Savg#  else /* LOG_MAIL */
287309260Scognet	openlog(SM_LOG_STR, LOG_PID);
288339597Savg#  endif /* LOG_MAIL */
289339597Savg#endif /* LOG */
290339597Savg
291309260Scognet	/*
292309260Scognet	**  Seed the random number generator.
293309260Scognet	**  Used for queue file names, picking a queue directory, and
294339597Savg	**  MX randomization.
295309260Scognet	*/
296339597Savg
297339597Savg	seed_random();
298309260Scognet
299309260Scognet	/* do machine-dependent initializations */
300309260Scognet	init_md(argc, argv);
301339597Savg
302309260Scognet
303309260Scognet	SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) + SL_FUDGE + SLDLL;
304339597Savg
305309260Scognet	/* reset status from syserr() calls for missing file descriptors */
306309260Scognet	Errors = 0;
307339597Savg	ExitStat = EX_OK;
308339597Savg
309339597Savg	SubmitMode = SUBMIT_UNKNOWN;
310309260Scognet#if XDEBUG
311309260Scognet	checkfd012("after openlog");
312309260Scognet#endif /* XDEBUG */
313309260Scognet
314309260Scognet	tTsetup(tTdvect, sizeof tTdvect, "0-99.1,*_trace_*.1");
315339597Savg
316339597Savg#ifdef NGROUPS_MAX
317339597Savg	/* save initial group set for future checks */
318339597Savg	i = getgroups(NGROUPS_MAX, InitialGidSet);
319309260Scognet	if (i <= 0)
320309260Scognet	{
321309260Scognet		InitialGidSet[0] = (GID_T) -1;
322339597Savg		i = 0;
323339597Savg	}
324339597Savg	while (i < NGROUPS_MAX)
325339597Savg		InitialGidSet[i++] = InitialGidSet[0];
326309260Scognet#endif /* NGROUPS_MAX */
327309260Scognet
328309260Scognet	/* drop group id privileges (RunAsUser not yet set) */
329339597Savg	dp = drop_privileges(false);
330339597Savg	setstat(dp);
331339597Savg
332339597Savg#ifdef SIGUSR1
333309260Scognet	/* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
334309260Scognet	if (!extraprivs)
335309260Scognet	{
336309260Scognet		/* arrange to dump state on user-1 signal */
337309260Scognet		(void) sm_signal(SIGUSR1, sigusr1);
338309260Scognet	}
339309260Scognet	else
340339597Savg	{
341309260Scognet		/* ignore user-1 signal */
342339597Savg		(void) sm_signal(SIGUSR1, SIG_IGN);
343309260Scognet	}
344339597Savg#endif /* SIGUSR1 */
345309260Scognet
346339597Savg	/* initialize for setproctitle */
347309260Scognet	initsetproctitle(argc, argv, envp);
348339597Savg
349309260Scognet	/* Handle any non-getoptable constructions. */
350309260Scognet	obsolete(argv);
351309260Scognet
352309260Scognet	/*
353309260Scognet	**  Do a quick prescan of the argument list.
354309260Scognet	*/
355309260Scognet
356339597Savg
357309260Scognet	/* find initial opMode */
358309260Scognet	OpMode = MD_DELIVER;
359309260Scognet	av = argv;
360309260Scognet	p = strrchr(*av, '/');
361309260Scognet	if (p++ == NULL)
362309260Scognet		p = *av;
363309260Scognet	if (strcmp(p, "newaliases") == 0)
364339597Savg		OpMode = MD_INITALIAS;
365339597Savg	else if (strcmp(p, "mailq") == 0)
366309260Scognet		OpMode = MD_PRINT;
367309260Scognet	else if (strcmp(p, "smtpd") == 0)
368339597Savg		OpMode = MD_DAEMON;
369309260Scognet	else if (strcmp(p, "hoststat") == 0)
370339597Savg		OpMode = MD_HOSTSTAT;
371309260Scognet	else if (strcmp(p, "purgestat") == 0)
372309260Scognet		OpMode = MD_PURGESTAT;
373309260Scognet
374309260Scognet#if _FFR_QUARANTINE
375309260Scognet# if defined(__osf__) || defined(_AIX3)
376309260Scognet#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:xQ:"
377309260Scognet# endif /* defined(__osf__) || defined(_AIX3) */
378309260Scognet# if defined(sony_news)
379309260Scognet#  define OPTIONS	"A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:"
380309260Scognet# endif /* defined(sony_news) */
381309260Scognet# ifndef OPTIONS
382309260Scognet#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:Q:"
383339597Savg# endif /* ! OPTIONS */
384309260Scognet#else /* _FFR_QUARANTINE */
385309260Scognet# if defined(__osf__) || defined(_AIX3)
386309260Scognet#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:x"
387309260Scognet# endif /* defined(__osf__) || defined(_AIX3) */
388339597Savg# if defined(sony_news)
389339597Savg#  define OPTIONS	"A:B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtV:vX:"
390309260Scognet# endif /* defined(sony_news) */
391339597Savg# ifndef OPTIONS
392339597Savg#  define OPTIONS	"A:B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtV:vX:"
393339597Savg# endif /* ! OPTIONS */
394309260Scognet#endif /* _FFR_QUARANTINE */
395309260Scognet
396309260Scognet	/* Set to 0 to allow -b; need to check optarg before using it! */
397339597Savg	opterr = 0;
398339597Savg	while ((j = getopt(argc, argv, OPTIONS)) != -1)
399309260Scognet	{
400339597Savg		switch (j)
401339597Savg		{
402309260Scognet		  case 'b':	/* operations mode */
403309260Scognet			j = (optarg == NULL) ? ' ' : *optarg;
404309260Scognet			switch (j)
405339597Savg			{
406309260Scognet			  case MD_DAEMON:
407339597Savg			  case MD_FGDAEMON:
408339597Savg			  case MD_SMTP:
409339597Savg			  case MD_INITALIAS:
410339597Savg			  case MD_DELIVER:
411309260Scognet			  case MD_VERIFY:
412309260Scognet			  case MD_TEST:
413309260Scognet			  case MD_PRINT:
414339597Savg			  case MD_PRINTNQE:
415339597Savg			  case MD_HOSTSTAT:
416339597Savg			  case MD_PURGESTAT:
417309260Scognet			  case MD_ARPAFTP:
418309260Scognet				OpMode = j;
419309260Scognet				break;
420339597Savg
421339597Savg			  case MD_FREEZE:
422339597Savg				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
423309260Scognet						     "Frozen configurations unsupported\n");
424309260Scognet				return EX_USAGE;
425309260Scognet
426309260Scognet			  default:
427309260Scognet				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
428309260Scognet						     "Invalid operation mode %c\n",
429339597Savg						     j);
430339597Savg				return EX_USAGE;
431339597Savg			}
432339597Savg			break;
433339597Savg
434339597Savg		  case 'd':
435339597Savg			debug = true;
436309260Scognet			tTflag(optarg);
437309260Scognet			(void) sm_io_setvbuf(smioout, SM_TIME_DEFAULT,
438309260Scognet					     (char *) NULL, SM_IO_NBF,
439					     SM_IO_BUFSIZ);
440			break;
441
442		  case 'G':	/* relay (gateway) submission */
443			SubmitMode = SUBMIT_MTA;
444			break;
445
446		  case 'L':
447			if (optarg == NULL)
448			{
449				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
450						     "option requires an argument -- '%c'",
451						     (char) j);
452				return EX_USAGE;
453			}
454			j = SM_MIN(strlen(optarg), 24) + 1;
455			sysloglabel = xalloc(j);
456			(void) sm_strlcpy(sysloglabel, optarg, j);
457			SyslogPrefixLen = PIDLEN + (MAXQFNAME - 3) +
458					  SL_FUDGE + j;
459			break;
460
461#if _FFR_QUARANTINE
462		  case 'Q':
463#endif /* _FFR_QUARANTINE */
464		  case 'q':
465			/* just check if it is there */
466			queuerun = true;
467			break;
468		}
469	}
470	opterr = 1;
471
472	/* Don't leak queue information via debug flags */
473	if (extraprivs && queuerun && debug)
474	{
475		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
476				     "WARNING: Can not use -d with -q.  Disabling debugging.\n");
477		sm_debug_setfile(NULL);
478		(void) memset(tTdvect, '\0', sizeof tTdvect);
479	}
480
481#if LOG
482	if (sysloglabel != NULL)
483	{
484		/* Sanitize the string */
485		for (p = sysloglabel; *p != '\0'; p++)
486		{
487			if (!isascii(*p) || !isprint(*p) || *p == '%')
488				*p = '*';
489		}
490		closelog();
491#  ifdef LOG_MAIL
492		openlog(sysloglabel, LOG_PID, LOG_MAIL);
493#  else /* LOG_MAIL */
494		openlog(sysloglabel, LOG_PID);
495#  endif /* LOG_MAIL */
496	}
497#endif /* LOG */
498
499	/* set up the blank envelope */
500	BlankEnvelope.e_puthdr = putheader;
501	BlankEnvelope.e_putbody = putbody;
502	BlankEnvelope.e_xfp = NULL;
503	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
504	CurEnv = &BlankEnvelope;
505	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
506
507	/*
508	**  Set default values for variables.
509	**	These cannot be in initialized data space.
510	*/
511
512	setdefaults(&BlankEnvelope);
513	initmacros(&BlankEnvelope);
514
515	/* reset macro */
516	set_op_mode(OpMode);
517
518	pw = sm_getpwuid(RealUid);
519	if (pw != NULL)
520		(void) sm_strlcpy(rnamebuf, pw->pw_name, sizeof rnamebuf);
521	else
522		(void) sm_snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
523				   (int) RealUid);
524
525	RealUserName = rnamebuf;
526
527	if (tTd(0, 101))
528	{
529		sm_dprintf("Version %s\n", Version);
530		finis(false, true, EX_OK);
531		/* NOTREACHED */
532	}
533
534	/*
535	**  if running non-set-user-ID binary as non-root, pretend
536	**  we are the RunAsUid
537	*/
538
539	if (RealUid != 0 && geteuid() == RealUid)
540	{
541		if (tTd(47, 1))
542			sm_dprintf("Non-set-user-ID binary: RunAsUid = RealUid = %d\n",
543				   (int) RealUid);
544		RunAsUid = RealUid;
545	}
546	else if (geteuid() != 0)
547		RunAsUid = geteuid();
548
549	EffGid = getegid();
550	if (RealUid != 0 && EffGid == RealGid)
551		RunAsGid = RealGid;
552
553	if (tTd(47, 5))
554	{
555		sm_dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
556			   (int) geteuid(), (int) getuid(),
557			   (int) getegid(), (int) getgid());
558		sm_dprintf("main: RunAsUser = %d:%d\n",
559			   (int) RunAsUid, (int) RunAsGid);
560	}
561
562	/* save command line arguments */
563	j = 0;
564	for (av = argv; *av != NULL; )
565		j += strlen(*av++) + 1;
566	SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
567	CommandLineArgs = xalloc(j);
568	p = CommandLineArgs;
569	for (av = argv, i = 0; *av != NULL; )
570	{
571		int h;
572
573		SaveArgv[i++] = newstr(*av);
574		if (av != argv)
575			*p++ = ' ';
576		(void) sm_strlcpy(p, *av++, j);
577		h = strlen(p);
578		p += h;
579		j -= h + 1;
580	}
581	SaveArgv[i] = NULL;
582
583	if (tTd(0, 1))
584	{
585		extern char *CompileOptions[];
586
587		sm_dprintf("Version %s\n Compiled with:", Version);
588		sm_printoptions(CompileOptions);
589	}
590	if (tTd(0, 10))
591	{
592		extern char *OsCompileOptions[];
593
594		sm_dprintf("    OS Defines:");
595		sm_printoptions(OsCompileOptions);
596#ifdef _PATH_UNIX
597		sm_dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
598#endif /* _PATH_UNIX */
599
600		sm_dprintf("     Conf file:\t%s (default for MSP)\n",
601			   getcfname(OpMode, SubmitMode, SM_GET_SUBMIT_CF,
602				     conffile));
603		sm_dprintf("     Conf file:\t%s (default for MTA)\n",
604			   getcfname(OpMode, SubmitMode, SM_GET_SENDMAIL_CF,
605				     conffile));
606		sm_dprintf("      Pid file:\t%s (default)\n", PidFile);
607	}
608
609	if (tTd(0, 12))
610	{
611		extern char *SmCompileOptions[];
612
613		sm_dprintf(" libsm Defines:");
614		sm_printoptions(SmCompileOptions);
615	}
616
617	if (tTd(0, 13))
618	{
619		extern char *FFRCompileOptions[];
620
621		sm_dprintf("   FFR Defines:");
622		sm_printoptions(FFRCompileOptions);
623	}
624
625	/* clear sendmail's environment */
626	ExternalEnviron = environ;
627	emptyenviron[0] = NULL;
628	environ = emptyenviron;
629
630	/*
631	**  restore any original TZ setting until TimeZoneSpec has been
632	**  determined - or early log messages may get bogus time stamps
633	*/
634
635	if ((p = getextenv("TZ")) != NULL)
636	{
637		char *tz;
638		int tzlen;
639
640		/* XXX check for reasonable length? */
641		tzlen = strlen(p) + 4;
642		tz = xalloc(tzlen);
643		(void) sm_strlcpyn(tz, tzlen, 2, "TZ=", p);
644
645		/* XXX check return code? */
646		(void) putenv(tz);
647	}
648
649	/* prime the child environment */
650	setuserenv("AGENT", "sendmail");
651
652	(void) sm_signal(SIGPIPE, SIG_IGN);
653	OldUmask = umask(022);
654	FullName = getextenv("NAME");
655	if (FullName != NULL)
656		FullName = newstr(FullName);
657
658	/*
659	**  Initialize name server if it is going to be used.
660	*/
661
662#if NAMED_BIND
663	if (!bitset(RES_INIT, _res.options))
664		(void) res_init();
665	if (tTd(8, 8))
666		_res.options |= RES_DEBUG;
667	else
668		_res.options &= ~RES_DEBUG;
669# ifdef RES_NOALIASES
670	if (bitset(RES_NOALIASES, _res.options))
671		ResNoAliases = true;
672	_res.options |= RES_NOALIASES;
673# endif /* RES_NOALIASES */
674	TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
675	TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
676	TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
677	TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
678	TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
679	TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
680#endif /* NAMED_BIND */
681
682	errno = 0;
683	from = NULL;
684
685	/* initialize some macros, etc. */
686	init_vendor_macros(&BlankEnvelope);
687
688	/* version */
689	macdefine(&BlankEnvelope.e_macro, A_PERM, 'v', Version);
690
691	/* hostname */
692	hp = myhostname(jbuf, sizeof jbuf);
693	if (jbuf[0] != '\0')
694	{
695		struct utsname utsname;
696
697		if (tTd(0, 4))
698			sm_dprintf("Canonical name: %s\n", jbuf);
699		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'w', jbuf);
700		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'j', jbuf);
701		setclass('w', jbuf);
702
703		p = strchr(jbuf, '.');
704		if (p != NULL)
705		{
706			if (p[1] != '\0')
707			{
708				macdefine(&BlankEnvelope.e_macro, A_TEMP, 'm',
709					  &p[1]);
710			}
711			while (p != NULL && strchr(&p[1], '.') != NULL)
712			{
713				*p = '\0';
714				if (tTd(0, 4))
715					sm_dprintf("\ta.k.a.: %s\n", jbuf);
716				setclass('w', jbuf);
717				*p++ = '.';
718				p = strchr(p, '.');
719			}
720		}
721
722		if (uname(&utsname) >= 0)
723			p = utsname.nodename;
724		else
725		{
726			if (tTd(0, 22))
727				sm_dprintf("uname failed (%s)\n",
728					   sm_errstring(errno));
729			makelower(jbuf);
730			p = jbuf;
731		}
732		if (tTd(0, 4))
733			sm_dprintf(" UUCP nodename: %s\n", p);
734		macdefine(&BlankEnvelope.e_macro, A_TEMP, 'k', p);
735		setclass('k', p);
736		setclass('w', p);
737	}
738	if (hp != NULL)
739	{
740		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
741		{
742			if (tTd(0, 4))
743				sm_dprintf("\ta.k.a.: %s\n", *av);
744			setclass('w', *av);
745		}
746#if NETINET || NETINET6
747		for (i = 0; i >= 0 && hp->h_addr_list[i] != NULL; i++)
748		{
749# if NETINET6
750			char *addr;
751			char buf6[INET6_ADDRSTRLEN];
752			struct in6_addr ia6;
753# endif /* NETINET6 */
754# if NETINET
755			struct in_addr ia;
756# endif /* NETINET */
757			char ipbuf[103];
758
759			ipbuf[0] = '\0';
760			switch (hp->h_addrtype)
761			{
762# if NETINET
763			  case AF_INET:
764				if (hp->h_length != INADDRSZ)
765					break;
766
767				memmove(&ia, hp->h_addr_list[i], INADDRSZ);
768				(void) sm_snprintf(ipbuf, sizeof ipbuf,
769						   "[%.100s]", inet_ntoa(ia));
770				break;
771# endif /* NETINET */
772
773# if NETINET6
774			  case AF_INET6:
775				if (hp->h_length != IN6ADDRSZ)
776					break;
777
778				memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
779				addr = anynet_ntop(&ia6, buf6, sizeof buf6);
780				if (addr != NULL)
781					(void) sm_snprintf(ipbuf, sizeof ipbuf,
782							   "[%.100s]", addr);
783				break;
784# endif /* NETINET6 */
785			}
786			if (ipbuf[0] == '\0')
787				break;
788
789			if (tTd(0, 4))
790				sm_dprintf("\ta.k.a.: %s\n", ipbuf);
791			setclass('w', ipbuf);
792		}
793#endif /* NETINET || NETINET6 */
794#if NETINET6
795		freehostent(hp);
796		hp = NULL;
797#endif /* NETINET6 */
798	}
799
800	/* current time */
801	macdefine(&BlankEnvelope.e_macro, A_TEMP, 'b', arpadate((char *) NULL));
802
803	/* current load average */
804	sm_getla();
805
806	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
807	QueueLimitSender = (QUEUE_CHAR *) NULL;
808	QueueLimitId = (QUEUE_CHAR *) NULL;
809#if _FFR_QUARANTINE
810	QueueLimitQuarantine = (QUEUE_CHAR *) NULL;
811#endif /* _FFR_QUARANTINE */
812
813	/*
814	**  Crack argv.
815	*/
816
817	optind = 1;
818	while ((j = getopt(argc, argv, OPTIONS)) != -1)
819	{
820		switch (j)
821		{
822		  case 'b':	/* operations mode */
823			/* already done */
824			break;
825
826		  case 'A':	/* use Alternate sendmail/submit.cf */
827			cftype = optarg[0] == 'c' ? SM_GET_SUBMIT_CF
828						  : SM_GET_SENDMAIL_CF;
829			break;
830
831		  case 'B':	/* body type */
832			CHECK_AGAINST_OPMODE(j);
833			BlankEnvelope.e_bodytype = newstr(optarg);
834			break;
835
836		  case 'C':	/* select configuration file (already done) */
837			if (RealUid != 0)
838				warn_C_flag = true;
839			conffile = newstr(optarg);
840			dp = drop_privileges(true);
841			setstat(dp);
842			safecf = false;
843			break;
844
845		  case 'd':	/* debugging */
846			/* already done */
847			break;
848
849		  case 'f':	/* from address */
850		  case 'r':	/* obsolete -f flag */
851			CHECK_AGAINST_OPMODE(j);
852			if (from != NULL)
853			{
854				usrerr("More than one \"from\" person");
855				ExitStat = EX_USAGE;
856				break;
857			}
858			if (optarg[0] == '\0')
859				from = newstr("<>");
860			else
861				from = newstr(denlstring(optarg, true, true));
862			if (strcmp(RealUserName, from) != 0)
863				warn_f_flag = j;
864			break;
865
866		  case 'F':	/* set full name */
867			CHECK_AGAINST_OPMODE(j);
868			FullName = newstr(optarg);
869			break;
870
871		  case 'G':	/* relay (gateway) submission */
872			/* already set */
873			CHECK_AGAINST_OPMODE(j);
874			break;
875
876		  case 'h':	/* hop count */
877			CHECK_AGAINST_OPMODE(j);
878			BlankEnvelope.e_hopcount = (short) strtol(optarg, &ep,
879								  10);
880			(void) sm_snprintf(buf, sizeof buf, "%d",
881					   BlankEnvelope.e_hopcount);
882			macdefine(&BlankEnvelope.e_macro, A_TEMP, 'c', buf);
883
884			if (*ep)
885			{
886				usrerr("Bad hop count (%s)", optarg);
887				ExitStat = EX_USAGE;
888			}
889			break;
890
891		  case 'L':	/* program label */
892			/* already set */
893			break;
894
895		  case 'n':	/* don't alias */
896			CHECK_AGAINST_OPMODE(j);
897			NoAlias = true;
898			break;
899
900		  case 'N':	/* delivery status notifications */
901			CHECK_AGAINST_OPMODE(j);
902			DefaultNotify |= QHASNOTIFY;
903			macdefine(&BlankEnvelope.e_macro, A_TEMP,
904				macid("{dsn_notify}"), optarg);
905			if (sm_strcasecmp(optarg, "never") == 0)
906				break;
907			for (p = optarg; p != NULL; optarg = p)
908			{
909				p = strchr(p, ',');
910				if (p != NULL)
911					*p++ = '\0';
912				if (sm_strcasecmp(optarg, "success") == 0)
913					DefaultNotify |= QPINGONSUCCESS;
914				else if (sm_strcasecmp(optarg, "failure") == 0)
915					DefaultNotify |= QPINGONFAILURE;
916				else if (sm_strcasecmp(optarg, "delay") == 0)
917					DefaultNotify |= QPINGONDELAY;
918				else
919				{
920					usrerr("Invalid -N argument");
921					ExitStat = EX_USAGE;
922				}
923			}
924			break;
925
926		  case 'o':	/* set option */
927			setoption(*optarg, optarg + 1, false, true,
928				  &BlankEnvelope);
929			break;
930
931		  case 'O':	/* set option (long form) */
932			setoption(' ', optarg, false, true, &BlankEnvelope);
933			break;
934
935		  case 'p':	/* set protocol */
936			CHECK_AGAINST_OPMODE(j);
937			p = strchr(optarg, ':');
938			if (p != NULL)
939			{
940				*p++ = '\0';
941				if (*p != '\0')
942				{
943					i = strlen(p) + 1;
944					ep = sm_malloc_x(i);
945					cleanstrcpy(ep, p, i);
946					macdefine(&BlankEnvelope.e_macro,
947						  A_HEAP, 's', ep);
948				}
949			}
950			if (*optarg != '\0')
951			{
952				i = strlen(optarg) + 1;
953				ep = sm_malloc_x(i);
954				cleanstrcpy(ep, optarg, i);
955				macdefine(&BlankEnvelope.e_macro, A_HEAP,
956					  'r', ep);
957			}
958			break;
959
960#if _FFR_QUARANTINE
961		  case 'Q':	/* change quarantining on queued items */
962			/* sanity check */
963			if (OpMode != MD_DELIVER &&
964			    OpMode != MD_QUEUERUN)
965			{
966				usrerr("Can not use -Q with -b%c", OpMode);
967				ExitStat = EX_USAGE;
968				break;
969			}
970
971			if (OpMode == MD_DELIVER)
972				set_op_mode(MD_QUEUERUN);
973
974			FullName = NULL;
975
976			quarantining = newstr(optarg);
977			break;
978#endif /* _FFR_QUARANTINE */
979
980		  case 'q':	/* run queue files at intervals */
981			/* sanity check */
982			if (OpMode != MD_DELIVER &&
983			    OpMode != MD_DAEMON &&
984			    OpMode != MD_FGDAEMON &&
985			    OpMode != MD_PRINT &&
986			    OpMode != MD_PRINTNQE &&
987			    OpMode != MD_QUEUERUN)
988			{
989				usrerr("Can not use -q with -b%c", OpMode);
990				ExitStat = EX_USAGE;
991				break;
992			}
993
994			/* don't override -bd, -bD or -bp */
995			if (OpMode == MD_DELIVER)
996				set_op_mode(MD_QUEUERUN);
997
998			FullName = NULL;
999			negate = optarg[0] == '!';
1000			if (negate)
1001			{
1002				/* negate meaning of pattern match */
1003				optarg++; /* skip '!' for next switch */
1004			}
1005
1006			switch (optarg[0])
1007			{
1008			  case 'G': /* Limit by queue group name */
1009				if (negate)
1010				{
1011					usrerr("Can not use -q!G");
1012					ExitStat = EX_USAGE;
1013					break;
1014				}
1015				if (queuegroup != NULL)
1016				{
1017					usrerr("Can not use multiple -qG options");
1018					ExitStat = EX_USAGE;
1019					break;
1020				}
1021				queuegroup = newstr(&optarg[1]);
1022				break;
1023
1024			  case 'I': /* Limit by ID */
1025				new = (QUEUE_CHAR *) xalloc(sizeof *new);
1026				new->queue_match = newstr(&optarg[1]);
1027				new->queue_negate = negate;
1028				new->queue_next = QueueLimitId;
1029				QueueLimitId = new;
1030				break;
1031
1032			  case 'R': /* Limit by recipient */
1033				new = (QUEUE_CHAR *) xalloc(sizeof *new);
1034				new->queue_match = newstr(&optarg[1]);
1035				new->queue_negate = negate;
1036				new->queue_next = QueueLimitRecipient;
1037				QueueLimitRecipient = new;
1038				break;
1039
1040			  case 'S': /* Limit by sender */
1041				new = (QUEUE_CHAR *) xalloc(sizeof *new);
1042				new->queue_match = newstr(&optarg[1]);
1043				new->queue_negate = negate;
1044				new->queue_next = QueueLimitSender;
1045				QueueLimitSender = new;
1046				break;
1047
1048			  case 'f': /* foreground queue run */
1049				foregroundqueue  = true;
1050				break;
1051
1052#if _FFR_QUARANTINE
1053			  case 'Q': /* Limit by quarantine message */
1054				if (optarg[1] != '\0')
1055				{
1056					new = (QUEUE_CHAR *) xalloc(sizeof *new);
1057					new->queue_match = newstr(&optarg[1]);
1058					new->queue_negate = negate;
1059					new->queue_next = QueueLimitQuarantine;
1060					QueueLimitQuarantine = new;
1061				}
1062				QueueMode = QM_QUARANTINE;
1063				break;
1064
1065			  case 'L': /* act on lost items */
1066				QueueMode = QM_LOST;
1067				break;
1068#endif /* _FFR_QUARANTINE */
1069
1070			  case 'p': /* Persistent queue */
1071				queuepersistent = true;
1072				if (QueueIntvl == 0)
1073					QueueIntvl = 1;
1074				if (optarg[1] == '\0')
1075					break;
1076				++optarg;
1077				/* FALLTHROUGH */
1078
1079			  default:
1080				i = Errors;
1081				QueueIntvl = convtime(optarg, 'm');
1082				if (QueueIntvl < 0)
1083				{
1084					usrerr("Invalid -q value");
1085					ExitStat = EX_USAGE;
1086				}
1087
1088				/* check for bad conversion */
1089				if (i < Errors)
1090					ExitStat = EX_USAGE;
1091				break;
1092			}
1093			break;
1094
1095		  case 'R':	/* DSN RET: what to return */
1096			CHECK_AGAINST_OPMODE(j);
1097			if (bitset(EF_RET_PARAM, BlankEnvelope.e_flags))
1098			{
1099				usrerr("Duplicate -R flag");
1100				ExitStat = EX_USAGE;
1101				break;
1102			}
1103			BlankEnvelope.e_flags |= EF_RET_PARAM;
1104			if (sm_strcasecmp(optarg, "hdrs") == 0)
1105				BlankEnvelope.e_flags |= EF_NO_BODY_RETN;
1106			else if (sm_strcasecmp(optarg, "full") != 0)
1107			{
1108				usrerr("Invalid -R value");
1109				ExitStat = EX_USAGE;
1110			}
1111			macdefine(&BlankEnvelope.e_macro, A_TEMP,
1112				  macid("{dsn_ret}"), optarg);
1113			break;
1114
1115		  case 't':	/* read recipients from message */
1116			CHECK_AGAINST_OPMODE(j);
1117			GrabTo = true;
1118			break;
1119
1120		  case 'V':	/* DSN ENVID: set "original" envelope id */
1121			CHECK_AGAINST_OPMODE(j);
1122			if (!xtextok(optarg))
1123			{
1124				usrerr("Invalid syntax in -V flag");
1125				ExitStat = EX_USAGE;
1126			}
1127			else
1128			{
1129				BlankEnvelope.e_envid = newstr(optarg);
1130				macdefine(&BlankEnvelope.e_macro, A_TEMP,
1131					  macid("{dsn_envid}"), optarg);
1132			}
1133			break;
1134
1135		  case 'X':	/* traffic log file */
1136			dp = drop_privileges(true);
1137			setstat(dp);
1138			if (stat(optarg, &traf_st) == 0 &&
1139			    S_ISFIFO(traf_st.st_mode))
1140				TrafficLogFile = sm_io_open(SmFtStdio,
1141							    SM_TIME_DEFAULT,
1142							    optarg,
1143							    SM_IO_WRONLY, NULL);
1144			else
1145				TrafficLogFile = sm_io_open(SmFtStdio,
1146							    SM_TIME_DEFAULT,
1147							    optarg,
1148							    SM_IO_APPEND, NULL);
1149			if (TrafficLogFile == NULL)
1150			{
1151				syserr("cannot open %s", optarg);
1152				ExitStat = EX_CANTCREAT;
1153				break;
1154			}
1155			(void) sm_io_setvbuf(TrafficLogFile, SM_TIME_DEFAULT,
1156					     NULL, SM_IO_LBF, 0);
1157			break;
1158
1159			/* compatibility flags */
1160		  case 'c':	/* connect to non-local mailers */
1161		  case 'i':	/* don't let dot stop me */
1162		  case 'm':	/* send to me too */
1163		  case 'T':	/* set timeout interval */
1164		  case 'v':	/* give blow-by-blow description */
1165			setoption(j, "T", false, true, &BlankEnvelope);
1166			break;
1167
1168		  case 'e':	/* error message disposition */
1169		  case 'M':	/* define macro */
1170			setoption(j, optarg, false, true, &BlankEnvelope);
1171			break;
1172
1173		  case 's':	/* save From lines in headers */
1174			setoption('f', "T", false, true, &BlankEnvelope);
1175			break;
1176
1177#ifdef DBM
1178		  case 'I':	/* initialize alias DBM file */
1179			set_op_mode(MD_INITALIAS);
1180			break;
1181#endif /* DBM */
1182
1183#if defined(__osf__) || defined(_AIX3)
1184		  case 'x':	/* random flag that OSF/1 & AIX mailx passes */
1185			break;
1186#endif /* defined(__osf__) || defined(_AIX3) */
1187#if defined(sony_news)
1188		  case 'E':
1189		  case 'J':	/* ignore flags for Japanese code conversion
1190				   implemented on Sony NEWS */
1191			break;
1192#endif /* defined(sony_news) */
1193
1194		  default:
1195			finis(true, true, EX_USAGE);
1196			/* NOTREACHED */
1197			break;
1198		}
1199	}
1200
1201	/* if we've had errors so far, exit now */
1202	if ((ExitStat != EX_OK && OpMode != MD_TEST) ||
1203	    ExitStat == EX_OSERR)
1204	{
1205		finis(false, true, ExitStat);
1206		/* NOTREACHED */
1207	}
1208
1209	if (bitset(SUBMIT_MTA, SubmitMode))
1210	{
1211		/* If set daemon_flags on command line, don't reset it */
1212		if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
1213			macdefine(&BlankEnvelope.e_macro, A_PERM,
1214				  macid("{daemon_flags}"), "CC f");
1215	}
1216	else if (OpMode == MD_DELIVER || OpMode == MD_SMTP)
1217	{
1218		SubmitMode = SUBMIT_MSA;
1219
1220		/* If set daemon_flags on command line, don't reset it */
1221		if (macvalue(macid("{daemon_flags}"), &BlankEnvelope) == NULL)
1222			macdefine(&BlankEnvelope.e_macro, A_PERM,
1223				  macid("{daemon_flags}"), "c u");
1224	}
1225
1226	/*
1227	**  Do basic initialization.
1228	**	Read system control file.
1229	**	Extract special fields for local use.
1230	*/
1231
1232#if XDEBUG
1233	checkfd012("before readcf");
1234#endif /* XDEBUG */
1235	vendor_pre_defaults(&BlankEnvelope);
1236
1237	readcf(getcfname(OpMode, SubmitMode, cftype, conffile),
1238			 safecf, &BlankEnvelope);
1239#if !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_)
1240	ConfigFileRead = true;
1241#endif /* !defined(_USE_SUN_NSSWITCH_) && !defined(_USE_DEC_SVC_CONF_) */
1242	vendor_post_defaults(&BlankEnvelope);
1243
1244	/* now we can complain about missing fds */
1245	if (MissingFds != 0 && LogLevel > 8)
1246	{
1247		char mbuf[MAXLINE];
1248
1249		mbuf[0] = '\0';
1250		if (bitset(1 << STDIN_FILENO, MissingFds))
1251			(void) sm_strlcat(mbuf, ", stdin", sizeof mbuf);
1252		if (bitset(1 << STDOUT_FILENO, MissingFds))
1253			(void) sm_strlcat(mbuf, ", stdout", sizeof mbuf);
1254		if (bitset(1 << STDERR_FILENO, MissingFds))
1255			(void) sm_strlcat(mbuf, ", stderr", sizeof mbuf);
1256
1257		/* Notice: fill_errno is from high above: fill_fd() */
1258		sm_syslog(LOG_WARNING, NOQID,
1259			  "File descriptors missing on startup: %s; %s",
1260			  &mbuf[2], sm_errstring(fill_errno));
1261	}
1262
1263	/* Remove the ability for a normal user to send signals */
1264	if (RealUid != 0 && RealUid != geteuid())
1265	{
1266		uid_t new_uid = geteuid();
1267
1268#if HASSETREUID
1269		/*
1270		**  Since we can differentiate between uid and euid,
1271		**  make the uid a different user so the real user
1272		**  can't send signals.  However, it doesn't need to be
1273		**  root (euid has root).
1274		*/
1275
1276		if (new_uid == 0)
1277			new_uid = DefUid;
1278		if (tTd(47, 5))
1279			sm_dprintf("Changing real uid to %d\n", (int) new_uid);
1280		if (setreuid(new_uid, geteuid()) < 0)
1281		{
1282			syserr("main: setreuid(%d, %d) failed",
1283			       (int) new_uid, (int) geteuid());
1284			finis(false, true, EX_OSERR);
1285			/* NOTREACHED */
1286		}
1287		if (tTd(47, 10))
1288			sm_dprintf("Now running as e/ruid %d:%d\n",
1289				   (int) geteuid(), (int) getuid());
1290#else /* HASSETREUID */
1291		/*
1292		**  Have to change both effective and real so need to
1293		**  change them both to effective to keep privs.
1294		*/
1295
1296		if (tTd(47, 5))
1297			sm_dprintf("Changing uid to %d\n", (int) new_uid);
1298		if (setuid(new_uid) < 0)
1299		{
1300			syserr("main: setuid(%d) failed", (int) new_uid);
1301			finis(false, true, EX_OSERR);
1302			/* NOTREACHED */
1303		}
1304		if (tTd(47, 10))
1305			sm_dprintf("Now running as e/ruid %d:%d\n",
1306				   (int) geteuid(), (int) getuid());
1307#endif /* HASSETREUID */
1308	}
1309
1310#if NAMED_BIND
1311	if (FallBackMX != NULL)
1312		(void) getfallbackmxrr(FallBackMX);
1313#endif /* NAMED_BIND */
1314
1315	if (SuperSafe == SAFE_INTERACTIVE && CurEnv->e_sendmode != SM_DELIVER)
1316	{
1317		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1318				     "WARNING: SuperSafe=interactive should only be used with\n         DeliveryMode=interactive\n");
1319	}
1320
1321	if (UseMSP && (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON))
1322	{
1323		usrerr("Mail submission program cannot be used as daemon");
1324		finis(false, true, EX_USAGE);
1325	}
1326
1327	if (OpMode == MD_DELIVER || OpMode == MD_SMTP ||
1328	    OpMode == MD_QUEUERUN || OpMode == MD_ARPAFTP ||
1329	    OpMode == MD_DAEMON || OpMode == MD_FGDAEMON)
1330		makeworkgroups();
1331
1332	/* set up the basic signal handlers */
1333	if (sm_signal(SIGINT, SIG_IGN) != SIG_IGN)
1334		(void) sm_signal(SIGINT, intsig);
1335	(void) sm_signal(SIGTERM, intsig);
1336
1337	/* Enforce use of local time (null string overrides this) */
1338	if (TimeZoneSpec == NULL)
1339		unsetenv("TZ");
1340	else if (TimeZoneSpec[0] != '\0')
1341		setuserenv("TZ", TimeZoneSpec);
1342	else
1343		setuserenv("TZ", NULL);
1344	tzset();
1345
1346	/* initialize mailbox database */
1347	i = sm_mbdb_initialize(Mbdb);
1348	if (i != EX_OK)
1349	{
1350		usrerr("Can't initialize mailbox database \"%s\": %s",
1351		       Mbdb, sm_strexit(i));
1352		ExitStat = i;
1353	}
1354
1355	/* avoid denial-of-service attacks */
1356	resetlimits();
1357
1358	if (OpMode == MD_TEST)
1359	{
1360		/* can't be done after readcf if RunAs* is used */
1361		dp = drop_privileges(true);
1362		if (dp != EX_OK)
1363		{
1364			finis(false, true, dp);
1365			/* NOTREACHED */
1366		}
1367	}
1368	else if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
1369	{
1370		/* drop privileges -- daemon mode done after socket/bind */
1371		dp = drop_privileges(false);
1372		setstat(dp);
1373		if (dp == EX_OK && UseMSP && (geteuid() == 0 || getuid() == 0))
1374		{
1375			usrerr("Mail submission program must have RunAsUser set to non root user");
1376			finis(false, true, EX_CONFIG);
1377			/* NOTREACHED */
1378		}
1379	}
1380
1381#if NAMED_BIND
1382	_res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
1383	_res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
1384#endif /* NAMED_BIND */
1385
1386	/*
1387	**  Find our real host name for future logging.
1388	*/
1389
1390	authinfo = getauthinfo(STDIN_FILENO, &forged);
1391	macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
1392
1393	/* suppress error printing if errors mailed back or whatever */
1394	if (BlankEnvelope.e_errormode != EM_PRINT)
1395		HoldErrs = true;
1396
1397	/* set up the $=m class now, after .cf has a chance to redefine $m */
1398	expand("\201m", jbuf, sizeof jbuf, &BlankEnvelope);
1399	if (jbuf[0] != '\0')
1400		setclass('m', jbuf);
1401
1402	/* probe interfaces and locate any additional names */
1403	if (DontProbeInterfaces != DPI_PROBENONE)
1404		load_if_names();
1405
1406	if (tTd(0, 10))
1407	{
1408		char pidpath[MAXPATHLEN];
1409
1410		/* Now we know which .cf file we use */
1411		sm_dprintf("     Conf file:\t%s (selected)\n",
1412			   getcfname(OpMode, SubmitMode, cftype, conffile));
1413		expand(PidFile, pidpath, sizeof pidpath, &BlankEnvelope);
1414		sm_dprintf("      Pid file:\t%s (selected)\n", pidpath);
1415	}
1416
1417	if (tTd(0, 1))
1418	{
1419		sm_dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
1420		sm_dprintf("\n      (short domain name) $w = ");
1421		xputs(macvalue('w', &BlankEnvelope));
1422		sm_dprintf("\n  (canonical domain name) $j = ");
1423		xputs(macvalue('j', &BlankEnvelope));
1424		sm_dprintf("\n         (subdomain name) $m = ");
1425		xputs(macvalue('m', &BlankEnvelope));
1426		sm_dprintf("\n              (node name) $k = ");
1427		xputs(macvalue('k', &BlankEnvelope));
1428		sm_dprintf("\n========================================================\n\n");
1429	}
1430
1431	/*
1432	**  Do more command line checking -- these are things that
1433	**  have to modify the results of reading the config file.
1434	*/
1435
1436	/* process authorization warnings from command line */
1437	if (warn_C_flag)
1438		auth_warning(&BlankEnvelope, "Processed by %s with -C %s",
1439			     RealUserName, conffile);
1440	if (Warn_Q_option && !wordinclass(RealUserName, 't'))
1441		auth_warning(&BlankEnvelope, "Processed from queue %s",
1442			     QueueDir);
1443	if (sysloglabel != NULL && !wordinclass(RealUserName, 't') &&
1444	    RealUid != 0 && RealUid != TrustedUid && LogLevel > 1)
1445		sm_syslog(LOG_WARNING, NOQID, "user %d changed syslog label",
1446			  (int) RealUid);
1447
1448	/* check body type for legality */
1449	i = check_bodytype(BlankEnvelope.e_bodytype);
1450	if (i == BODYTYPE_ILLEGAL)
1451	{
1452		usrerr("Illegal body type %s", BlankEnvelope.e_bodytype);
1453		BlankEnvelope.e_bodytype = NULL;
1454	}
1455	else if (i != BODYTYPE_NONE)
1456		SevenBitInput = (i == BODYTYPE_7BIT);
1457
1458	/* tweak default DSN notifications */
1459	if (DefaultNotify == 0)
1460		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1461
1462	/* be sure we don't pick up bogus HOSTALIASES environment variable */
1463	if (OpMode == MD_QUEUERUN && RealUid != 0)
1464		(void) unsetenv("HOSTALIASES");
1465
1466	/* check for sane configuration level */
1467	if (ConfigLevel > MAXCONFIGLEVEL)
1468	{
1469		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
1470		       ConfigLevel, Version, MAXCONFIGLEVEL);
1471	}
1472
1473	/* need MCI cache to have persistence */
1474	if (HostStatDir != NULL && MaxMciCache == 0)
1475	{
1476		HostStatDir = NULL;
1477		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1478				     "Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
1479	}
1480
1481	/* need HostStatusDir in order to have SingleThreadDelivery */
1482	if (SingleThreadDelivery && HostStatDir == NULL)
1483	{
1484		SingleThreadDelivery = false;
1485		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1486				     "Warning: HostStatusDirectory required for SingleThreadDelivery\n");
1487	}
1488
1489	/* check for permissions */
1490	if (RealUid != 0 &&
1491	    RealUid != TrustedUid)
1492	{
1493		char *action = NULL;
1494
1495		switch (OpMode)
1496		{
1497		  case MD_QUEUERUN:
1498#if _FFR_QUARANTINE
1499			if (quarantining != NULL)
1500				action = "quarantine jobs";
1501			else
1502#endif /* _FFR_QUARANTINE */
1503			/* Normal users can do a single queue run */
1504			if (QueueIntvl == 0)
1505				break;
1506
1507			/* but not persistent queue runners */
1508			if (action == NULL)
1509				action = "start a queue runner daemon";
1510			/* FALLTHROUGH */
1511
1512		  case MD_PURGESTAT:
1513			if (action == NULL)
1514				action = "purge host status";
1515			/* FALLTHROUGH */
1516
1517		  case MD_DAEMON:
1518		  case MD_FGDAEMON:
1519			if (action == NULL)
1520				action = "run daemon";
1521
1522			if (tTd(65, 1))
1523				sm_dprintf("Deny user %d attempt to %s\n",
1524					   (int) RealUid, action);
1525
1526			if (LogLevel > 1)
1527				sm_syslog(LOG_ALERT, NOQID,
1528					  "user %d attempted to %s",
1529					  (int) RealUid, action);
1530			HoldErrs = false;
1531			usrerr("Permission denied (real uid not trusted)");
1532			finis(false, true, EX_USAGE);
1533			/* NOTREACHED */
1534			break;
1535
1536		  case MD_VERIFY:
1537			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags))
1538			{
1539				/*
1540				**  If -bv and RestrictExpand,
1541				**  drop privs to prevent normal
1542				**  users from reading private
1543				**  aliases/forwards/:include:s
1544				*/
1545
1546				if (tTd(65, 1))
1547					sm_dprintf("Drop privs for user %d attempt to expand (RestrictExpand)\n",
1548						   (int) RealUid);
1549
1550				dp = drop_privileges(true);
1551
1552				/* Fake address safety */
1553				if (tTd(65, 1))
1554					sm_dprintf("Faking DontBlameSendmail=NonRootSafeAddr\n");
1555				setbitn(DBS_NONROOTSAFEADDR, DontBlameSendmail);
1556
1557				if (dp != EX_OK)
1558				{
1559					if (tTd(65, 1))
1560						sm_dprintf("Failed to drop privs for user %d attempt to expand, exiting\n",
1561							   (int) RealUid);
1562					CurEnv->e_id = NULL;
1563					finis(true, true, dp);
1564					/* NOTREACHED */
1565				}
1566			}
1567			break;
1568
1569		  case MD_TEST:
1570		  case MD_PRINT:
1571		  case MD_PRINTNQE:
1572		  case MD_FREEZE:
1573		  case MD_HOSTSTAT:
1574			/* Nothing special to check */
1575			break;
1576
1577		  case MD_INITALIAS:
1578			if (!wordinclass(RealUserName, 't'))
1579			{
1580				if (tTd(65, 1))
1581					sm_dprintf("Deny user %d attempt to rebuild the alias map\n",
1582						   (int) RealUid);
1583				if (LogLevel > 1)
1584					sm_syslog(LOG_ALERT, NOQID,
1585						  "user %d attempted to rebuild the alias map",
1586						  (int) RealUid);
1587				HoldErrs = false;
1588				usrerr("Permission denied (real uid not trusted)");
1589				finis(false, true, EX_USAGE);
1590				/* NOTREACHED */
1591			}
1592			if (UseMSP)
1593			{
1594				HoldErrs = false;
1595				usrerr("User %d cannot rebuild aliases in mail submission program",
1596				       (int) RealUid);
1597				finis(false, true, EX_USAGE);
1598				/* NOTREACHED */
1599			}
1600			/* FALLTHROUGH */
1601
1602		  default:
1603			if (bitset(PRIV_RESTRICTEXPAND, PrivacyFlags) &&
1604			    Verbose != 0)
1605			{
1606				/*
1607				**  If -v and RestrictExpand, reset
1608				**  Verbose to prevent normal users
1609				**  from seeing the expansion of
1610				**  aliases/forwards/:include:s
1611				*/
1612
1613				if (tTd(65, 1))
1614					sm_dprintf("Dropping verbosity for user %d (RestrictExpand)\n",
1615						   (int) RealUid);
1616				Verbose = 0;
1617			}
1618			break;
1619		}
1620	}
1621
1622	if (MeToo)
1623		BlankEnvelope.e_flags |= EF_METOO;
1624
1625	switch (OpMode)
1626	{
1627	  case MD_TEST:
1628		/* don't have persistent host status in test mode */
1629		HostStatDir = NULL;
1630		if (Verbose == 0)
1631			Verbose = 2;
1632		BlankEnvelope.e_errormode = EM_PRINT;
1633		HoldErrs = false;
1634		break;
1635
1636	  case MD_VERIFY:
1637		BlankEnvelope.e_errormode = EM_PRINT;
1638		HoldErrs = false;
1639		/* arrange to exit cleanly on hangup signal */
1640		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1641			(void) sm_signal(SIGHUP, intsig);
1642		if (geteuid() != 0)
1643			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1644					     "Notice: -bv may give misleading output for non-privileged user\n");
1645		break;
1646
1647	  case MD_FGDAEMON:
1648		run_in_foreground = true;
1649		set_op_mode(MD_DAEMON);
1650		/* FALLTHROUGH */
1651
1652	  case MD_DAEMON:
1653		vendor_daemon_setup(&BlankEnvelope);
1654
1655		/* remove things that don't make sense in daemon mode */
1656		FullName = NULL;
1657		GrabTo = false;
1658
1659		/* arrange to restart on hangup signal */
1660		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
1661			sm_syslog(LOG_WARNING, NOQID,
1662				  "daemon invoked without full pathname; kill -1 won't work");
1663		break;
1664
1665	  case MD_INITALIAS:
1666		Verbose = 2;
1667		BlankEnvelope.e_errormode = EM_PRINT;
1668		HoldErrs = false;
1669		/* FALLTHROUGH */
1670
1671	  default:
1672		/* arrange to exit cleanly on hangup signal */
1673		if (sm_signal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1674			(void) sm_signal(SIGHUP, intsig);
1675		break;
1676	}
1677
1678	/* special considerations for FullName */
1679	if (FullName != NULL)
1680	{
1681		char *full = NULL;
1682
1683		/* full names can't have newlines */
1684		if (strchr(FullName, '\n') != NULL)
1685		{
1686			full = newstr(denlstring(FullName, true, true));
1687			FullName = full;
1688		}
1689
1690		/* check for characters that may have to be quoted */
1691		if (!rfc822_string(FullName))
1692		{
1693			/*
1694			**  Quote a full name with special characters
1695			**  as a comment so crackaddr() doesn't destroy
1696			**  the name portion of the address.
1697			*/
1698
1699			FullName = addquotes(FullName, NULL);
1700			if (full != NULL)
1701				sm_free(full);  /* XXX */
1702		}
1703	}
1704
1705	/* do heuristic mode adjustment */
1706	if (Verbose)
1707	{
1708		/* turn off noconnect option */
1709		setoption('c', "F", true, false, &BlankEnvelope);
1710
1711		/* turn on interactive delivery */
1712		setoption('d', "", true, false, &BlankEnvelope);
1713	}
1714
1715#ifdef VENDOR_CODE
1716	/* check for vendor mismatch */
1717	if (VendorCode != VENDOR_CODE)
1718	{
1719		message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
1720			getvendor(VENDOR_CODE), getvendor(VendorCode));
1721	}
1722#endif /* VENDOR_CODE */
1723
1724	/* check for out of date configuration level */
1725	if (ConfigLevel < MAXCONFIGLEVEL)
1726	{
1727		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
1728			Version, MAXCONFIGLEVEL, ConfigLevel);
1729	}
1730
1731	if (ConfigLevel < 3)
1732		UseErrorsTo = true;
1733
1734	/* set options that were previous macros */
1735	if (SmtpGreeting == NULL)
1736	{
1737		if (ConfigLevel < 7 &&
1738		    (p = macvalue('e', &BlankEnvelope)) != NULL)
1739			SmtpGreeting = newstr(p);
1740		else
1741			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
1742	}
1743	if (UnixFromLine == NULL)
1744	{
1745		if (ConfigLevel < 7 &&
1746		    (p = macvalue('l', &BlankEnvelope)) != NULL)
1747			UnixFromLine = newstr(p);
1748		else
1749			UnixFromLine = "From \201g  \201d";
1750	}
1751	SmtpError[0] = '\0';
1752
1753	/* our name for SMTP codes */
1754	expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope);
1755	if (jbuf[0] == '\0')
1756		PSTRSET(MyHostName, "localhost");
1757	else
1758		PSTRSET(MyHostName, jbuf);
1759	if (strchr(MyHostName, '.') == NULL)
1760		message("WARNING: local host name (%s) is not qualified; fix $j in config file",
1761			MyHostName);
1762
1763	/* make certain that this name is part of the $=w class */
1764	setclass('w', MyHostName);
1765
1766	/* fill in the structure of the *default* queue */
1767	st = stab("mqueue", ST_QUEUE, ST_FIND);
1768	if (st == NULL)
1769		syserr("No default queue (mqueue) defined");
1770	else
1771		set_def_queueval(st->s_quegrp, true);
1772
1773	/* the indices of built-in mailers */
1774	st = stab("local", ST_MAILER, ST_FIND);
1775	if (st != NULL)
1776		LocalMailer = st->s_mailer;
1777	else if (OpMode != MD_TEST || !warn_C_flag)
1778		syserr("No local mailer defined");
1779
1780	st = stab("prog", ST_MAILER, ST_FIND);
1781	if (st == NULL)
1782		syserr("No prog mailer defined");
1783	else
1784	{
1785		ProgMailer = st->s_mailer;
1786		clrbitn(M_MUSER, ProgMailer->m_flags);
1787	}
1788
1789	st = stab("*file*", ST_MAILER, ST_FIND);
1790	if (st == NULL)
1791		syserr("No *file* mailer defined");
1792	else
1793	{
1794		FileMailer = st->s_mailer;
1795		clrbitn(M_MUSER, FileMailer->m_flags);
1796	}
1797
1798	st = stab("*include*", ST_MAILER, ST_FIND);
1799	if (st == NULL)
1800		syserr("No *include* mailer defined");
1801	else
1802		InclMailer = st->s_mailer;
1803
1804	if (ConfigLevel < 6)
1805	{
1806		/* heuristic tweaking of local mailer for back compat */
1807		if (LocalMailer != NULL)
1808		{
1809			setbitn(M_ALIASABLE, LocalMailer->m_flags);
1810			setbitn(M_HASPWENT, LocalMailer->m_flags);
1811			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
1812			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
1813			setbitn(M_CHECKPROG, LocalMailer->m_flags);
1814			setbitn(M_CHECKFILE, LocalMailer->m_flags);
1815			setbitn(M_CHECKUDB, LocalMailer->m_flags);
1816		}
1817		if (ProgMailer != NULL)
1818			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
1819		if (FileMailer != NULL)
1820			setbitn(M_RUNASRCPT, FileMailer->m_flags);
1821	}
1822	if (ConfigLevel < 7)
1823	{
1824		if (LocalMailer != NULL)
1825			setbitn(M_VRFY250, LocalMailer->m_flags);
1826		if (ProgMailer != NULL)
1827			setbitn(M_VRFY250, ProgMailer->m_flags);
1828		if (FileMailer != NULL)
1829			setbitn(M_VRFY250, FileMailer->m_flags);
1830	}
1831
1832	/* MIME Content-Types that cannot be transfer encoded */
1833	setclass('n', "multipart/signed");
1834
1835	/* MIME message/xxx subtypes that can be treated as messages */
1836	setclass('s', "rfc822");
1837
1838	/* MIME Content-Transfer-Encodings that can be encoded */
1839	setclass('e', "7bit");
1840	setclass('e', "8bit");
1841	setclass('e', "binary");
1842
1843#ifdef USE_B_CLASS
1844	/* MIME Content-Types that should be treated as binary */
1845	setclass('b', "image");
1846	setclass('b', "audio");
1847	setclass('b', "video");
1848	setclass('b', "application/octet-stream");
1849#endif /* USE_B_CLASS */
1850
1851	/* MIME headers which have fields to check for overflow */
1852	setclass(macid("{checkMIMEFieldHeaders}"), "content-disposition");
1853	setclass(macid("{checkMIMEFieldHeaders}"), "content-type");
1854
1855	/* MIME headers to check for length overflow */
1856	setclass(macid("{checkMIMETextHeaders}"), "content-description");
1857
1858	/* MIME headers to check for overflow and rebalance */
1859	setclass(macid("{checkMIMEHeaders}"), "content-disposition");
1860	setclass(macid("{checkMIMEHeaders}"), "content-id");
1861	setclass(macid("{checkMIMEHeaders}"), "content-transfer-encoding");
1862	setclass(macid("{checkMIMEHeaders}"), "content-type");
1863	setclass(macid("{checkMIMEHeaders}"), "mime-version");
1864
1865	/* Macros to save in the queue file -- don't remove any */
1866	setclass(macid("{persistentMacros}"), "r");
1867	setclass(macid("{persistentMacros}"), "s");
1868	setclass(macid("{persistentMacros}"), "_");
1869	setclass(macid("{persistentMacros}"), "{if_addr}");
1870	setclass(macid("{persistentMacros}"), "{daemon_flags}");
1871
1872	/* operate in queue directory */
1873	if (QueueDir == NULL || *QueueDir == '\0')
1874	{
1875		if (OpMode != MD_TEST)
1876		{
1877			syserr("QueueDirectory (Q) option must be set");
1878			ExitStat = EX_CONFIG;
1879		}
1880	}
1881	else
1882	{
1883		if (OpMode != MD_TEST)
1884			setup_queues(OpMode == MD_DAEMON);
1885	}
1886
1887	/* check host status directory for validity */
1888	if (HostStatDir != NULL && !path_is_dir(HostStatDir, false))
1889	{
1890		/* cannot use this value */
1891		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1892				     "Warning: Cannot use HostStatusDirectory = %s: %s\n",
1893				     HostStatDir, sm_errstring(errno));
1894		HostStatDir = NULL;
1895	}
1896
1897	if (OpMode == MD_QUEUERUN &&
1898	    RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
1899	{
1900		struct stat stbuf;
1901
1902		/* check to see if we own the queue directory */
1903		if (stat(".", &stbuf) < 0)
1904			syserr("main: cannot stat %s", QueueDir);
1905		if (stbuf.st_uid != RealUid)
1906		{
1907			/* nope, really a botch */
1908			HoldErrs = false;
1909			usrerr("You do not have permission to process the queue");
1910			finis(false, true, EX_NOPERM);
1911			/* NOTREACHED */
1912		}
1913	}
1914
1915#if MILTER
1916	/* sanity checks on milter filters */
1917	if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
1918	{
1919		milter_config(InputFilterList, InputFilters, MAXFILTERS);
1920# if _FFR_MILTER_PERDAEMON
1921		setup_daemon_milters();
1922# endif /* _FFR_MILTER_PERDAEMON */
1923	}
1924#endif /* MILTER */
1925
1926	/* Convert queuegroup string to qgrp number */
1927	if (queuegroup != NULL)
1928	{
1929		qgrp = name2qid(queuegroup);
1930		if (qgrp == NOQGRP)
1931		{
1932			HoldErrs = false;
1933			usrerr("Queue group %s unknown", queuegroup);
1934			finis(false, true, ExitStat);
1935			/* NOTREACHED */
1936		}
1937	}
1938
1939	/* if we've had errors so far, exit now */
1940	if (ExitStat != EX_OK && OpMode != MD_TEST)
1941	{
1942		finis(false, true, ExitStat);
1943		/* NOTREACHED */
1944	}
1945
1946#if SASL
1947	/* sendmail specific SASL initialization */
1948	sm_sasl_init();
1949#endif /* SASL */
1950
1951#if XDEBUG
1952	checkfd012("before main() initmaps");
1953#endif /* XDEBUG */
1954
1955	/*
1956	**  Do operation-mode-dependent initialization.
1957	*/
1958
1959	switch (OpMode)
1960	{
1961	  case MD_PRINT:
1962		/* print the queue */
1963		HoldErrs = false;
1964		dropenvelope(&BlankEnvelope, true, false);
1965		(void) sm_signal(SIGPIPE, sigpipe);
1966		if (qgrp != NOQGRP)
1967		{
1968			int j;
1969
1970			/* Selecting a particular queue group to run */
1971			for (j = 0; j < Queue[qgrp]->qg_numqueues; j++)
1972			{
1973				if (StopRequest)
1974					stop_sendmail();
1975				(void) print_single_queue(qgrp, j);
1976			}
1977			finis(false, true, EX_OK);
1978			/* NOTREACHED */
1979		}
1980		printqueue();
1981		finis(false, true, EX_OK);
1982		/* NOTREACHED */
1983		break;
1984
1985	  case MD_PRINTNQE:
1986		/* print number of entries in queue */
1987		dropenvelope(&BlankEnvelope, true, false);
1988		(void) sm_signal(SIGPIPE, sigpipe);
1989		printnqe(smioout, NULL);
1990		finis(false, true, EX_OK);
1991		/* NOTREACHED */
1992		break;
1993
1994#if _FFR_QUARANTINE
1995	  case MD_QUEUERUN:
1996		/* only handle quarantining here */
1997		if (quarantining == NULL)
1998			break;
1999
2000		if (QueueMode != QM_QUARANTINE &&
2001		    QueueMode != QM_NORMAL)
2002		{
2003			HoldErrs = false;
2004			usrerr("Can not use -Q with -q%c", QueueMode);
2005			ExitStat = EX_USAGE;
2006			finis(false, true, ExitStat);
2007			/* NOTREACHED */
2008		}
2009		quarantine_queue(quarantining, qgrp);
2010		finis(false, true, EX_OK);
2011		break;
2012#endif /* _FFR_QUARANTINE */
2013
2014	  case MD_HOSTSTAT:
2015		(void) sm_signal(SIGPIPE, sigpipe);
2016		(void) mci_traverse_persistent(mci_print_persistent, NULL);
2017		finis(false, true, EX_OK);
2018		/* NOTREACHED */
2019		break;
2020
2021	  case MD_PURGESTAT:
2022		(void) mci_traverse_persistent(mci_purge_persistent, NULL);
2023		finis(false, true, EX_OK);
2024		/* NOTREACHED */
2025		break;
2026
2027	  case MD_INITALIAS:
2028		/* initialize maps */
2029		initmaps();
2030		finis(false, true, ExitStat);
2031		/* NOTREACHED */
2032		break;
2033
2034	  case MD_SMTP:
2035	  case MD_DAEMON:
2036		/* reset DSN parameters */
2037		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
2038		macdefine(&BlankEnvelope.e_macro, A_PERM,
2039			  macid("{dsn_notify}"), NULL);
2040		BlankEnvelope.e_envid = NULL;
2041		macdefine(&BlankEnvelope.e_macro, A_PERM,
2042			  macid("{dsn_envid}"), NULL);
2043		BlankEnvelope.e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
2044		macdefine(&BlankEnvelope.e_macro, A_PERM,
2045			  macid("{dsn_ret}"), NULL);
2046
2047		/* don't open maps for daemon -- done below in child */
2048		break;
2049	}
2050
2051	if (tTd(0, 15))
2052	{
2053		/* print configuration table (or at least part of it) */
2054		if (tTd(0, 90))
2055			printrules();
2056		for (i = 0; i < MAXMAILERS; i++)
2057		{
2058			if (Mailer[i] != NULL)
2059				printmailer(Mailer[i]);
2060		}
2061	}
2062
2063	/*
2064	**  Switch to the main envelope.
2065	*/
2066
2067	CurEnv = newenvelope(&MainEnvelope, &BlankEnvelope,
2068			     sm_rpool_new_x(NULL));
2069	MainEnvelope.e_flags = BlankEnvelope.e_flags;
2070
2071	/*
2072	**  If test mode, read addresses from stdin and process.
2073	*/
2074
2075	if (OpMode == MD_TEST)
2076	{
2077		if (isatty(sm_io_getinfo(smioin, SM_IO_WHAT_FD, NULL)))
2078			Verbose = 2;
2079
2080		if (Verbose)
2081		{
2082			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2083				     "ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
2084			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2085				     "Enter <ruleset> <address>\n");
2086		}
2087		macdefine(&(MainEnvelope.e_macro), A_PERM,
2088			  macid("{addr_type}"), "e r");
2089		for (;;)
2090		{
2091			SM_TRY
2092			{
2093				(void) sm_signal(SIGINT, intindebug);
2094				(void) sm_releasesignal(SIGINT);
2095				if (Verbose == 2)
2096					(void) sm_io_fprintf(smioout,
2097							     SM_TIME_DEFAULT,
2098							     "> ");
2099				(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
2100				if (sm_io_fgets(smioin, SM_TIME_DEFAULT, buf,
2101						sizeof buf) == NULL)
2102					testmodeline("/quit", &MainEnvelope);
2103				p = strchr(buf, '\n');
2104				if (p != NULL)
2105					*p = '\0';
2106				if (Verbose < 2)
2107					(void) sm_io_fprintf(smioout,
2108							     SM_TIME_DEFAULT,
2109							     "> %s\n", buf);
2110				testmodeline(buf, &MainEnvelope);
2111			}
2112			SM_EXCEPT(exc, "[!F]*")
2113			{
2114				/*
2115				**  8.10 just prints \n on interrupt.
2116				**  I'm printing the exception here in case
2117				**  sendmail is extended to raise additional
2118				**  exceptions in this context.
2119				*/
2120
2121				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2122						     "\n");
2123				sm_exc_print(exc, smioout);
2124			}
2125			SM_END_TRY
2126		}
2127	}
2128
2129#if STARTTLS
2130	tls_ok = true;
2131	if (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER)
2132	{
2133		/* check whether STARTTLS is turned off for the client */
2134		if (chkclientmodifiers(D_NOTLS))
2135			tls_ok = false;
2136	}
2137	else if (OpMode == MD_DAEMON || OpMode == MD_FGDAEMON ||
2138		 OpMode == MD_SMTP)
2139	{
2140		/* check whether STARTTLS is turned off for the server */
2141		if (chkdaemonmodifiers(D_NOTLS))
2142			tls_ok = false;
2143	}
2144	else	/* other modes don't need STARTTLS */
2145		tls_ok = false;
2146
2147	if (tls_ok)
2148	{
2149		/* basic TLS initialization */
2150		tls_ok = init_tls_library();
2151	}
2152
2153	if (!tls_ok && (OpMode == MD_QUEUERUN || OpMode == MD_DELIVER))
2154	{
2155		/* disable TLS for client */
2156		setclttls(false);
2157	}
2158#endif /* STARTTLS */
2159
2160	/*
2161	**  If collecting stuff from the queue, go start doing that.
2162	*/
2163
2164	if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
2165	{
2166		pid_t pid = -1;
2167
2168#if STARTTLS
2169		/* init TLS for client, ignore result for now */
2170		(void) initclttls(tls_ok);
2171#endif /* STARTTLS */
2172
2173		/*
2174		**  The parent process of the caller of runqueue() needs
2175		**  to stay around for a possible SIGTERM. The SIGTERM will
2176		**  tell this process that all of the queue runners children
2177		**  need to be sent SIGTERM as well. At the same time, we
2178		**  want to return control to the command line. So we do an
2179		**  extra fork().
2180		*/
2181
2182		if (Verbose || foregroundqueue || (pid = fork()) <= 0)
2183		{
2184			/*
2185			**  If the fork() failed we should still try to do
2186			**  the queue run. If it succeeded then the child
2187			**  is going to start the run and wait for all
2188			**  of the children to finish.
2189			*/
2190
2191			if (pid == 0)
2192			{
2193				/* Reset global flags */
2194				RestartRequest = NULL;
2195				ShutdownRequest = NULL;
2196				PendingSignal = 0;
2197
2198				/* disconnect from terminal */
2199				disconnect(2, CurEnv);
2200			}
2201
2202			CurrentPid = getpid();
2203			if (qgrp != NOQGRP)
2204			{
2205				int rwgflags = RWG_NONE;
2206
2207				/*
2208				**  To run a specific queue group mark it to
2209				**  be run, select the work group it's in and
2210				**  increment the work counter.
2211				*/
2212
2213				for (i = 0; i < NumQueue && Queue[i] != NULL;
2214				     i++)
2215					Queue[i]->qg_nextrun = (time_t) -1;
2216				Queue[qgrp]->qg_nextrun = 0;
2217				if (Verbose)
2218					rwgflags |= RWG_VERBOSE;
2219				if (queuepersistent)
2220					rwgflags |= RWG_PERSISTENT;
2221				rwgflags |= RWG_FORCE;
2222				(void) run_work_group(Queue[qgrp]->qg_wgrp,
2223						      rwgflags);
2224			}
2225			else
2226				(void) runqueue(false, Verbose,
2227						queuepersistent, true);
2228
2229			/* set the title to make it easier to find */
2230			sm_setproctitle(true, CurEnv, "Queue control");
2231			(void) sm_signal(SIGCHLD, SIG_DFL);
2232			while (CurChildren > 0)
2233			{
2234				int status;
2235				pid_t ret;
2236
2237				while ((ret = sm_wait(&status)) <= 0)
2238					continue;
2239
2240				/* Only drop when a child gives status */
2241				if (WIFSTOPPED(status))
2242					continue;
2243
2244				proc_list_drop(ret, status, NULL);
2245			}
2246		}
2247		finis(true, true, ExitStat);
2248		/* NOTREACHED */
2249	}
2250
2251# if SASL
2252	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
2253	{
2254		/* check whether AUTH is turned off for the server */
2255		if (!chkdaemonmodifiers(D_NOAUTH) &&
2256		    (i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
2257			syserr("!sasl_server_init failed! [%s]",
2258				sasl_errstring(i, NULL, NULL));
2259	}
2260# endif /* SASL */
2261
2262	if (OpMode == MD_SMTP)
2263	{
2264		proc_list_add(CurrentPid, "Sendmail SMTP Agent",
2265			      PROC_DAEMON, 0, -1);
2266
2267		/* clean up background delivery children */
2268		(void) sm_signal(SIGCHLD, reapchild);
2269	}
2270
2271	/*
2272	**  If a daemon, wait for a request.
2273	**	getrequests will always return in a child.
2274	**	If we should also be processing the queue, start
2275	**		doing it in background.
2276	**	We check for any errors that might have happened
2277	**		during startup.
2278	*/
2279
2280	if (OpMode == MD_DAEMON || QueueIntvl > 0)
2281	{
2282		char dtype[200];
2283
2284		if (!run_in_foreground && !tTd(99, 100))
2285		{
2286			/* put us in background */
2287			i = fork();
2288			if (i < 0)
2289				syserr("daemon: cannot fork");
2290			if (i != 0)
2291			{
2292				finis(false, true, EX_OK);
2293				/* NOTREACHED */
2294			}
2295
2296			/*
2297			**  Initialize exception stack and default exception
2298			**  handler for child process.
2299			*/
2300
2301			/* Reset global flags */
2302			RestartRequest = NULL;
2303			RestartWorkGroup = false;
2304			ShutdownRequest = NULL;
2305			PendingSignal = 0;
2306			CurrentPid = getpid();
2307
2308			sm_exc_newthread(fatal_error);
2309
2310			/* disconnect from our controlling tty */
2311			disconnect(2, &MainEnvelope);
2312		}
2313
2314		dtype[0] = '\0';
2315		if (OpMode == MD_DAEMON)
2316		{
2317			(void) sm_strlcat(dtype, "+SMTP", sizeof dtype);
2318			DaemonPid = CurrentPid;
2319		}
2320		if (QueueIntvl > 0)
2321		{
2322			(void) sm_strlcat2(dtype,
2323					   queuepersistent
2324					   ? "+persistent-queueing@"
2325					   : "+queueing@",
2326					   pintvl(QueueIntvl, true),
2327					   sizeof dtype);
2328		}
2329		if (tTd(0, 1))
2330			(void) sm_strlcat(dtype, "+debugging", sizeof dtype);
2331
2332		sm_syslog(LOG_INFO, NOQID,
2333			  "starting daemon (%s): %s", Version, dtype + 1);
2334#if XLA
2335		xla_create_file();
2336#endif /* XLA */
2337
2338		/* save daemon type in a macro for possible PidFile use */
2339		macdefine(&BlankEnvelope.e_macro, A_TEMP,
2340			macid("{daemon_info}"), dtype + 1);
2341
2342		/* save queue interval in a macro for possible PidFile use */
2343		macdefine(&MainEnvelope.e_macro, A_TEMP,
2344			macid("{queue_interval}"), pintvl(QueueIntvl, true));
2345
2346		/* workaround: can't seem to release the signal in the parent */
2347		(void) sm_signal(SIGHUP, sighup);
2348		(void) sm_releasesignal(SIGHUP);
2349		(void) sm_signal(SIGTERM, sigterm);
2350
2351		if (QueueIntvl > 0)
2352		{
2353			(void) runqueue(true, false, queuepersistent, true);
2354
2355			/*
2356			**  If queuepersistent but not in daemon mode then
2357			**  we're going to do the queue runner monitoring here.
2358			**  If in daemon mode then the monitoring will happen
2359			**  elsewhere.
2360			*/
2361
2362			if (OpMode != MD_DAEMON && queuepersistent)
2363			{
2364				/* set the title to make it easier to find */
2365				sm_setproctitle(true, CurEnv, "Queue control");
2366				(void) sm_signal(SIGCHLD, SIG_DFL);
2367				while (CurChildren > 0)
2368				{
2369					int status;
2370					pid_t ret;
2371					int group;
2372
2373					CHECK_RESTART;
2374					while ((ret = sm_wait(&status)) <= 0)
2375						continue;
2376
2377					if (WIFSTOPPED(status))
2378						continue;
2379
2380					/* Probe only on a child status */
2381					proc_list_drop(ret, status, &group);
2382
2383					if (WIFSIGNALED(status))
2384					{
2385						if (WCOREDUMP(status))
2386						{
2387							sm_syslog(LOG_ERR, NOQID,
2388								  "persistent queue runner=%d core dumped, signal=%d",
2389								  group, WTERMSIG(status));
2390
2391							/* don't restart this */
2392							mark_work_group_restart(
2393								group, -1);
2394							continue;
2395						}
2396
2397						sm_syslog(LOG_ERR, NOQID,
2398							  "persistent queue runner=%d died, signal=%d",
2399							  group, WTERMSIG(status));
2400					}
2401
2402					/*
2403					**  When debugging active, don't
2404					**  restart the persistent queues.
2405					**  But do log this as info.
2406					*/
2407
2408					if (sm_debug_active(&DebugNoPRestart,
2409							    1))
2410					{
2411						sm_syslog(LOG_DEBUG, NOQID,
2412							  "persistent queue runner=%d, exited",
2413							  group);
2414						mark_work_group_restart(group,
2415									-1);
2416					}
2417				}
2418				finis(true, true, ExitStat);
2419				/* NOTREACHED */
2420			}
2421
2422			if (OpMode != MD_DAEMON)
2423			{
2424				char qtype[200];
2425
2426				/*
2427				**  Write the pid to file
2428				**  XXX Overwrites sendmail.pid
2429				*/
2430
2431				log_sendmail_pid(&MainEnvelope);
2432
2433				/* set the title to make it easier to find */
2434				qtype[0] = '\0';
2435				(void) sm_strlcpyn(qtype, sizeof qtype, 4,
2436						   "Queue runner@",
2437						   pintvl(QueueIntvl, true),
2438						   " for ",
2439						   QueueDir);
2440				sm_setproctitle(true, CurEnv, qtype);
2441				for (;;)
2442				{
2443					(void) pause();
2444					CHECK_RESTART;
2445					if (doqueuerun())
2446						(void) runqueue(true, false,
2447								false, false);
2448				}
2449			}
2450		}
2451		dropenvelope(&MainEnvelope, true, false);
2452
2453#if STARTTLS
2454		/* init TLS for server, ignore result for now */
2455		(void) initsrvtls(tls_ok);
2456#endif /* STARTTLS */
2457
2458	nextreq:
2459		p_flags = getrequests(&MainEnvelope);
2460
2461		/* drop privileges */
2462		(void) drop_privileges(false);
2463
2464		/*
2465		**  Get authentication data
2466		**  Set _ macro in BlankEnvelope before calling newenvelope().
2467		*/
2468
2469		authinfo = getauthinfo(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
2470						     NULL), &forged);
2471		macdefine(&BlankEnvelope.e_macro, A_TEMP, '_', authinfo);
2472
2473		/* at this point we are in a child: reset state */
2474		sm_rpool_free(MainEnvelope.e_rpool);
2475		(void) newenvelope(&MainEnvelope, &MainEnvelope,
2476				   sm_rpool_new_x(NULL));
2477	}
2478
2479	if (LogLevel > 9)
2480	{
2481		/* log connection information */
2482		sm_syslog(LOG_INFO, NULL, "connect from %s", authinfo);
2483	}
2484
2485	/*
2486	**  If running SMTP protocol, start collecting and executing
2487	**  commands.  This will never return.
2488	*/
2489
2490	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
2491	{
2492		char pbuf[20];
2493
2494		/*
2495		**  Save some macros for check_* rulesets.
2496		*/
2497
2498		if (forged)
2499		{
2500			char ipbuf[103];
2501
2502			(void) sm_snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
2503					   anynet_ntoa(&RealHostAddr));
2504			macdefine(&BlankEnvelope.e_macro, A_TEMP,
2505				  macid("{client_name}"), ipbuf);
2506		}
2507		else
2508			macdefine(&BlankEnvelope.e_macro, A_PERM,
2509				  macid("{client_name}"), RealHostName);
2510		macdefine(&BlankEnvelope.e_macro, A_TEMP,
2511			  macid("{client_addr}"), anynet_ntoa(&RealHostAddr));
2512		sm_getla();
2513
2514		switch (RealHostAddr.sa.sa_family)
2515		{
2516#if NETINET
2517		  case AF_INET:
2518			(void) sm_snprintf(pbuf, sizeof pbuf, "%d",
2519					   RealHostAddr.sin.sin_port);
2520			break;
2521#endif /* NETINET */
2522#if NETINET6
2523		  case AF_INET6:
2524			(void) sm_snprintf(pbuf, sizeof pbuf, "%d",
2525					   RealHostAddr.sin6.sin6_port);
2526			break;
2527#endif /* NETINET6 */
2528		  default:
2529			(void) sm_snprintf(pbuf, sizeof pbuf, "0");
2530			break;
2531		}
2532		macdefine(&BlankEnvelope.e_macro, A_TEMP,
2533			macid("{client_port}"), pbuf);
2534
2535		if (OpMode == MD_DAEMON)
2536		{
2537			/* validate the connection */
2538			HoldErrs = true;
2539			nullserver = validate_connection(&RealHostAddr,
2540							 RealHostName,
2541							 &MainEnvelope);
2542			HoldErrs = false;
2543		}
2544		else if (p_flags == NULL)
2545		{
2546			p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
2547			clrbitmap(p_flags);
2548		}
2549#if STARTTLS
2550		if (OpMode == MD_SMTP)
2551			(void) initsrvtls(tls_ok);
2552#endif /* STARTTLS */
2553
2554		/* turn off profiling */
2555		SM_PROF(1);
2556		smtp(nullserver, *p_flags, &MainEnvelope);
2557
2558		if (tTd(93, 100))
2559		{
2560			/* turn off profiling */
2561			SM_PROF(0);
2562			if (OpMode == MD_DAEMON)
2563				goto nextreq;
2564		}
2565	}
2566
2567	sm_rpool_free(MainEnvelope.e_rpool);
2568	clearenvelope(&MainEnvelope, false, sm_rpool_new_x(NULL));
2569	if (OpMode == MD_VERIFY)
2570	{
2571		set_delivery_mode(SM_VERIFY, &MainEnvelope);
2572		PostMasterCopy = NULL;
2573	}
2574	else
2575	{
2576		/* interactive -- all errors are global */
2577		MainEnvelope.e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
2578	}
2579
2580	/*
2581	**  Do basic system initialization and set the sender
2582	*/
2583
2584	initsys(&MainEnvelope);
2585	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{ntries}"), "0");
2586	macdefine(&MainEnvelope.e_macro, A_PERM, macid("{nrcpts}"), "0");
2587	setsender(from, &MainEnvelope, NULL, '\0', false);
2588	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
2589	    (!bitnset(M_LOCALMAILER, MainEnvelope.e_from.q_mailer->m_flags) ||
2590	     strcmp(MainEnvelope.e_from.q_user, RealUserName) != 0))
2591	{
2592		auth_warning(&MainEnvelope, "%s set sender to %s using -%c",
2593			     RealUserName, from, warn_f_flag);
2594#if SASL
2595		auth = false;
2596#endif /* SASL */
2597	}
2598	if (auth)
2599	{
2600		char *fv;
2601
2602		/* set the initial sender for AUTH= to $f@$j */
2603		fv = macvalue('f', &MainEnvelope);
2604		if (fv == NULL || *fv == '\0')
2605			MainEnvelope.e_auth_param = NULL;
2606		else
2607		{
2608			if (strchr(fv, '@') == NULL)
2609			{
2610				i = strlen(fv) + strlen(macvalue('j',
2611							&MainEnvelope)) + 2;
2612				p = sm_malloc_x(i);
2613				(void) sm_strlcpyn(p, i, 3, fv, "@",
2614						   macvalue('j',
2615							    &MainEnvelope));
2616			}
2617			else
2618				p = sm_strdup_x(fv);
2619			MainEnvelope.e_auth_param = sm_rpool_strdup_x(MainEnvelope.e_rpool,
2620								      xtextify(p, "="));
2621			sm_free(p);  /* XXX */
2622		}
2623	}
2624	if (macvalue('s', &MainEnvelope) == NULL)
2625		macdefine(&MainEnvelope.e_macro, A_PERM, 's', RealHostName);
2626
2627	av = argv + optind;
2628	if (*av == NULL && !GrabTo)
2629	{
2630		MainEnvelope.e_to = NULL;
2631		MainEnvelope.e_flags |= EF_GLOBALERRS;
2632		HoldErrs = false;
2633		SuperSafe = SAFE_NO;
2634		usrerr("Recipient names must be specified");
2635
2636		/* collect body for UUCP return */
2637		if (OpMode != MD_VERIFY)
2638			collect(InChannel, false, NULL, &MainEnvelope, true);
2639		finis(true, true, EX_USAGE);
2640		/* NOTREACHED */
2641	}
2642
2643	/*
2644	**  Scan argv and deliver the message to everyone.
2645	*/
2646
2647	save_val = LogUsrErrs;
2648	LogUsrErrs = true;
2649	sendtoargv(av, &MainEnvelope);
2650	LogUsrErrs = save_val;
2651
2652	/* if we have had errors sofar, arrange a meaningful exit stat */
2653	if (Errors > 0 && ExitStat == EX_OK)
2654		ExitStat = EX_USAGE;
2655
2656#if _FFR_FIX_DASHT
2657	/*
2658	**  If using -t, force not sending to argv recipients, even
2659	**  if they are mentioned in the headers.
2660	*/
2661
2662	if (GrabTo)
2663	{
2664		ADDRESS *q;
2665
2666		for (q = MainEnvelope.e_sendqueue; q != NULL; q = q->q_next)
2667			q->q_state = QS_REMOVED;
2668	}
2669#endif /* _FFR_FIX_DASHT */
2670
2671	/*
2672	**  Read the input mail.
2673	*/
2674
2675	MainEnvelope.e_to = NULL;
2676	if (OpMode != MD_VERIFY || GrabTo)
2677	{
2678		int savederrors;
2679		unsigned long savedflags;
2680
2681		/*
2682		**  workaround for compiler warning on Irix:
2683		**  do not initialize variable in the definition, but
2684		**  later on:
2685		**  warning(1548): transfer of control bypasses
2686		**  initialization of:
2687		**  variable "savederrors" (declared at line 2570)
2688		**  variable "savedflags" (declared at line 2571)
2689		**  goto giveup;
2690		*/
2691
2692		savederrors = Errors;
2693		savedflags = MainEnvelope.e_flags & EF_FATALERRS;
2694		MainEnvelope.e_flags |= EF_GLOBALERRS;
2695		MainEnvelope.e_flags &= ~EF_FATALERRS;
2696		Errors = 0;
2697		buffer_errors();
2698		collect(InChannel, false, NULL, &MainEnvelope, true);
2699
2700		/* header checks failed */
2701		if (Errors > 0)
2702		{
2703  giveup:
2704			if (!GrabTo)
2705			{
2706				/* Log who the mail would have gone to */
2707				logundelrcpts(&MainEnvelope,
2708					      MainEnvelope.e_message,
2709					      8, false);
2710			}
2711			flush_errors(true);
2712			finis(true, true, ExitStat);
2713			/* NOTREACHED */
2714			return -1;
2715		}
2716
2717		/* bail out if message too large */
2718		if (bitset(EF_CLRQUEUE, MainEnvelope.e_flags))
2719		{
2720			finis(true, true, ExitStat != EX_OK ? ExitStat
2721							    : EX_DATAERR);
2722			/* NOTREACHED */
2723			return -1;
2724		}
2725
2726		/* set message size */
2727		(void) sm_snprintf(buf, sizeof buf, "%ld",
2728				   MainEnvelope.e_msgsize);
2729		macdefine(&MainEnvelope.e_macro, A_TEMP,
2730			  macid("{msg_size}"), buf);
2731
2732		Errors = savederrors;
2733		MainEnvelope.e_flags |= savedflags;
2734	}
2735	errno = 0;
2736
2737	if (tTd(1, 1))
2738		sm_dprintf("From person = \"%s\"\n",
2739			   MainEnvelope.e_from.q_paddr);
2740
2741#if _FFR_QUARANTINE
2742	/* Check if quarantining stats should be updated */
2743	if (MainEnvelope.e_quarmsg != NULL)
2744		markstats(&MainEnvelope, NULL, STATS_QUARANTINE);
2745#endif /* _FFR_QUARANTINE */
2746
2747	/*
2748	**  Actually send everything.
2749	**	If verifying, just ack.
2750	*/
2751
2752	if (Errors == 0)
2753	{
2754		if (!split_by_recipient(&MainEnvelope) &&
2755		    bitset(EF_FATALERRS, MainEnvelope.e_flags))
2756			goto giveup;
2757	}
2758
2759	/* make sure we deliver at least the first envelope */
2760	i = FastSplit > 0 ? 0 : -1;
2761	for (e = &MainEnvelope; e != NULL; e = e->e_sibling, i++)
2762	{
2763		ENVELOPE *next;
2764
2765		e->e_from.q_state = QS_SENDER;
2766		if (tTd(1, 5))
2767		{
2768			sm_dprintf("main[%d]: QS_SENDER ", i);
2769			printaddr(&e->e_from, false);
2770		}
2771		e->e_to = NULL;
2772		sm_getla();
2773		GrabTo = false;
2774#if NAMED_BIND
2775		_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
2776		_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
2777#endif /* NAMED_BIND */
2778		next = e->e_sibling;
2779		e->e_sibling = NULL;
2780
2781		/* after FastSplit envelopes: queue up */
2782		sendall(e, i >= FastSplit ? SM_QUEUE : SM_DEFAULT);
2783		e->e_sibling = next;
2784	}
2785
2786	/*
2787	**  All done.
2788	**	Don't send return error message if in VERIFY mode.
2789	*/
2790
2791	finis(true, true, ExitStat);
2792	/* NOTREACHED */
2793	return ExitStat;
2794}
2795/*
2796**  STOP_SENDMAIL -- Stop the running program
2797**
2798**	Parameters:
2799**		none.
2800**
2801**	Returns:
2802**		none.
2803**
2804**	Side Effects:
2805**		exits.
2806*/
2807
2808void
2809stop_sendmail()
2810{
2811	/* reset uid for process accounting */
2812	endpwent();
2813	(void) setuid(RealUid);
2814	exit(EX_OK);
2815}
2816/*
2817**  FINIS -- Clean up and exit.
2818**
2819**	Parameters:
2820**		drop -- whether or not to drop CurEnv envelope
2821**		cleanup -- call exit() or _exit()?
2822**		exitstat -- exit status to use for exit() call
2823**
2824**	Returns:
2825**		never
2826**
2827**	Side Effects:
2828**		exits sendmail
2829*/
2830
2831void
2832finis(drop, cleanup, exitstat)
2833	bool drop;
2834	bool cleanup;
2835	volatile int exitstat;
2836{
2837
2838	/* Still want to process new timeouts added below */
2839	sm_clear_events();
2840	(void) sm_releasesignal(SIGALRM);
2841
2842	if (tTd(2, 1))
2843	{
2844		sm_dprintf("\n====finis: stat %d e_id=%s e_flags=",
2845			   exitstat,
2846			   CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
2847		printenvflags(CurEnv);
2848	}
2849	if (tTd(2, 9))
2850		printopenfds(false);
2851
2852	SM_TRY
2853		/*
2854		**  Clean up.  This might raise E:mta.quickabort
2855		*/
2856
2857		/* clean up temp files */
2858		CurEnv->e_to = NULL;
2859		if (drop)
2860		{
2861			if (CurEnv->e_id != NULL)
2862			{
2863				dropenvelope(CurEnv, true, false);
2864				sm_rpool_free(CurEnv->e_rpool);
2865				CurEnv->e_rpool = NULL;
2866			}
2867			else
2868				poststats(StatFile);
2869		}
2870
2871		/* flush any cached connections */
2872		mci_flush(true, NULL);
2873
2874		/* close maps belonging to this pid */
2875		closemaps(false);
2876
2877#if USERDB
2878		/* close UserDatabase */
2879		_udbx_close();
2880#endif /* USERDB */
2881
2882#if SASL
2883		stop_sasl_client();
2884#endif /* SASL */
2885
2886#if XLA
2887		/* clean up extended load average stuff */
2888		xla_all_end();
2889#endif /* XLA */
2890
2891	SM_FINALLY
2892		/*
2893		**  And exit.
2894		*/
2895
2896		if (LogLevel > 78)
2897			sm_syslog(LOG_DEBUG, CurEnv->e_id, "finis, pid=%d",
2898				  (int) CurrentPid);
2899		if (exitstat == EX_TEMPFAIL ||
2900		    CurEnv->e_errormode == EM_BERKNET)
2901			exitstat = EX_OK;
2902
2903		/* XXX clean up queues and related data structures */
2904		cleanup_queues();
2905#if SM_CONF_SHM
2906		cleanup_shm(DaemonPid == getpid());
2907#endif /* SM_CONF_SHM */
2908
2909		/* reset uid for process accounting */
2910		endpwent();
2911		sm_mbdb_terminate();
2912		(void) setuid(RealUid);
2913#if SM_HEAP_CHECK
2914		/* dump the heap, if we are checking for memory leaks */
2915		if (sm_debug_active(&SmHeapCheck, 2))
2916			sm_heap_report(smioout,
2917				       sm_debug_level(&SmHeapCheck) - 1);
2918#endif /* SM_HEAP_CHECK */
2919		if (sm_debug_active(&SmXtrapReport, 1))
2920			sm_dprintf("xtrap count = %d\n", SmXtrapCount);
2921		if (cleanup)
2922			exit(exitstat);
2923		else
2924			_exit(exitstat);
2925	SM_END_TRY
2926}
2927/*
2928**  INTINDEBUG -- signal handler for SIGINT in -bt mode
2929**
2930**	Parameters:
2931**		sig -- incoming signal.
2932**
2933**	Returns:
2934**		none.
2935**
2936**	Side Effects:
2937**		longjmps back to test mode loop.
2938**
2939**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2940**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2941**		DOING.
2942*/
2943
2944/* Type of an exception generated on SIGINT during address test mode.  */
2945static const SM_EXC_TYPE_T EtypeInterrupt =
2946{
2947	SmExcTypeMagic,
2948	"S:mta.interrupt",
2949	"",
2950	sm_etype_printf,
2951	"interrupt",
2952};
2953
2954/* ARGSUSED */
2955static SIGFUNC_DECL
2956intindebug(sig)
2957	int sig;
2958{
2959	int save_errno = errno;
2960
2961	FIX_SYSV_SIGNAL(sig, intindebug);
2962	errno = save_errno;
2963	CHECK_CRITICAL(sig);
2964	errno = save_errno;
2965	sm_exc_raisenew_x(&EtypeInterrupt);
2966	errno = save_errno;
2967	return SIGFUNC_RETURN;
2968}
2969/*
2970**  SIGTERM -- SIGTERM handler for the daemon
2971**
2972**	Parameters:
2973**		sig -- signal number.
2974**
2975**	Returns:
2976**		none.
2977**
2978**	Side Effects:
2979**		Sets ShutdownRequest which will hopefully trigger
2980**		the daemon to exit.
2981**
2982**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2983**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2984**		DOING.
2985*/
2986
2987/* ARGSUSED */
2988static SIGFUNC_DECL
2989sigterm(sig)
2990	int sig;
2991{
2992	int save_errno = errno;
2993
2994	FIX_SYSV_SIGNAL(sig, sigterm);
2995	ShutdownRequest = "signal";
2996	errno = save_errno;
2997	return SIGFUNC_RETURN;
2998}
2999/*
3000**  SIGHUP -- handle a SIGHUP signal
3001**
3002**	Parameters:
3003**		sig -- incoming signal.
3004**
3005**	Returns:
3006**		none.
3007**
3008**	Side Effects:
3009**		Sets RestartRequest which should cause the daemon
3010**		to restart.
3011**
3012**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3013**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3014**		DOING.
3015*/
3016
3017/* ARGSUSED */
3018static SIGFUNC_DECL
3019sighup(sig)
3020	int sig;
3021{
3022	int save_errno = errno;
3023
3024	FIX_SYSV_SIGNAL(sig, sighup);
3025	RestartRequest = "signal";
3026	errno = save_errno;
3027	return SIGFUNC_RETURN;
3028}
3029/*
3030**  SIGPIPE -- signal handler for SIGPIPE
3031**
3032**	Parameters:
3033**		sig -- incoming signal.
3034**
3035**	Returns:
3036**		none.
3037**
3038**	Side Effects:
3039**		Sets StopRequest which should cause the mailq/hoststatus
3040**		display to stop.
3041**
3042**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3043**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3044**		DOING.
3045*/
3046
3047/* ARGSUSED */
3048static SIGFUNC_DECL
3049sigpipe(sig)
3050	int sig;
3051{
3052	int save_errno = errno;
3053
3054	FIX_SYSV_SIGNAL(sig, sigpipe);
3055	StopRequest = true;
3056	errno = save_errno;
3057	return SIGFUNC_RETURN;
3058}
3059/*
3060**  INTSIG -- clean up on interrupt
3061**
3062**	This just arranges to exit.  It pessimizes in that it
3063**	may resend a message.
3064**
3065**	Parameters:
3066**		none.
3067**
3068**	Returns:
3069**		none.
3070**
3071**	Side Effects:
3072**		Unlocks the current job.
3073**
3074**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3075**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3076**		DOING.
3077**
3078**		XXX: More work is needed for this signal handler.
3079*/
3080
3081/* ARGSUSED */
3082SIGFUNC_DECL
3083intsig(sig)
3084	int sig;
3085{
3086	bool drop = false;
3087	int save_errno = errno;
3088
3089	FIX_SYSV_SIGNAL(sig, intsig);
3090	errno = save_errno;
3091	CHECK_CRITICAL(sig);
3092	sm_allsignals(true);
3093
3094	if (sig != 0 && LogLevel > 79)
3095		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
3096	FileName = NULL;
3097
3098	/* Clean-up on aborted stdin message submission */
3099	if (CurEnv->e_id != NULL &&
3100	    (OpMode == MD_SMTP ||
3101	     OpMode == MD_DELIVER ||
3102	     OpMode == MD_ARPAFTP))
3103	{
3104		register ADDRESS *q;
3105
3106		/* don't return an error indication */
3107		CurEnv->e_to = NULL;
3108		CurEnv->e_flags &= ~EF_FATALERRS;
3109		CurEnv->e_flags |= EF_CLRQUEUE;
3110
3111		/*
3112		**  Spin through the addresses and
3113		**  mark them dead to prevent bounces
3114		*/
3115
3116		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
3117			q->q_state = QS_DONTSEND;
3118
3119		drop = true;
3120	}
3121	else if (OpMode != MD_TEST)
3122	{
3123		unlockqueue(CurEnv);
3124	}
3125
3126	finis(drop, false, EX_OK);
3127	/* NOTREACHED */
3128}
3129/*
3130**  DISCONNECT -- remove our connection with any foreground process
3131**
3132**	Parameters:
3133**		droplev -- how "deeply" we should drop the line.
3134**			0 -- ignore signals, mail back errors, make sure
3135**			     output goes to stdout.
3136**			1 -- also, make stdout go to /dev/null.
3137**			2 -- also, disconnect from controlling terminal
3138**			     (only for daemon mode).
3139**		e -- the current envelope.
3140**
3141**	Returns:
3142**		none
3143**
3144**	Side Effects:
3145**		Trys to insure that we are immune to vagaries of
3146**		the controlling tty.
3147*/
3148
3149void
3150disconnect(droplev, e)
3151	int droplev;
3152	register ENVELOPE *e;
3153{
3154	int fd;
3155
3156	if (tTd(52, 1))
3157		sm_dprintf("disconnect: In %d Out %d, e=%p\n",
3158			   sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL),
3159			   sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL), e);
3160	if (tTd(52, 100))
3161	{
3162		sm_dprintf("don't\n");
3163		return;
3164	}
3165	if (LogLevel > 93)
3166		sm_syslog(LOG_DEBUG, e->e_id,
3167			  "disconnect level %d",
3168			  droplev);
3169
3170	/* be sure we don't get nasty signals */
3171	(void) sm_signal(SIGINT, SIG_IGN);
3172	(void) sm_signal(SIGQUIT, SIG_IGN);
3173
3174	/* we can't communicate with our caller, so.... */
3175	HoldErrs = true;
3176	CurEnv->e_errormode = EM_MAIL;
3177	Verbose = 0;
3178	DisConnected = true;
3179
3180	/* all input from /dev/null */
3181	if (InChannel != smioin)
3182	{
3183		(void) sm_io_close(InChannel, SM_TIME_DEFAULT);
3184		InChannel = smioin;
3185	}
3186	if (sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
3187			 SM_IO_RDONLY, NULL, smioin) == NULL)
3188		sm_syslog(LOG_ERR, e->e_id,
3189			  "disconnect: sm_io_reopen(\"%s\") failed: %s",
3190			  SM_PATH_DEVNULL, sm_errstring(errno));
3191
3192	/*
3193	**  output to the transcript
3194	**	We also compare the fd numbers here since OutChannel
3195	**	might be a layer on top of smioout due to encryption
3196	**	(see sfsasl.c).
3197	*/
3198
3199	if (OutChannel != smioout &&
3200	    sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL) !=
3201	    sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL))
3202	{
3203		(void) sm_io_close(OutChannel, SM_TIME_DEFAULT);
3204		OutChannel = smioout;
3205
3206#if 0
3207		/*
3208		**  Has smioout been closed? Reopen it.
3209		**	This shouldn't happen anymore, the code is here
3210		**	just as a reminder.
3211		*/
3212
3213		if (smioout->sm_magic == NULL &&
3214		    sm_io_reopen(SmFtStdio, SM_TIME_DEFAULT, SM_PATH_DEVNULL,
3215				 SM_IO_WRONLY, NULL, smioout) == NULL)
3216			sm_syslog(LOG_ERR, e->e_id,
3217				  "disconnect: sm_io_reopen(\"%s\") failed: %s",
3218				  SM_PATH_DEVNULL, sm_errstring(errno));
3219#endif /* 0 */
3220	}
3221	if (droplev > 0)
3222	{
3223		fd = open(SM_PATH_DEVNULL, O_WRONLY, 0666);
3224		if (fd == -1)
3225			sm_syslog(LOG_ERR, e->e_id,
3226				  "disconnect: open(\"%s\") failed: %s",
3227				  SM_PATH_DEVNULL, sm_errstring(errno));
3228		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
3229		(void) dup2(fd, STDOUT_FILENO);
3230		(void) dup2(fd, STDERR_FILENO);
3231		(void) close(fd);
3232	}
3233
3234	/* drop our controlling TTY completely if possible */
3235	if (droplev > 1)
3236	{
3237		(void) setsid();
3238		errno = 0;
3239	}
3240
3241#if XDEBUG
3242	checkfd012("disconnect");
3243#endif /* XDEBUG */
3244
3245	if (LogLevel > 71)
3246		sm_syslog(LOG_DEBUG, e->e_id, "in background, pid=%d",
3247			  (int) CurrentPid);
3248
3249	errno = 0;
3250}
3251
3252static void
3253obsolete(argv)
3254	char *argv[];
3255{
3256	register char *ap;
3257	register char *op;
3258
3259	while ((ap = *++argv) != NULL)
3260	{
3261		/* Return if "--" or not an option of any form. */
3262		if (ap[0] != '-' || ap[1] == '-')
3263			return;
3264
3265#if _FFR_QUARANTINE
3266		/* Don't allow users to use "-Q." or "-Q ." */
3267		if ((ap[1] == 'Q' && ap[2] == '.') ||
3268		    (ap[1] == 'Q' && argv[1] != NULL &&
3269		     argv[1][0] == '.' && argv[1][1] == '\0'))
3270		{
3271			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3272					     "Can not use -Q.\n");
3273			exit(EX_USAGE);
3274		}
3275#endif /* _FFR_QUARANTINE */
3276
3277		/* skip over options that do have a value */
3278		op = strchr(OPTIONS, ap[1]);
3279		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
3280		    ap[1] != 'd' &&
3281#if defined(sony_news)
3282		    ap[1] != 'E' && ap[1] != 'J' &&
3283#endif /* defined(sony_news) */
3284		    argv[1] != NULL && argv[1][0] != '-')
3285		{
3286			argv++;
3287			continue;
3288		}
3289
3290		/* If -C doesn't have an argument, use sendmail.cf. */
3291#define __DEFPATH	"sendmail.cf"
3292		if (ap[1] == 'C' && ap[2] == '\0')
3293		{
3294			*argv = xalloc(sizeof(__DEFPATH) + 2);
3295			(void) sm_strlcpyn(argv[0], sizeof(__DEFPATH) + 2, 2,
3296					   "-C", __DEFPATH);
3297		}
3298
3299		/* If -q doesn't have an argument, run it once. */
3300		if (ap[1] == 'q' && ap[2] == '\0')
3301			*argv = "-q0";
3302
3303#if _FFR_QUARANTINE
3304		/* If -Q doesn't have an argument, disable quarantining */
3305		if (ap[1] == 'Q' && ap[2] == '\0')
3306			*argv = "-Q.";
3307#endif /* _FFR_QUARANTINE */
3308
3309		/* if -d doesn't have an argument, use 0-99.1 */
3310		if (ap[1] == 'd' && ap[2] == '\0')
3311			*argv = "-d0-99.1";
3312
3313#if defined(sony_news)
3314		/* if -E doesn't have an argument, use -EC */
3315		if (ap[1] == 'E' && ap[2] == '\0')
3316			*argv = "-EC";
3317
3318		/* if -J doesn't have an argument, use -JJ */
3319		if (ap[1] == 'J' && ap[2] == '\0')
3320			*argv = "-JJ";
3321#endif /* defined(sony_news) */
3322	}
3323}
3324/*
3325**  AUTH_WARNING -- specify authorization warning
3326**
3327**	Parameters:
3328**		e -- the current envelope.
3329**		msg -- the text of the message.
3330**		args -- arguments to the message.
3331**
3332**	Returns:
3333**		none.
3334*/
3335
3336void
3337#ifdef __STDC__
3338auth_warning(register ENVELOPE *e, const char *msg, ...)
3339#else /* __STDC__ */
3340auth_warning(e, msg, va_alist)
3341	register ENVELOPE *e;
3342	const char *msg;
3343	va_dcl
3344#endif /* __STDC__ */
3345{
3346	char buf[MAXLINE];
3347	SM_VA_LOCAL_DECL
3348
3349	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
3350	{
3351		register char *p;
3352		static char hostbuf[48];
3353
3354		if (hostbuf[0] == '\0')
3355		{
3356			struct hostent *hp;
3357
3358			hp = myhostname(hostbuf, sizeof hostbuf);
3359#if NETINET6
3360			if (hp != NULL)
3361			{
3362				freehostent(hp);
3363				hp = NULL;
3364			}
3365#endif /* NETINET6 */
3366		}
3367
3368		(void) sm_strlcpyn(buf, sizeof buf, 2, hostbuf, ": ");
3369		p = &buf[strlen(buf)];
3370		SM_VA_START(ap, msg);
3371		(void) sm_vsnprintf(p, SPACELEFT(buf, p), msg, ap);
3372		SM_VA_END(ap);
3373		addheader("X-Authentication-Warning", buf, 0, e);
3374		if (LogLevel > 3)
3375			sm_syslog(LOG_INFO, e->e_id,
3376				  "Authentication-Warning: %.400s",
3377				  buf);
3378	}
3379}
3380/*
3381**  GETEXTENV -- get from external environment
3382**
3383**	Parameters:
3384**		envar -- the name of the variable to retrieve
3385**
3386**	Returns:
3387**		The value, if any.
3388*/
3389
3390static char *
3391getextenv(envar)
3392	const char *envar;
3393{
3394	char **envp;
3395	int l;
3396
3397	l = strlen(envar);
3398	for (envp = ExternalEnviron; envp != NULL && *envp != NULL; envp++)
3399	{
3400		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
3401			return &(*envp)[l + 1];
3402	}
3403	return NULL;
3404}
3405/*
3406**  SETUSERENV -- set an environment in the propagated environment
3407**
3408**	Parameters:
3409**		envar -- the name of the environment variable.
3410**		value -- the value to which it should be set.  If
3411**			null, this is extracted from the incoming
3412**			environment.  If that is not set, the call
3413**			to setuserenv is ignored.
3414**
3415**	Returns:
3416**		none.
3417*/
3418
3419void
3420setuserenv(envar, value)
3421	const char *envar;
3422	const char *value;
3423{
3424	int i, l;
3425	char **evp = UserEnviron;
3426	char *p;
3427
3428	if (value == NULL)
3429	{
3430		value = getextenv(envar);
3431		if (value == NULL)
3432			return;
3433	}
3434
3435	/* XXX enforce reasonable size? */
3436	i = strlen(envar) + 1;
3437	l = strlen(value) + i + 1;
3438	p = (char *) xalloc(l);
3439	(void) sm_strlcpyn(p, l, 3, envar, "=", value);
3440
3441	while (*evp != NULL && strncmp(*evp, p, i) != 0)
3442		evp++;
3443	if (*evp != NULL)
3444	{
3445		*evp++ = p;
3446	}
3447	else if (evp < &UserEnviron[MAXUSERENVIRON])
3448	{
3449		*evp++ = p;
3450		*evp = NULL;
3451	}
3452
3453	/* make sure it is in our environment as well */
3454	if (putenv(p) < 0)
3455		syserr("setuserenv: putenv(%s) failed", p);
3456}
3457/*
3458**  DUMPSTATE -- dump state
3459**
3460**	For debugging.
3461*/
3462
3463void
3464dumpstate(when)
3465	char *when;
3466{
3467	register char *j = macvalue('j', CurEnv);
3468	int rs;
3469	extern int NextMacroId;
3470
3471	sm_syslog(LOG_DEBUG, CurEnv->e_id,
3472		  "--- dumping state on %s: $j = %s ---",
3473		  when,
3474		  j == NULL ? "<NULL>" : j);
3475	if (j != NULL)
3476	{
3477		if (!wordinclass(j, 'w'))
3478			sm_syslog(LOG_DEBUG, CurEnv->e_id,
3479				  "*** $j not in $=w ***");
3480	}
3481	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
3482	sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)",
3483		  NextMacroId, MAXMACROID);
3484	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
3485	printopenfds(true);
3486	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
3487	mci_dump_all(true);
3488	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
3489	if (rs > 0)
3490	{
3491		int status;
3492		register char **pvp;
3493		char *pv[MAXATOM + 1];
3494
3495		pv[0] = NULL;
3496		status = REWRITE(pv, rs, CurEnv);
3497		sm_syslog(LOG_DEBUG, CurEnv->e_id,
3498			  "--- ruleset debug_dumpstate returns stat %d, pv: ---",
3499			  status);
3500		for (pvp = pv; *pvp != NULL; pvp++)
3501			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
3502	}
3503	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
3504}
3505
3506#ifdef SIGUSR1
3507/*
3508**  SIGUSR1 -- Signal a request to dump state.
3509**
3510**	Parameters:
3511**		sig -- calling signal.
3512**
3513**	Returns:
3514**		none.
3515**
3516**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
3517**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
3518**		DOING.
3519**
3520**		XXX: More work is needed for this signal handler.
3521*/
3522
3523/* ARGSUSED */
3524static SIGFUNC_DECL
3525sigusr1(sig)
3526	int sig;
3527{
3528	int save_errno = errno;
3529# if SM_HEAP_CHECK
3530	extern void dumpstab __P((void));
3531# endif /* SM_HEAP_CHECK */
3532
3533	FIX_SYSV_SIGNAL(sig, sigusr1);
3534	errno = save_errno;
3535	CHECK_CRITICAL(sig);
3536	dumpstate("user signal");
3537# if SM_HEAP_CHECK
3538	dumpstab();
3539# endif /* SM_HEAP_CHECK */
3540	errno = save_errno;
3541	return SIGFUNC_RETURN;
3542}
3543#endif /* SIGUSR1 */
3544
3545/*
3546**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
3547**
3548**	Parameters:
3549**		to_real_uid -- if set, drop to the real uid instead
3550**			of the RunAsUser.
3551**
3552**	Returns:
3553**		EX_OSERR if the setuid failed.
3554**		EX_OK otherwise.
3555*/
3556
3557int
3558drop_privileges(to_real_uid)
3559	bool to_real_uid;
3560{
3561	int rval = EX_OK;
3562	GIDSET_T emptygidset[1];
3563
3564	if (tTd(47, 1))
3565		sm_dprintf("drop_privileges(%d): Real[UG]id=%d:%d, get[ug]id=%d:%d, gete[ug]id=%d:%d, RunAs[UG]id=%d:%d\n",
3566			   (int) to_real_uid,
3567			   (int) RealUid, (int) RealGid,
3568			   (int) getuid(), (int) getgid(),
3569			   (int) geteuid(), (int) getegid(),
3570			   (int) RunAsUid, (int) RunAsGid);
3571
3572	if (to_real_uid)
3573	{
3574		RunAsUserName = RealUserName;
3575		RunAsUid = RealUid;
3576		RunAsGid = RealGid;
3577		EffGid = RunAsGid;
3578	}
3579
3580	/* make sure no one can grab open descriptors for secret files */
3581	endpwent();
3582	sm_mbdb_terminate();
3583
3584	/* reset group permissions; these can be set later */
3585	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
3586
3587	/*
3588	**  Notice:  on some OS (Linux...) the setgroups() call causes
3589	**	a logfile entry if sendmail is not run by root.
3590	**	However, it is unclear (no POSIX standard) whether
3591	**	setgroups() can only succeed if executed by root.
3592	**	So for now we keep it as it is; if you want to change it, use
3593	**  if (geteuid() == 0 && setgroups(1, emptygidset) == -1)
3594	*/
3595
3596	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
3597	{
3598		syserr("drop_privileges: setgroups(1, %d) failed",
3599		       (int) emptygidset[0]);
3600		rval = EX_OSERR;
3601	}
3602
3603	/* reset primary group id */
3604	if (to_real_uid)
3605	{
3606		/*
3607		**  Drop gid to real gid.
3608		**  On some OS we must reset the effective[/real[/saved]] gid,
3609		**  and then use setgid() to finally drop all group privileges.
3610		**  Later on we check whether we can get back the
3611		**  effective gid.
3612		*/
3613
3614#if HASSETEGID
3615		if (setegid(RunAsGid) < 0)
3616		{
3617			syserr("drop_privileges: setegid(%d) failed",
3618			       (int) RunAsGid);
3619			rval = EX_OSERR;
3620		}
3621#else /* HASSETEGID */
3622# if HASSETREGID
3623		if (setregid(RunAsGid, RunAsGid) < 0)
3624		{
3625			syserr("drop_privileges: setregid(%d, %d) failed",
3626			       (int) RunAsGid, (int) RunAsGid);
3627			rval = EX_OSERR;
3628		}
3629# else /* HASSETREGID */
3630#  if HASSETRESGID
3631		if (setresgid(RunAsGid, RunAsGid, RunAsGid) < 0)
3632		{
3633			syserr("drop_privileges: setresgid(%d, %d, %d) failed",
3634			       (int) RunAsGid, (int) RunAsGid, (int) RunAsGid);
3635			rval = EX_OSERR;
3636		}
3637#  endif /* HASSETRESGID */
3638# endif /* HASSETREGID */
3639#endif /* HASSETEGID */
3640	}
3641	if (rval == EX_OK && (to_real_uid || RunAsGid != 0))
3642	{
3643		if (setgid(RunAsGid) < 0 && (!UseMSP || getegid() != RunAsGid))
3644		{
3645			syserr("drop_privileges: setgid(%d) failed",
3646			       (int) RunAsGid);
3647			rval = EX_OSERR;
3648		}
3649		errno = 0;
3650		if (rval == EX_OK && getegid() != RunAsGid)
3651		{
3652			syserr("drop_privileges: Unable to set effective gid=%d to RunAsGid=%d",
3653			       (int) getegid(), (int) RunAsGid);
3654			rval = EX_OSERR;
3655		}
3656	}
3657
3658	/* fiddle with uid */
3659	if (to_real_uid || RunAsUid != 0)
3660	{
3661		uid_t euid;
3662
3663		/*
3664		**  Try to setuid(RunAsUid).
3665		**  euid must be RunAsUid,
3666		**  ruid must be RunAsUid unless (e|r)uid wasn't 0
3667		**	and we didn't have to drop privileges to the real uid.
3668		*/
3669
3670		if (setuid(RunAsUid) < 0 ||
3671		    geteuid() != RunAsUid ||
3672		    (getuid() != RunAsUid &&
3673		     (to_real_uid || geteuid() == 0 || getuid() == 0)))
3674		{
3675#if HASSETREUID
3676			/*
3677			**  if ruid != RunAsUid, euid == RunAsUid, then
3678			**  try resetting just the real uid, then using
3679			**  setuid() to drop the saved-uid as well.
3680			*/
3681
3682			if (geteuid() == RunAsUid)
3683			{
3684				if (setreuid(RunAsUid, -1) < 0)
3685				{
3686					syserr("drop_privileges: setreuid(%d, -1) failed",
3687					       (int) RunAsUid);
3688					rval = EX_OSERR;
3689				}
3690				if (setuid(RunAsUid) < 0)
3691				{
3692					syserr("drop_privileges: second setuid(%d) attempt failed",
3693					       (int) RunAsUid);
3694					rval = EX_OSERR;
3695				}
3696			}
3697			else
3698#endif /* HASSETREUID */
3699			{
3700				syserr("drop_privileges: setuid(%d) failed",
3701				       (int) RunAsUid);
3702				rval = EX_OSERR;
3703			}
3704		}
3705		euid = geteuid();
3706		if (RunAsUid != 0 && setuid(0) == 0)
3707		{
3708			/*
3709			**  Believe it or not, the Linux capability model
3710			**  allows a non-root process to override setuid()
3711			**  on a process running as root and prevent that
3712			**  process from dropping privileges.
3713			*/
3714
3715			syserr("drop_privileges: setuid(0) succeeded (when it should not)");
3716			rval = EX_OSERR;
3717		}
3718		else if (RunAsUid != euid && setuid(euid) == 0)
3719		{
3720			/*
3721			**  Some operating systems will keep the saved-uid
3722			**  if a non-root effective-uid calls setuid(real-uid)
3723			**  making it possible to set it back again later.
3724			*/
3725
3726			syserr("drop_privileges: Unable to drop non-root set-user-ID privileges");
3727			rval = EX_OSERR;
3728		}
3729	}
3730
3731	if ((to_real_uid || RunAsGid != 0) &&
3732	    rval == EX_OK && RunAsGid != EffGid &&
3733	    getuid() != 0 && geteuid() != 0)
3734	{
3735		errno = 0;
3736		if (setgid(EffGid) == 0)
3737		{
3738			syserr("drop_privileges: setgid(%d) succeeded (when it should not)",
3739			       (int) EffGid);
3740			rval = EX_OSERR;
3741		}
3742	}
3743
3744	if (tTd(47, 5))
3745	{
3746		sm_dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
3747			   (int) geteuid(), (int) getuid(),
3748			   (int) getegid(), (int) getgid());
3749		sm_dprintf("drop_privileges: RunAsUser = %d:%d\n",
3750			   (int) RunAsUid, (int) RunAsGid);
3751		if (tTd(47, 10))
3752			sm_dprintf("drop_privileges: rval = %d\n", rval);
3753	}
3754	return rval;
3755}
3756/*
3757**  FILL_FD -- make sure a file descriptor has been properly allocated
3758**
3759**	Used to make sure that stdin/out/err are allocated on startup
3760**
3761**	Parameters:
3762**		fd -- the file descriptor to be filled.
3763**		where -- a string used for logging.  If NULL, this is
3764**			being called on startup, and logging should
3765**			not be done.
3766**
3767**	Returns:
3768**		none
3769**
3770**	Side Effects:
3771**		possibly changes MissingFds
3772*/
3773
3774void
3775fill_fd(fd, where)
3776	int fd;
3777	char *where;
3778{
3779	int i;
3780	struct stat stbuf;
3781
3782	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
3783		return;
3784
3785	if (where != NULL)
3786		syserr("fill_fd: %s: fd %d not open", where, fd);
3787	else
3788		MissingFds |= 1 << fd;
3789	i = open(SM_PATH_DEVNULL, fd == 0 ? O_RDONLY : O_WRONLY, 0666);
3790	if (i < 0)
3791	{
3792		syserr("!fill_fd: %s: cannot open %s",
3793		       where == NULL ? "startup" : where, SM_PATH_DEVNULL);
3794	}
3795	if (fd != i)
3796	{
3797		(void) dup2(i, fd);
3798		(void) close(i);
3799	}
3800}
3801/*
3802**  SM_PRINTOPTIONS -- print options
3803**
3804**	Parameters:
3805**		options -- array of options.
3806**
3807**	Returns:
3808**		none.
3809*/
3810
3811static void
3812sm_printoptions(options)
3813	char **options;
3814{
3815	int ll;
3816	char **av;
3817
3818	av = options;
3819	ll = 7;
3820	while (*av != NULL)
3821	{
3822		if (ll + strlen(*av) > 63)
3823		{
3824			sm_dprintf("\n");
3825			ll = 0;
3826		}
3827		if (ll == 0)
3828			sm_dprintf("\t\t");
3829		else
3830			sm_dprintf(" ");
3831		sm_dprintf("%s", *av);
3832		ll += strlen(*av++) + 1;
3833	}
3834	sm_dprintf("\n");
3835}
3836/*
3837**  TESTMODELINE -- process a test mode input line
3838**
3839**	Parameters:
3840**		line -- the input line.
3841**		e -- the current environment.
3842**	Syntax:
3843**		#  a comment
3844**		.X process X as a configuration line
3845**		=X dump a configuration item (such as mailers)
3846**		$X dump a macro or class
3847**		/X try an activity
3848**		X  normal process through rule set X
3849*/
3850
3851static void
3852testmodeline(line, e)
3853	char *line;
3854	ENVELOPE *e;
3855{
3856	register char *p;
3857	char *q;
3858	auto char *delimptr;
3859	int mid;
3860	int i, rs;
3861	STAB *map;
3862	char **s;
3863	struct rewrite *rw;
3864	ADDRESS a;
3865	static int tryflags = RF_COPYNONE;
3866	char exbuf[MAXLINE];
3867	extern unsigned char TokTypeNoC[];
3868
3869	/* skip leading spaces */
3870	while (*line == ' ')
3871		line++;
3872
3873	switch (line[0])
3874	{
3875	  case '#':
3876	  case '\0':
3877		return;
3878
3879	  case '?':
3880		help("-bt", e);
3881		return;
3882
3883	  case '.':		/* config-style settings */
3884		switch (line[1])
3885		{
3886		  case 'D':
3887			mid = macid_parse(&line[2], &delimptr);
3888			if (mid == 0)
3889				return;
3890			translate_dollars(delimptr);
3891			macdefine(&e->e_macro, A_TEMP, mid, delimptr);
3892			break;
3893
3894		  case 'C':
3895			if (line[2] == '\0')	/* not to call syserr() */
3896				return;
3897
3898			mid = macid_parse(&line[2], &delimptr);
3899			if (mid == 0)
3900				return;
3901			translate_dollars(delimptr);
3902			expand(delimptr, exbuf, sizeof exbuf, e);
3903			p = exbuf;
3904			while (*p != '\0')
3905			{
3906				register char *wd;
3907				char delim;
3908
3909				while (*p != '\0' && isascii(*p) && isspace(*p))
3910					p++;
3911				wd = p;
3912				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
3913					p++;
3914				delim = *p;
3915				*p = '\0';
3916				if (wd[0] != '\0')
3917					setclass(mid, wd);
3918				*p = delim;
3919			}
3920			break;
3921
3922		  case '\0':
3923			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3924					     "Usage: .[DC]macro value(s)\n");
3925			break;
3926
3927		  default:
3928			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3929					     "Unknown \".\" command %s\n", line);
3930			break;
3931		}
3932		return;
3933
3934	  case '=':		/* config-style settings */
3935		switch (line[1])
3936		{
3937		  case 'S':		/* dump rule set */
3938			rs = strtorwset(&line[2], NULL, ST_FIND);
3939			if (rs < 0)
3940			{
3941				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3942						     "Undefined ruleset %s\n", &line[2]);
3943				return;
3944			}
3945			rw = RewriteRules[rs];
3946			if (rw == NULL)
3947				return;
3948			do
3949			{
3950				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
3951						  'R');
3952				s = rw->r_lhs;
3953				while (*s != NULL)
3954				{
3955					xputs(*s++);
3956					(void) sm_io_putc(smioout,
3957							  SM_TIME_DEFAULT, ' ');
3958				}
3959				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
3960						  '\t');
3961				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
3962						  '\t');
3963				s = rw->r_rhs;
3964				while (*s != NULL)
3965				{
3966					xputs(*s++);
3967					(void) sm_io_putc(smioout,
3968							  SM_TIME_DEFAULT, ' ');
3969				}
3970				(void) sm_io_putc(smioout, SM_TIME_DEFAULT,
3971						  '\n');
3972			} while ((rw = rw->r_next) != NULL);
3973			break;
3974
3975		  case 'M':
3976			for (i = 0; i < MAXMAILERS; i++)
3977			{
3978				if (Mailer[i] != NULL)
3979					printmailer(Mailer[i]);
3980			}
3981			break;
3982
3983		  case '\0':
3984			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3985					     "Usage: =Sruleset or =M\n");
3986			break;
3987
3988		  default:
3989			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3990					     "Unknown \"=\" command %s\n", line);
3991			break;
3992		}
3993		return;
3994
3995	  case '-':		/* set command-line-like opts */
3996		switch (line[1])
3997		{
3998		  case 'd':
3999			tTflag(&line[2]);
4000			break;
4001
4002		  case '\0':
4003			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4004					     "Usage: -d{debug arguments}\n");
4005			break;
4006
4007		  default:
4008			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4009					     "Unknown \"-\" command %s\n", line);
4010			break;
4011		}
4012		return;
4013
4014	  case '$':
4015		if (line[1] == '=')
4016		{
4017			mid = macid(&line[2]);
4018			if (mid != 0)
4019				stabapply(dump_class, mid);
4020			return;
4021		}
4022		mid = macid(&line[1]);
4023		if (mid == 0)
4024			return;
4025		p = macvalue(mid, e);
4026		if (p == NULL)
4027			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4028					     "Undefined\n");
4029		else
4030		{
4031			xputs(p);
4032			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4033					     "\n");
4034		}
4035		return;
4036
4037	  case '/':		/* miscellaneous commands */
4038		p = &line[strlen(line)];
4039		while (--p >= line && isascii(*p) && isspace(*p))
4040			*p = '\0';
4041		p = strpbrk(line, " \t");
4042		if (p != NULL)
4043		{
4044			while (isascii(*p) && isspace(*p))
4045				*p++ = '\0';
4046		}
4047		else
4048			p = "";
4049		if (line[1] == '\0')
4050		{
4051			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4052					     "Usage: /[canon|map|mx|parse|try|tryflags]\n");
4053			return;
4054		}
4055		if (sm_strcasecmp(&line[1], "quit") == 0)
4056		{
4057			CurEnv->e_id = NULL;
4058			finis(true, true, ExitStat);
4059			/* NOTREACHED */
4060		}
4061		if (sm_strcasecmp(&line[1], "mx") == 0)
4062		{
4063#if NAMED_BIND
4064			/* look up MX records */
4065			int nmx;
4066			auto int rcode;
4067			char *mxhosts[MAXMXHOSTS + 1];
4068
4069			if (*p == '\0')
4070			{
4071				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4072						     "Usage: /mx address\n");
4073				return;
4074			}
4075			nmx = getmxrr(p, mxhosts, NULL, false, &rcode, true,
4076				      NULL);
4077			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4078					     "getmxrr(%s) returns %d value(s):\n",
4079				p, nmx);
4080			for (i = 0; i < nmx; i++)
4081				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4082						     "\t%s\n", mxhosts[i]);
4083#else /* NAMED_BIND */
4084			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4085					     "No MX code compiled in\n");
4086#endif /* NAMED_BIND */
4087		}
4088		else if (sm_strcasecmp(&line[1], "canon") == 0)
4089		{
4090			char host[MAXHOSTNAMELEN];
4091
4092			if (*p == '\0')
4093			{
4094				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4095						     "Usage: /canon address\n");
4096				return;
4097			}
4098			else if (sm_strlcpy(host, p, sizeof host) >= sizeof host)
4099			{
4100				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4101						     "Name too long\n");
4102				return;
4103			}
4104			(void) getcanonname(host, sizeof host, !HasWildcardMX,
4105					    NULL);
4106			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4107					     "getcanonname(%s) returns %s\n",
4108					     p, host);
4109		}
4110		else if (sm_strcasecmp(&line[1], "map") == 0)
4111		{
4112			auto int rcode = EX_OK;
4113			char *av[2];
4114
4115			if (*p == '\0')
4116			{
4117				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4118						     "Usage: /map mapname key\n");
4119				return;
4120			}
4121			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q));			     q++)
4122				continue;
4123			if (*q == '\0')
4124			{
4125				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4126						     "No key specified\n");
4127				return;
4128			}
4129			*q++ = '\0';
4130			map = stab(p, ST_MAP, ST_FIND);
4131			if (map == NULL)
4132			{
4133				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4134						     "Map named \"%s\" not found\n", p);
4135				return;
4136			}
4137			if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
4138			    !openmap(&(map->s_map)))
4139			{
4140				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4141						     "Map named \"%s\" not open\n", p);
4142				return;
4143			}
4144			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4145					     "map_lookup: %s (%s) ", p, q);
4146			av[0] = q;
4147			av[1] = NULL;
4148			p = (*map->s_map.map_class->map_lookup)
4149					(&map->s_map, q, av, &rcode);
4150			if (p == NULL)
4151				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4152						     "no match (%d)\n",
4153						     rcode);
4154			else
4155				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4156						     "returns %s (%d)\n", p,
4157						     rcode);
4158		}
4159		else if (sm_strcasecmp(&line[1], "try") == 0)
4160		{
4161			MAILER *m;
4162			STAB *st;
4163			auto int rcode = EX_OK;
4164
4165			q = strpbrk(p, " \t");
4166			if (q != NULL)
4167			{
4168				while (isascii(*q) && isspace(*q))
4169					*q++ = '\0';
4170			}
4171			if (q == NULL || *q == '\0')
4172			{
4173				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4174						     "Usage: /try mailer address\n");
4175				return;
4176			}
4177			st = stab(p, ST_MAILER, ST_FIND);
4178			if (st == NULL)
4179			{
4180				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4181						     "Unknown mailer %s\n", p);
4182				return;
4183			}
4184			m = st->s_mailer;
4185			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4186					     "Trying %s %s address %s for mailer %s\n",
4187				     bitset(RF_HEADERADDR, tryflags) ? "header"
4188							: "envelope",
4189				     bitset(RF_SENDERADDR, tryflags) ? "sender"
4190							: "recipient", q, p);
4191			p = remotename(q, m, tryflags, &rcode, CurEnv);
4192			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4193					     "Rcode = %d, addr = %s\n",
4194					     rcode, p == NULL ? "<NULL>" : p);
4195			e->e_to = NULL;
4196		}
4197		else if (sm_strcasecmp(&line[1], "tryflags") == 0)
4198		{
4199			if (*p == '\0')
4200			{
4201				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4202						     "Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
4203				return;
4204			}
4205			for (; *p != '\0'; p++)
4206			{
4207				switch (*p)
4208				{
4209				  case 'H':
4210				  case 'h':
4211					tryflags |= RF_HEADERADDR;
4212					break;
4213
4214				  case 'E':
4215				  case 'e':
4216					tryflags &= ~RF_HEADERADDR;
4217					break;
4218
4219				  case 'S':
4220				  case 's':
4221					tryflags |= RF_SENDERADDR;
4222					break;
4223
4224				  case 'R':
4225				  case 'r':
4226					tryflags &= ~RF_SENDERADDR;
4227					break;
4228				}
4229			}
4230			exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
4231			exbuf[1] = ' ';
4232			exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
4233			exbuf[3] = '\0';
4234			macdefine(&e->e_macro, A_TEMP,
4235				macid("{addr_type}"), exbuf);
4236		}
4237		else if (sm_strcasecmp(&line[1], "parse") == 0)
4238		{
4239			if (*p == '\0')
4240			{
4241				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4242						     "Usage: /parse address\n");
4243				return;
4244			}
4245			q = crackaddr(p, e);
4246			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4247					     "Cracked address = ");
4248			xputs(q);
4249			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4250					     "\nParsing %s %s address\n",
4251					     bitset(RF_HEADERADDR, tryflags) ?
4252							"header" : "envelope",
4253					     bitset(RF_SENDERADDR, tryflags) ?
4254							"sender" : "recipient");
4255			if (parseaddr(p, &a, tryflags, '\0', NULL, e, true)
4256			    == NULL)
4257				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4258						     "Cannot parse\n");
4259			else if (a.q_host != NULL && a.q_host[0] != '\0')
4260				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4261						     "mailer %s, host %s, user %s\n",
4262						     a.q_mailer->m_name,
4263						     a.q_host,
4264						     a.q_user);
4265			else
4266				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4267						     "mailer %s, user %s\n",
4268						     a.q_mailer->m_name,
4269						     a.q_user);
4270			e->e_to = NULL;
4271		}
4272		else
4273		{
4274			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4275					     "Unknown \"/\" command %s\n",
4276					     line);
4277		}
4278		return;
4279	}
4280
4281	for (p = line; isascii(*p) && isspace(*p); p++)
4282		continue;
4283	q = p;
4284	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
4285		p++;
4286	if (*p == '\0')
4287	{
4288		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4289				     "No address!\n");
4290		return;
4291	}
4292	*p = '\0';
4293	if (invalidaddr(p + 1, NULL, true))
4294		return;
4295	do
4296	{
4297		register char **pvp;
4298		char pvpbuf[PSBUFSIZE];
4299
4300		pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
4301			      &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL);
4302		if (pvp == NULL)
4303			continue;
4304		p = q;
4305		while (*p != '\0')
4306		{
4307			int status;
4308
4309			rs = strtorwset(p, NULL, ST_FIND);
4310			if (rs < 0)
4311			{
4312				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4313						     "Undefined ruleset %s\n",
4314						     p);
4315				break;
4316			}
4317			status = REWRITE(pvp, rs, e);
4318			if (status != EX_OK)
4319				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4320						     "== Ruleset %s (%d) status %d\n",
4321						     p, rs, status);
4322			while (*p != '\0' && *p++ != ',')
4323				continue;
4324		}
4325	} while (*(p = delimptr) != '\0');
4326}
4327
4328static void
4329dump_class(s, id)
4330	register STAB *s;
4331	int id;
4332{
4333	if (s->s_symtype != ST_CLASS)
4334		return;
4335	if (bitnset(bitidx(id), s->s_class))
4336		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4337				     "%s\n", s->s_name);
4338}
4339
4340/*
4341**  An exception type used to create QuickAbort exceptions.
4342**  This is my first cut at converting QuickAbort from longjmp to exceptions.
4343**  These exceptions have a single integer argument, which is the argument
4344**  to longjmp in the original code (either 1 or 2).  I don't know the
4345**  significance of 1 vs 2: the calls to setjmp don't care.
4346*/
4347
4348const SM_EXC_TYPE_T EtypeQuickAbort =
4349{
4350	SmExcTypeMagic,
4351	"E:mta.quickabort",
4352	"i",
4353	sm_etype_printf,
4354	"quick abort %0",
4355};
4356