main.c revision 80785
1210284Sjmallett/*
2232812Sjmallett * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.
3215990Sjmallett *	All rights reserved.
4210284Sjmallett * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5210284Sjmallett * Copyright (c) 1988, 1993
6215990Sjmallett *	The Regents of the University of California.  All rights reserved.
7215990Sjmallett *
8215990Sjmallett * By using this file, you agree to the terms and conditions set
9210284Sjmallett * forth in the LICENSE file which can be found at the top level of
10215990Sjmallett * the sendmail distribution.
11215990Sjmallett *
12210284Sjmallett */
13215990Sjmallett
14215990Sjmallett#ifndef lint
15215990Sjmallettstatic char copyright[] =
16215990Sjmallett"@(#) Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers.\n\
17215990Sjmallett	All rights reserved.\n\
18232812Sjmallett     Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
19215990Sjmallett     Copyright (c) 1988, 1993\n\
20215990Sjmallett	The Regents of the University of California.  All rights reserved.\n";
21215990Sjmallett#endif /* ! lint */
22215990Sjmallett
23215990Sjmallett#ifndef lint
24215990Sjmallettstatic char id[] = "@(#)$Id: main.c,v 8.485.4.65 2001/07/20 00:53:00 gshapiro Exp $";
25215990Sjmallett#endif /* ! lint */
26215990Sjmallett
27215990Sjmallett#define	_DEFINE
28215990Sjmallett
29232812Sjmallett#include <sendmail.h>
30215990Sjmallett
31215990Sjmallett
32215990Sjmallett#if NETINET || NETINET6
33215990Sjmallett# include <arpa/inet.h>
34215990Sjmallett#endif /* NETINET || NETINET6 */
35215990Sjmallett
36215990Sjmallettstatic SIGFUNC_DECL	intindebug __P((int));
37215990Sjmallettstatic SIGFUNC_DECL	quiesce __P((int));
38210284Sjmallett#ifdef SIGUSR1
39210284Sjmallettstatic SIGFUNC_DECL	sigusr1 __P((int));
40210284Sjmallett# endif /* SIGUSR1 */
41210284Sjmallettstatic SIGFUNC_DECL	term_daemon __P((int));
42210284Sjmallettstatic void	dump_class __P((STAB *, int));
43210284Sjmallettstatic void	obsolete __P((char **));
44210284Sjmallettstatic void	testmodeline __P((char *, ENVELOPE *));
45215990Sjmallett
46210284Sjmallett/*
47210284Sjmallett**  SENDMAIL -- Post mail to a set of destinations.
48210284Sjmallett**
49210284Sjmallett**	This is the basic mail router.  All user mail programs should
50210284Sjmallett**	call this routine to actually deliver mail.  Sendmail in
51232812Sjmallett**	turn calls a bunch of mail servers that do the real work of
52210284Sjmallett**	delivering the mail.
53210284Sjmallett**
54210284Sjmallett**	Sendmail is driven by settings read in from /etc/mail/sendmail.cf
55210284Sjmallett**	(read by readcf.c).
56210284Sjmallett**
57210284Sjmallett**	Usage:
58210284Sjmallett**		/usr/lib/sendmail [flags] addr ...
59210284Sjmallett**
60210284Sjmallett**		See the associated documentation for details.
61210284Sjmallett**
62210284Sjmallett**	Author:
63210284Sjmallett**		Eric Allman, UCB/INGRES (until 10/81).
64210284Sjmallett**			     Britton-Lee, Inc., purveyors of fine
65210284Sjmallett**				database computers (11/81 - 10/88).
66210284Sjmallett**			     International Computer Science Institute
67210284Sjmallett**				(11/88 - 9/89).
68210284Sjmallett**			     UCB/Mammoth Project (10/89 - 7/95).
69210284Sjmallett**			     InReference, Inc. (8/95 - 1/97).
70210284Sjmallett**			     Sendmail, Inc. (1/98 - present).
71210284Sjmallett**		The support of the my employers is gratefully acknowledged.
72210284Sjmallett**			Few of them (Britton-Lee in particular) have had
73210284Sjmallett**			anything to gain from my involvement in this project.
74210284Sjmallett*/
75210284Sjmallett
76210284Sjmallett
77210284Sjmallettint		NextMailer;	/* "free" index into Mailer struct */
78210284Sjmallettchar		*FullName;	/* sender's full name */
79210284SjmallettENVELOPE	BlankEnvelope;	/* a "blank" envelope */
80210284Sjmallettstatic ENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
81210284SjmallettADDRESS		NullAddress =	/* a null address */
82210284Sjmallett		{ "", "", NULL, "" };
83210284Sjmallettchar		*CommandLineArgs;	/* command line args for pid file */
84210284Sjmallettbool		Warn_Q_option = FALSE;	/* warn about Q option use */
85210284Sjmallettstatic int	MissingFds = 0;	/* bit map of fds missing on startup */
86210284Sjmallett
87210284Sjmallett#ifdef NGROUPS_MAX
88210284SjmallettGIDSET_T	InitialGidSet[NGROUPS_MAX];
89210284Sjmallett#endif /* NGROUPS_MAX */
90210284Sjmallett
91210284Sjmallett#if DAEMON && !SMTP
92210284SjmallettERROR %%%%   Cannot have DAEMON mode without SMTP   %%%% ERROR
93210284Sjmallett#endif /* DAEMON && !SMTP */
94210284Sjmallett#if SMTP && !QUEUE
95210284SjmallettERROR %%%%   Cannot have SMTP mode without QUEUE   %%%% ERROR
96210284Sjmallett#endif /* SMTP && !QUEUE */
97210284Sjmallett
98210284Sjmallett#define MAXCONFIGLEVEL	9	/* highest config version level known */
99210284Sjmallett
100210284Sjmallett#if SASL
101210284Sjmallettstatic sasl_callback_t srvcallbacks[] =
102210284Sjmallett{
103210284Sjmallett	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
104210284Sjmallett	{	SASL_CB_PROXY_POLICY,	&proxy_policy,	NULL	},
105210284Sjmallett	{	SASL_CB_LIST_END,	NULL,		NULL	}
106210284Sjmallett};
107210284Sjmallett
108210284Sjmallett#endif /* SASL */
109210284Sjmallett
110210284Sjmallettint SubmitMode;
111210284Sjmallett
112210284Sjmallettint
113210284Sjmallettmain(argc, argv, envp)
114210284Sjmallett	int argc;
115210284Sjmallett	char **argv;
116210284Sjmallett	char **envp;
117210284Sjmallett{
118210284Sjmallett	register char *p;
119210284Sjmallett	char **av;
120210284Sjmallett	extern char Version[];
121210284Sjmallett	char *ep, *from;
122210284Sjmallett	STAB *st;
123210284Sjmallett	register int i;
124210284Sjmallett	int j;
125210284Sjmallett	int dp;
126210284Sjmallett	bool safecf = TRUE;
127210284Sjmallett	BITMAP256 *p_flags = NULL;	/* daemon flags */
128210284Sjmallett	bool warn_C_flag = FALSE;
129210284Sjmallett	bool auth = TRUE;		/* whether to set e_auth_param */
130210284Sjmallett	char warn_f_flag = '\0';
131210284Sjmallett	bool run_in_foreground = FALSE;	/* -bD mode */
132210284Sjmallett	static bool reenter = FALSE;
133210284Sjmallett	struct passwd *pw;
134210284Sjmallett	struct hostent *hp;
135210284Sjmallett	char *nullserver = NULL;
136210284Sjmallett	char *authinfo = NULL;
137210284Sjmallett	char *sysloglabel = NULL;	/* label for syslog */
138210284Sjmallett	bool forged;
139210284Sjmallett	struct stat traf_st;		/* for TrafficLog FIFO check */
140210284Sjmallett	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
141210284Sjmallett	static char rnamebuf[MAXNAME];	/* holds RealUserName */
142210284Sjmallett	char *emptyenviron[1];
143210284Sjmallett# if STARTTLS
144210284Sjmallett	bool tls_ok;
145210284Sjmallett# endif /* STARTTLS */
146210284Sjmallett	QUEUE_CHAR *new;
147210284Sjmallett	extern int DtableSize;
148210284Sjmallett	extern int optind;
149210284Sjmallett	extern int opterr;
150210284Sjmallett	extern char *optarg;
151210284Sjmallett	extern char **environ;
152210284Sjmallett
153210284Sjmallett	/*
154210284Sjmallett	**  Check to see if we reentered.
155210284Sjmallett	**	This would normally happen if e_putheader or e_putbody
156210284Sjmallett	**	were NULL when invoked.
157210284Sjmallett	*/
158210284Sjmallett
159210284Sjmallett	if (reenter)
160210284Sjmallett	{
161210284Sjmallett		syserr("main: reentered!");
162210284Sjmallett		abort();
163210284Sjmallett	}
164210284Sjmallett	reenter = TRUE;
165210284Sjmallett
166210284Sjmallett	/* avoid null pointer dereferences */
167210284Sjmallett	TermEscape.te_rv_on = TermEscape.te_rv_off = "";
168210284Sjmallett
169210284Sjmallett	/*
170210284Sjmallett	**  Seed the random number generator.
171210284Sjmallett	**  Used for queue file names, picking a queue directory, and
172210284Sjmallett	**  MX randomization.
173210284Sjmallett	*/
174210284Sjmallett
175210284Sjmallett	seed_random();
176210284Sjmallett
177210284Sjmallett	/* do machine-dependent initializations */
178210284Sjmallett	init_md(argc, argv);
179210284Sjmallett
180210284Sjmallett
181210284Sjmallett	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
182210284Sjmallett	DtableSize = getdtsize();
183210284Sjmallett	if (DtableSize > 256)
184210284Sjmallett		DtableSize = 256;
185210284Sjmallett
186210284Sjmallett	/*
187210284Sjmallett	**  Be sure we have enough file descriptors.
188210284Sjmallett	**	But also be sure that 0, 1, & 2 are open.
189210284Sjmallett	*/
190210284Sjmallett
191210284Sjmallett	fill_fd(STDIN_FILENO, NULL);
192210284Sjmallett	fill_fd(STDOUT_FILENO, NULL);
193210284Sjmallett	fill_fd(STDERR_FILENO, NULL);
194210284Sjmallett
195210284Sjmallett	i = DtableSize;
196210284Sjmallett	while (--i > 0)
197210284Sjmallett	{
198210284Sjmallett		if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
199210284Sjmallett			(void) close(i);
200210284Sjmallett	}
201210284Sjmallett	errno = 0;
202210284Sjmallett
203210284Sjmallett#if LOG
204210284Sjmallett#  ifdef LOG_MAIL
205210284Sjmallett	openlog("sendmail", LOG_PID, LOG_MAIL);
206210284Sjmallett#  else /* LOG_MAIL */
207210284Sjmallett	openlog("sendmail", LOG_PID);
208210284Sjmallett#  endif /* LOG_MAIL */
209210284Sjmallett#endif /* LOG */
210210284Sjmallett
211210284Sjmallett	if (MissingFds != 0)
212210284Sjmallett	{
213210284Sjmallett		char mbuf[MAXLINE];
214210284Sjmallett
215210284Sjmallett		mbuf[0] = '\0';
216210284Sjmallett		if (bitset(1 << STDIN_FILENO, MissingFds))
217210284Sjmallett			(void) strlcat(mbuf, ", stdin", sizeof mbuf);
218210284Sjmallett		if (bitset(1 << STDOUT_FILENO, MissingFds))
219210284Sjmallett			(void) strlcat(mbuf, ", stdout", sizeof mbuf);
220210284Sjmallett		if (bitset(1 << STDERR_FILENO, MissingFds))
221210284Sjmallett			(void) strlcat(mbuf, ", stderr", sizeof mbuf);
222210284Sjmallett		syserr("File descriptors missing on startup: %s", &mbuf[2]);
223210284Sjmallett	}
224210284Sjmallett
225210284Sjmallett	/* reset status from syserr() calls for missing file descriptors */
226210284Sjmallett	Errors = 0;
227210284Sjmallett	ExitStat = EX_OK;
228215990Sjmallett
229210284Sjmallett	SubmitMode = SUBMIT_UNKNOWN;
230210284Sjmallett#if XDEBUG
231210284Sjmallett	checkfd012("after openlog");
232210284Sjmallett#endif /* XDEBUG */
233210284Sjmallett
234210284Sjmallett	tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
235210284Sjmallett
236210284Sjmallett#ifdef NGROUPS_MAX
237210284Sjmallett	/* save initial group set for future checks */
238210284Sjmallett	i = getgroups(NGROUPS_MAX, InitialGidSet);
239210284Sjmallett	if (i == 0)
240210284Sjmallett		InitialGidSet[0] = (GID_T) -1;
241210284Sjmallett	while (i < NGROUPS_MAX)
242210284Sjmallett		InitialGidSet[i++] = InitialGidSet[0];
243210284Sjmallett#endif /* NGROUPS_MAX */
244210284Sjmallett
245210284Sjmallett	/* drop group id privileges (RunAsUser not yet set) */
246210284Sjmallett	dp = drop_privileges(FALSE);
247210284Sjmallett	setstat(dp);
248210284Sjmallett
249210284Sjmallett# ifdef SIGUSR1
250210284Sjmallett	/* Only allow root (or non-set-*-ID binaries) to use SIGUSR1 */
251210284Sjmallett	if (getuid() == 0 ||
252210284Sjmallett	    (getuid() == geteuid() && getgid() == getegid()))
253210284Sjmallett	{
254210284Sjmallett		/* arrange to dump state on user-1 signal */
255210284Sjmallett		(void) setsignal(SIGUSR1, sigusr1);
256210284Sjmallett	}
257210284Sjmallett# endif /* SIGUSR1 */
258215990Sjmallett
259210284Sjmallett	/* initialize for setproctitle */
260215990Sjmallett	initsetproctitle(argc, argv, envp);
261215990Sjmallett
262210284Sjmallett	/* Handle any non-getoptable constructions. */
263210284Sjmallett	obsolete(argv);
264210284Sjmallett
265215990Sjmallett	/*
266215990Sjmallett	**  Do a quick prescan of the argument list.
267210284Sjmallett	*/
268210284Sjmallett
269210284Sjmallett
270210284Sjmallett#if defined(__osf__) || defined(_AIX3)
271210284Sjmallett# define OPTIONS	"B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:x"
272210284Sjmallett#endif /* defined(__osf__) || defined(_AIX3) */
273210284Sjmallett#if defined(sony_news)
274210284Sjmallett# define OPTIONS	"B:b:C:cd:E:e:F:f:Gh:IiJ:L:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
275210284Sjmallett#endif /* defined(sony_news) */
276210284Sjmallett#ifndef OPTIONS
277210284Sjmallett# define OPTIONS	"B:b:C:cd:e:F:f:Gh:IiL:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
278210284Sjmallett#endif /* ! OPTIONS */
279210284Sjmallett	opterr = 0;
280210284Sjmallett	while ((j = getopt(argc, argv, OPTIONS)) != -1)
281210284Sjmallett	{
282210284Sjmallett		switch (j)
283210284Sjmallett		{
284210284Sjmallett		  case 'd':
285210284Sjmallett			/* hack attack -- see if should use ANSI mode */
286210284Sjmallett			if (strcmp(optarg, "ANSI") == 0)
287210284Sjmallett			{
288210284Sjmallett				TermEscape.te_rv_on = "\033[7m";
289210284Sjmallett				TermEscape.te_rv_off = "\033[0m";
290210284Sjmallett				break;
291210284Sjmallett			}
292210284Sjmallett			tTflag(optarg);
293210284Sjmallett			setbuf(stdout, (char *) NULL);
294210284Sjmallett			break;
295210284Sjmallett
296210284Sjmallett		  case 'G':	/* relay (gateway) submission */
297210284Sjmallett			SubmitMode |= SUBMIT_MTA;
298210284Sjmallett			break;
299210284Sjmallett
300210284Sjmallett		  case 'L':
301210284Sjmallett			j = min(strlen(optarg), 24) + 1;
302210284Sjmallett			sysloglabel = xalloc(j);
303210284Sjmallett			(void) strlcpy(sysloglabel, optarg, j);
304210284Sjmallett			break;
305210284Sjmallett
306210284Sjmallett		  case 'U':	/* initial (user) submission */
307210284Sjmallett			SubmitMode |= SUBMIT_MSA;
308210284Sjmallett			break;
309210284Sjmallett		}
310210284Sjmallett	}
311210284Sjmallett	opterr = 1;
312210284Sjmallett
313210284Sjmallett#if LOG
314210284Sjmallett	if (sysloglabel != NULL)
315210284Sjmallett	{
316210284Sjmallett		/* Sanitize the string */
317210284Sjmallett		for (p = sysloglabel; *p != '\0'; p++)
318210284Sjmallett		{
319210284Sjmallett			if (!isascii(*p) || !isprint(*p) || *p == '%')
320210284Sjmallett				*p = '*';
321210284Sjmallett		}
322210284Sjmallett		closelog();
323210284Sjmallett#  ifdef LOG_MAIL
324210284Sjmallett		openlog(sysloglabel, LOG_PID, LOG_MAIL);
325210284Sjmallett#  else /* LOG_MAIL */
326210284Sjmallett		openlog(sysloglabel, LOG_PID);
327210284Sjmallett#  endif /* LOG_MAIL */
328210284Sjmallett	}
329210284Sjmallett#endif /* LOG */
330210284Sjmallett
331210284Sjmallett	/* set up the blank envelope */
332210284Sjmallett	BlankEnvelope.e_puthdr = putheader;
333210284Sjmallett	BlankEnvelope.e_putbody = putbody;
334210284Sjmallett	BlankEnvelope.e_xfp = NULL;
335210284Sjmallett	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
336210284Sjmallett	CurEnv = &BlankEnvelope;
337210284Sjmallett	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
338210284Sjmallett
339210284Sjmallett	/*
340210284Sjmallett	**  Set default values for variables.
341210284Sjmallett	**	These cannot be in initialized data space.
342210284Sjmallett	*/
343210284Sjmallett
344210284Sjmallett	setdefaults(&BlankEnvelope);
345210284Sjmallett
346210284Sjmallett	RealUid = getuid();
347210284Sjmallett	RealGid = getgid();
348210284Sjmallett
349210284Sjmallett	pw = sm_getpwuid(RealUid);
350210284Sjmallett	if (pw != NULL)
351210284Sjmallett		(void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
352210284Sjmallett	else
353210284Sjmallett		(void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d",
354210284Sjmallett				(int) RealUid);
355210284Sjmallett
356210284Sjmallett	RealUserName = rnamebuf;
357210284Sjmallett
358210284Sjmallett	if (tTd(0, 101))
359210284Sjmallett	{
360210284Sjmallett		dprintf("Version %s\n", Version);
361210284Sjmallett		finis(FALSE, EX_OK);
362210284Sjmallett	}
363210284Sjmallett
364210284Sjmallett	/*
365210284Sjmallett	**  if running non-setuid binary as non-root, pretend
366210284Sjmallett	**  we are the RunAsUid
367210284Sjmallett	*/
368210284Sjmallett
369210284Sjmallett	if (RealUid != 0 && geteuid() == RealUid)
370210284Sjmallett	{
371210284Sjmallett		if (tTd(47, 1))
372210284Sjmallett			dprintf("Non-setuid binary: RunAsUid = RealUid = %d\n",
373210284Sjmallett				(int)RealUid);
374210284Sjmallett		RunAsUid = RealUid;
375210284Sjmallett	}
376210284Sjmallett	else if (geteuid() != 0)
377210284Sjmallett		RunAsUid = geteuid();
378210284Sjmallett
379210284Sjmallett	if (RealUid != 0 && getegid() == RealGid)
380210284Sjmallett		RunAsGid = RealGid;
381210284Sjmallett
382210284Sjmallett	if (tTd(47, 5))
383210284Sjmallett	{
384210284Sjmallett		dprintf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
385210284Sjmallett			(int)geteuid(), (int)getuid(),
386210284Sjmallett			(int)getegid(), (int)getgid());
387210284Sjmallett		dprintf("main: RunAsUser = %d:%d\n",
388210284Sjmallett			(int)RunAsUid, (int)RunAsGid);
389210284Sjmallett	}
390210284Sjmallett
391210284Sjmallett	/* save command line arguments */
392	j = 0;
393	for (av = argv; *av != NULL; )
394		j += strlen(*av++) + 1;
395	SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
396	CommandLineArgs = xalloc(j);
397	p = CommandLineArgs;
398	for (av = argv, i = 0; *av != NULL; )
399	{
400		int h;
401
402		SaveArgv[i++] = newstr(*av);
403		if (av != argv)
404			*p++ = ' ';
405		(void) strlcpy(p, *av++, j);
406		h = strlen(p);
407		p += h;
408		j -= h + 1;
409	}
410	SaveArgv[i] = NULL;
411
412	if (tTd(0, 1))
413	{
414		int ll;
415		extern char *CompileOptions[];
416
417		dprintf("Version %s\n Compiled with:", Version);
418		av = CompileOptions;
419		ll = 7;
420		while (*av != NULL)
421		{
422			if (ll + strlen(*av) > 63)
423			{
424				dprintf("\n");
425				ll = 0;
426			}
427			if (ll == 0)
428				dprintf("\t\t");
429			else
430				dprintf(" ");
431			dprintf("%s", *av);
432			ll += strlen(*av++) + 1;
433		}
434		dprintf("\n");
435	}
436	if (tTd(0, 10))
437	{
438		int ll;
439		extern char *OsCompileOptions[];
440
441		dprintf("    OS Defines:");
442		av = OsCompileOptions;
443		ll = 7;
444		while (*av != NULL)
445		{
446			if (ll + strlen(*av) > 63)
447			{
448				dprintf("\n");
449				ll = 0;
450			}
451			if (ll == 0)
452				dprintf("\t\t");
453			else
454				dprintf(" ");
455			dprintf("%s", *av);
456			ll += strlen(*av++) + 1;
457		}
458		dprintf("\n");
459#ifdef _PATH_UNIX
460		dprintf("Kernel symbols:\t%s\n", _PATH_UNIX);
461#endif /* _PATH_UNIX */
462		dprintf(" Def Conf file:\t%s\n", getcfname());
463		dprintf("  Def Pid file:\t%s\n", PidFile);
464	}
465
466	InChannel = stdin;
467	OutChannel = stdout;
468
469	/* clear sendmail's environment */
470	ExternalEnviron = environ;
471	emptyenviron[0] = NULL;
472	environ = emptyenviron;
473
474	/*
475	**  restore any original TZ setting until TimeZoneSpec has been
476	**  determined - or early log messages may get bogus time stamps
477	*/
478	if ((p = getextenv("TZ")) != NULL)
479	{
480		char *tz;
481		int tzlen;
482
483		tzlen = strlen(p) + 4;
484		tz = xalloc(tzlen);
485		(void) snprintf(tz, tzlen, "TZ=%s", p);
486		(void) putenv(tz);
487	}
488
489	/* prime the child environment */
490	setuserenv("AGENT", "sendmail");
491	(void) setsignal(SIGPIPE, SIG_IGN);
492
493	OldUmask = umask(022);
494	OpMode = MD_DELIVER;
495	FullName = getextenv("NAME");
496
497	/*
498	**  Initialize name server if it is going to be used.
499	*/
500
501#if NAMED_BIND
502	if (!bitset(RES_INIT, _res.options))
503		(void) res_init();
504
505	/*
506	**  hack to avoid crashes when debugging for the resolver is
507	**  turned on and sfio is used
508	*/
509	if (tTd(8, 8))
510# if !SFIO || SFIO_STDIO_COMPAT
511		_res.options |= RES_DEBUG;
512# else /* !SFIO || SFIO_STDIO_COMPAT */
513		dprintf("RES_DEBUG not available due to SFIO\n");
514# endif /* !SFIO || SFIO_STDIO_COMPAT */
515	else
516		_res.options &= ~RES_DEBUG;
517# ifdef RES_NOALIASES
518	_res.options |= RES_NOALIASES;
519# endif /* RES_NOALIASES */
520	TimeOuts.res_retry[RES_TO_DEFAULT] = _res.retry;
521	TimeOuts.res_retry[RES_TO_FIRST] = _res.retry;
522	TimeOuts.res_retry[RES_TO_NORMAL] = _res.retry;
523	TimeOuts.res_retrans[RES_TO_DEFAULT] = _res.retrans;
524	TimeOuts.res_retrans[RES_TO_FIRST] = _res.retrans;
525	TimeOuts.res_retrans[RES_TO_NORMAL] = _res.retrans;
526#endif /* NAMED_BIND */
527
528	errno = 0;
529	from = NULL;
530
531	/* initialize some macros, etc. */
532	initmacros(CurEnv);
533	init_vendor_macros(CurEnv);
534
535	/* version */
536	define('v', Version, CurEnv);
537
538	/* hostname */
539	hp = myhostname(jbuf, sizeof jbuf);
540	if (jbuf[0] != '\0')
541	{
542		struct	utsname	utsname;
543
544		if (tTd(0, 4))
545			dprintf("canonical name: %s\n", jbuf);
546		define('w', newstr(jbuf), CurEnv);	/* must be new string */
547		define('j', newstr(jbuf), CurEnv);
548		setclass('w', jbuf);
549
550		p = strchr(jbuf, '.');
551		if (p != NULL)
552		{
553			if (p[1] != '\0')
554			{
555				define('m', newstr(&p[1]), CurEnv);
556			}
557			while (p != NULL && strchr(&p[1], '.') != NULL)
558			{
559				*p = '\0';
560				if (tTd(0, 4))
561					dprintf("\ta.k.a.: %s\n", jbuf);
562				setclass('w', jbuf);
563				*p++ = '.';
564				p = strchr(p, '.');
565			}
566		}
567
568		if (uname(&utsname) >= 0)
569			p = utsname.nodename;
570		else
571		{
572			if (tTd(0, 22))
573				dprintf("uname failed (%s)\n",
574					errstring(errno));
575			makelower(jbuf);
576			p = jbuf;
577		}
578		if (tTd(0, 4))
579			dprintf(" UUCP nodename: %s\n", p);
580		p = newstr(p);
581		define('k', p, CurEnv);
582		setclass('k', p);
583		setclass('w', p);
584	}
585	if (hp != NULL)
586	{
587		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
588		{
589			if (tTd(0, 4))
590				dprintf("\ta.k.a.: %s\n", *av);
591			setclass('w', *av);
592		}
593#if NETINET || NETINET6
594		for (i = 0; hp->h_addr_list[i] != NULL; i++)
595		{
596# if NETINET6
597			char *addr;
598			char buf6[INET6_ADDRSTRLEN];
599			struct in6_addr ia6;
600# endif /* NETINET6 */
601# if NETINET
602			struct in_addr ia;
603# endif /* NETINET */
604			char ipbuf[103];
605
606			ipbuf[0] = '\0';
607			switch (hp->h_addrtype)
608			{
609# if NETINET
610			  case AF_INET:
611				if (hp->h_length != INADDRSZ)
612					break;
613
614				memmove(&ia, hp->h_addr_list[i], INADDRSZ);
615				(void) snprintf(ipbuf,	 sizeof ipbuf,
616						"[%.100s]", inet_ntoa(ia));
617				break;
618# endif /* NETINET */
619
620# if NETINET6
621			  case AF_INET6:
622				if (hp->h_length != IN6ADDRSZ)
623					break;
624
625				memmove(&ia6, hp->h_addr_list[i], IN6ADDRSZ);
626				addr = anynet_ntop(&ia6, buf6, sizeof buf6);
627				if (addr != NULL)
628					(void) snprintf(ipbuf, sizeof ipbuf,
629							"[%.100s]", addr);
630				break;
631# endif /* NETINET6 */
632			}
633			if (ipbuf[0] == '\0')
634				break;
635
636			if (tTd(0, 4))
637				dprintf("\ta.k.a.: %s\n", ipbuf);
638			setclass('w', ipbuf);
639		}
640#endif /* NETINET || NETINET6 */
641#if _FFR_FREEHOSTENT && NETINET6
642		freehostent(hp);
643		hp = NULL;
644#endif /* _FFR_FREEHOSTENT && NETINET6 */
645	}
646
647	/* current time */
648	define('b', arpadate((char *) NULL), CurEnv);
649	/* current load average */
650	CurrentLA = sm_getla(CurEnv);
651
652	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
653	QueueLimitSender = (QUEUE_CHAR *) NULL;
654	QueueLimitId = (QUEUE_CHAR *) NULL;
655
656	/*
657	**  Crack argv.
658	*/
659
660	av = argv;
661	p = strrchr(*av, '/');
662	if (p++ == NULL)
663		p = *av;
664	if (strcmp(p, "newaliases") == 0)
665		OpMode = MD_INITALIAS;
666	else if (strcmp(p, "mailq") == 0)
667		OpMode = MD_PRINT;
668	else if (strcmp(p, "smtpd") == 0)
669		OpMode = MD_DAEMON;
670	else if (strcmp(p, "hoststat") == 0)
671		OpMode = MD_HOSTSTAT;
672	else if (strcmp(p, "purgestat") == 0)
673		OpMode = MD_PURGESTAT;
674
675	optind = 1;
676	while ((j = getopt(argc, argv, OPTIONS)) != -1)
677	{
678		switch (j)
679		{
680		  case 'b':	/* operations mode */
681			switch (j = *optarg)
682			{
683			  case MD_DAEMON:
684			  case MD_FGDAEMON:
685#if !DAEMON
686				usrerr("Daemon mode not implemented");
687				ExitStat = EX_USAGE;
688				break;
689#endif /* !DAEMON */
690			  case MD_SMTP:
691#if !SMTP
692				usrerr("I don't speak SMTP");
693				ExitStat = EX_USAGE;
694				break;
695#endif /* !SMTP */
696
697			  case MD_INITALIAS:
698			  case MD_DELIVER:
699			  case MD_VERIFY:
700			  case MD_TEST:
701			  case MD_PRINT:
702			  case MD_HOSTSTAT:
703			  case MD_PURGESTAT:
704			  case MD_ARPAFTP:
705				OpMode = j;
706				break;
707
708			  case MD_FREEZE:
709				usrerr("Frozen configurations unsupported");
710				ExitStat = EX_USAGE;
711				break;
712
713			  default:
714				usrerr("Invalid operation mode %c", j);
715				ExitStat = EX_USAGE;
716				break;
717			}
718			break;
719
720		  case 'B':	/* body type */
721			CurEnv->e_bodytype = newstr(optarg);
722			break;
723
724		  case 'C':	/* select configuration file (already done) */
725			if (RealUid != 0)
726				warn_C_flag = TRUE;
727			ConfFile = newstr(optarg);
728			dp = drop_privileges(TRUE);
729			setstat(dp);
730			safecf = FALSE;
731			break;
732
733		  case 'd':	/* debugging -- already done */
734			break;
735
736		  case 'f':	/* from address */
737		  case 'r':	/* obsolete -f flag */
738			if (from != NULL)
739			{
740				usrerr("More than one \"from\" person");
741				ExitStat = EX_USAGE;
742				break;
743			}
744			from = newstr(denlstring(optarg, TRUE, TRUE));
745			if (strcmp(RealUserName, from) != 0)
746				warn_f_flag = j;
747			break;
748
749		  case 'F':	/* set full name */
750			FullName = newstr(optarg);
751			break;
752
753		  case 'G':	/* relay (gateway) submission */
754			/* already set */
755			break;
756
757		  case 'h':	/* hop count */
758			CurEnv->e_hopcount = (short) strtol(optarg, &ep, 10);
759			if (*ep)
760			{
761				usrerr("Bad hop count (%s)", optarg);
762				ExitStat = EX_USAGE;
763			}
764			break;
765
766		  case 'L':	/* program label */
767			/* already set */
768			break;
769
770		  case 'n':	/* don't alias */
771			NoAlias = TRUE;
772			break;
773
774		  case 'N':	/* delivery status notifications */
775			DefaultNotify |= QHASNOTIFY;
776			define(macid("{dsn_notify}", NULL),
777			       newstr(optarg), CurEnv);
778			if (strcasecmp(optarg, "never") == 0)
779				break;
780			for (p = optarg; p != NULL; optarg = p)
781			{
782				p = strchr(p, ',');
783				if (p != NULL)
784					*p++ = '\0';
785				if (strcasecmp(optarg, "success") == 0)
786					DefaultNotify |= QPINGONSUCCESS;
787				else if (strcasecmp(optarg, "failure") == 0)
788					DefaultNotify |= QPINGONFAILURE;
789				else if (strcasecmp(optarg, "delay") == 0)
790					DefaultNotify |= QPINGONDELAY;
791				else
792				{
793					usrerr("Invalid -N argument");
794					ExitStat = EX_USAGE;
795				}
796			}
797			break;
798
799		  case 'o':	/* set option */
800			setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
801			break;
802
803		  case 'O':	/* set option (long form) */
804			setoption(' ', optarg, FALSE, TRUE, CurEnv);
805			break;
806
807		  case 'p':	/* set protocol */
808			p = strchr(optarg, ':');
809			if (p != NULL)
810			{
811				*p++ = '\0';
812				if (*p != '\0')
813				{
814					ep = xalloc(strlen(p) + 1);
815					cleanstrcpy(ep, p, MAXNAME);
816					define('s', ep, CurEnv);
817				}
818			}
819			if (*optarg != '\0')
820			{
821				ep = xalloc(strlen(optarg) + 1);
822				cleanstrcpy(ep, optarg, MAXNAME);
823				define('r', ep, CurEnv);
824			}
825			break;
826
827		  case 'q':	/* run queue files at intervals */
828#if QUEUE
829			/* sanity check */
830			if (OpMode != MD_DELIVER &&
831			    OpMode != MD_DAEMON &&
832			    OpMode != MD_FGDAEMON &&
833			    OpMode != MD_PRINT &&
834			    OpMode != MD_QUEUERUN)
835			{
836				usrerr("Can not use -q with -b%c", OpMode);
837				ExitStat = EX_USAGE;
838				break;
839			}
840
841			/* don't override -bd, -bD or -bp */
842			if (OpMode == MD_DELIVER)
843				OpMode = MD_QUEUERUN;
844
845			FullName = NULL;
846
847			switch (optarg[0])
848			{
849			  case 'I':
850				new = (QUEUE_CHAR *) xalloc(sizeof *new);
851				new->queue_match = newstr(&optarg[1]);
852				new->queue_next = QueueLimitId;
853				QueueLimitId = new;
854				break;
855
856			  case 'R':
857				new = (QUEUE_CHAR *) xalloc(sizeof *new);
858				new->queue_match = newstr(&optarg[1]);
859				new->queue_next = QueueLimitRecipient;
860				QueueLimitRecipient = new;
861				break;
862
863			  case 'S':
864				new = (QUEUE_CHAR *) xalloc(sizeof *new);
865				new->queue_match = newstr(&optarg[1]);
866				new->queue_next = QueueLimitSender;
867				QueueLimitSender = new;
868				break;
869
870			  default:
871				i = Errors;
872				QueueIntvl = convtime(optarg, 'm');
873
874				/* check for bad conversion */
875				if (i < Errors)
876					ExitStat = EX_USAGE;
877				break;
878			}
879#else /* QUEUE */
880			usrerr("I don't know about queues");
881			ExitStat = EX_USAGE;
882#endif /* QUEUE */
883			break;
884
885		  case 'R':	/* DSN RET: what to return */
886			if (bitset(EF_RET_PARAM, CurEnv->e_flags))
887			{
888				usrerr("Duplicate -R flag");
889				ExitStat = EX_USAGE;
890				break;
891			}
892			CurEnv->e_flags |= EF_RET_PARAM;
893			if (strcasecmp(optarg, "hdrs") == 0)
894				CurEnv->e_flags |= EF_NO_BODY_RETN;
895			else if (strcasecmp(optarg, "full") != 0)
896			{
897				usrerr("Invalid -R value");
898				ExitStat = EX_USAGE;
899			}
900			define(macid("{dsn_ret}", NULL),
901			       newstr(optarg), CurEnv);
902			break;
903
904		  case 't':	/* read recipients from message */
905			GrabTo = TRUE;
906			break;
907
908		  case 'U':	/* initial (user) submission */
909			/* already set */
910			break;
911
912		  case 'V':	/* DSN ENVID: set "original" envelope id */
913			if (!xtextok(optarg))
914			{
915				usrerr("Invalid syntax in -V flag");
916				ExitStat = EX_USAGE;
917			}
918			else
919			{
920				CurEnv->e_envid = newstr(optarg);
921				define(macid("{dsn_envid}", NULL),
922				       newstr(optarg), CurEnv);
923			}
924			break;
925
926		  case 'X':	/* traffic log file */
927			dp = drop_privileges(TRUE);
928			setstat(dp);
929			if (stat(optarg, &traf_st) == 0 &&
930			    S_ISFIFO(traf_st.st_mode))
931				TrafficLogFile = fopen(optarg, "w");
932			else
933				TrafficLogFile = fopen(optarg, "a");
934			if (TrafficLogFile == NULL)
935			{
936				syserr("cannot open %s", optarg);
937				ExitStat = EX_CANTCREAT;
938				break;
939			}
940#if HASSETVBUF
941			(void) setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
942#else /* HASSETVBUF */
943			(void) setlinebuf(TrafficLogFile);
944#endif /* HASSETVBUF */
945			break;
946
947			/* compatibility flags */
948		  case 'c':	/* connect to non-local mailers */
949		  case 'i':	/* don't let dot stop me */
950		  case 'm':	/* send to me too */
951		  case 'T':	/* set timeout interval */
952		  case 'v':	/* give blow-by-blow description */
953			setoption(j, "T", FALSE, TRUE, CurEnv);
954			break;
955
956		  case 'e':	/* error message disposition */
957		  case 'M':	/* define macro */
958			setoption(j, optarg, FALSE, TRUE, CurEnv);
959			break;
960
961		  case 's':	/* save From lines in headers */
962			setoption('f', "T", FALSE, TRUE, CurEnv);
963			break;
964
965#ifdef DBM
966		  case 'I':	/* initialize alias DBM file */
967			OpMode = MD_INITALIAS;
968			break;
969#endif /* DBM */
970
971#if defined(__osf__) || defined(_AIX3)
972		  case 'x':	/* random flag that OSF/1 & AIX mailx passes */
973			break;
974#endif /* defined(__osf__) || defined(_AIX3) */
975#if defined(sony_news)
976		  case 'E':
977		  case 'J':	/* ignore flags for Japanese code conversion
978				   implemented on Sony NEWS */
979			break;
980#endif /* defined(sony_news) */
981
982		  default:
983			finis(TRUE, EX_USAGE);
984			break;
985		}
986	}
987	av += optind;
988
989	if (bitset(SUBMIT_MTA, SubmitMode) &&
990	    bitset(SUBMIT_MSA, SubmitMode))
991	{
992		/* sanity check */
993		errno = 0;	/* reset to avoid bogus error messages */
994		syserr("Cannot use both -G and -U together");
995	}
996	else if (bitset(SUBMIT_MTA, SubmitMode))
997		define(macid("{daemon_flags}", NULL), "CC f", CurEnv);
998	else if (bitset(SUBMIT_MSA, SubmitMode))
999	{
1000		define(macid("{daemon_flags}", NULL), "c u", CurEnv);
1001
1002		/* check for wrong OpMode */
1003		if (OpMode != MD_DELIVER && OpMode != MD_SMTP)
1004		{
1005			errno = 0;	/* reset to avoid bogus error msgs */
1006			syserr("Cannot use -U and -b%c", OpMode);
1007		}
1008	}
1009	else
1010	{
1011#if _FFR_DEFAULT_SUBMIT_TO_MSA
1012		define(macid("{daemon_flags}", NULL), "c u", CurEnv);
1013#else /* _FFR_DEFAULT_SUBMIT_TO_MSA */
1014		/* EMPTY */
1015#endif /* _FFR_DEFAULT_SUBMIT_TO_MSA */
1016	}
1017
1018	/*
1019	**  Do basic initialization.
1020	**	Read system control file.
1021	**	Extract special fields for local use.
1022	*/
1023
1024	/* set up ${opMode} for use in config file */
1025	{
1026		char mbuf[2];
1027
1028		mbuf[0] = OpMode;
1029		mbuf[1] = '\0';
1030		define(MID_OPMODE, newstr(mbuf), CurEnv);
1031	}
1032
1033#if XDEBUG
1034	checkfd012("before readcf");
1035#endif /* XDEBUG */
1036	vendor_pre_defaults(CurEnv);
1037
1038	readcf(getcfname(), safecf, CurEnv);
1039	ConfigFileRead = TRUE;
1040	vendor_post_defaults(CurEnv);
1041
1042	/* Remove the ability for a normal user to send signals */
1043	if (RealUid != 0 &&
1044	    RealUid != geteuid())
1045	{
1046		uid_t new_uid = geteuid();
1047
1048#if HASSETREUID
1049		/*
1050		**  Since we can differentiate between uid and euid,
1051		**  make the uid a different user so the real user
1052		**  can't send signals.  However, it doesn't need to be
1053		**  root (euid has root).
1054		*/
1055
1056		if (new_uid == 0)
1057			new_uid = DefUid;
1058		if (tTd(47, 5))
1059			dprintf("Changing real uid to %d\n", (int) new_uid);
1060		if (setreuid(new_uid, geteuid()) < 0)
1061		{
1062			syserr("main: setreuid(%d, %d) failed",
1063			       (int) new_uid, (int) geteuid());
1064			finis(FALSE, EX_OSERR);
1065			/* NOTREACHED */
1066		}
1067		if (tTd(47, 10))
1068			dprintf("Now running as e/ruid %d:%d\n",
1069				(int) geteuid(), (int) getuid());
1070#else /* HASSETREUID */
1071		/*
1072		**  Have to change both effective and real so need to
1073		**  change them both to effective to keep privs.
1074		*/
1075
1076		if (tTd(47, 5))
1077			dprintf("Changing uid to %d\n", (int) new_uid);
1078		if (setuid(new_uid) < 0)
1079		{
1080			syserr("main: setuid(%d) failed", (int) new_uid);
1081			finis(FALSE, EX_OSERR);
1082			/* NOTREACHED */
1083		}
1084		if (tTd(47, 10))
1085			dprintf("Now running as e/ruid %d:%d\n",
1086				(int) geteuid(), (int) getuid());
1087#endif /* HASSETREUID */
1088	}
1089
1090	/* set up the basic signal handlers */
1091	if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
1092		(void) setsignal(SIGINT, intsig);
1093	(void) setsignal(SIGTERM, intsig);
1094
1095	/* Enforce use of local time (null string overrides this) */
1096	if (TimeZoneSpec == NULL)
1097		unsetenv("TZ");
1098	else if (TimeZoneSpec[0] != '\0')
1099		setuserenv("TZ", TimeZoneSpec);
1100	else
1101		setuserenv("TZ", NULL);
1102	tzset();
1103
1104	/* avoid denial-of-service attacks */
1105	resetlimits();
1106
1107	if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
1108	{
1109		/* drop privileges -- daemon mode done after socket/bind */
1110		dp = drop_privileges(FALSE);
1111		setstat(dp);
1112	}
1113
1114#if NAMED_BIND
1115	_res.retry = TimeOuts.res_retry[RES_TO_DEFAULT];
1116	_res.retrans = TimeOuts.res_retrans[RES_TO_DEFAULT];
1117#endif /* NAMED_BIND */
1118
1119	/*
1120	**  Find our real host name for future logging.
1121	*/
1122
1123	authinfo = getauthinfo(STDIN_FILENO, &forged);
1124	define('_', authinfo, CurEnv);
1125
1126	/* suppress error printing if errors mailed back or whatever */
1127	if (CurEnv->e_errormode != EM_PRINT)
1128		HoldErrs = TRUE;
1129
1130	/* set up the $=m class now, after .cf has a chance to redefine $m */
1131	expand("\201m", jbuf, sizeof jbuf, CurEnv);
1132	if (jbuf[0] != '\0')
1133		setclass('m', jbuf);
1134
1135	/* probe interfaces and locate any additional names */
1136	if (!DontProbeInterfaces)
1137		load_if_names();
1138
1139	if (tTd(0, 1))
1140	{
1141		dprintf("\n============ SYSTEM IDENTITY (after readcf) ============");
1142		dprintf("\n      (short domain name) $w = ");
1143		xputs(macvalue('w', CurEnv));
1144		dprintf("\n  (canonical domain name) $j = ");
1145		xputs(macvalue('j', CurEnv));
1146		dprintf("\n         (subdomain name) $m = ");
1147		xputs(macvalue('m', CurEnv));
1148		dprintf("\n              (node name) $k = ");
1149		xputs(macvalue('k', CurEnv));
1150		dprintf("\n========================================================\n\n");
1151	}
1152
1153	/*
1154	**  Do more command line checking -- these are things that
1155	**  have to modify the results of reading the config file.
1156	*/
1157
1158	/* process authorization warnings from command line */
1159	if (warn_C_flag)
1160		auth_warning(CurEnv, "Processed by %s with -C %s",
1161			RealUserName, ConfFile);
1162	if (Warn_Q_option && !wordinclass(RealUserName, 't'))
1163		auth_warning(CurEnv, "Processed from queue %s", QueueDir);
1164
1165	/* check body type for legality */
1166	if (CurEnv->e_bodytype == NULL)
1167		/* EMPTY */
1168		/* nothing */ ;
1169	else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
1170		SevenBitInput = TRUE;
1171	else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
1172		SevenBitInput = FALSE;
1173	else
1174	{
1175		usrerr("Illegal body type %s", CurEnv->e_bodytype);
1176		CurEnv->e_bodytype = NULL;
1177	}
1178
1179	/* tweak default DSN notifications */
1180	if (DefaultNotify == 0)
1181		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1182
1183	/* be sure we don't pick up bogus HOSTALIASES environment variable */
1184	if (OpMode == MD_QUEUERUN && RealUid != 0)
1185		(void) unsetenv("HOSTALIASES");
1186
1187	/* check for sane configuration level */
1188	if (ConfigLevel > MAXCONFIGLEVEL)
1189	{
1190		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
1191			ConfigLevel, Version, MAXCONFIGLEVEL);
1192	}
1193
1194	/* need MCI cache to have persistence */
1195	if (HostStatDir != NULL && MaxMciCache == 0)
1196	{
1197		HostStatDir = NULL;
1198		printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
1199	}
1200
1201	/* need HostStatusDir in order to have SingleThreadDelivery */
1202	if (SingleThreadDelivery && HostStatDir == NULL)
1203	{
1204		SingleThreadDelivery = FALSE;
1205		printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n");
1206	}
1207
1208	/* check for permissions */
1209	if ((OpMode == MD_DAEMON ||
1210	     OpMode == MD_FGDAEMON ||
1211	     OpMode == MD_PURGESTAT) &&
1212	    RealUid != 0 &&
1213	    RealUid != TrustedUid)
1214	{
1215		if (LogLevel > 1)
1216			sm_syslog(LOG_ALERT, NOQID,
1217				  "user %d attempted to %s",
1218				  RealUid,
1219				  OpMode != MD_PURGESTAT ? "run daemon"
1220							 : "purge host status");
1221		usrerr("Permission denied");
1222		finis(FALSE, EX_USAGE);
1223	}
1224	if (OpMode == MD_INITALIAS &&
1225	    RealUid != 0 &&
1226	    RealUid != TrustedUid &&
1227	    !wordinclass(RealUserName, 't'))
1228	{
1229		if (LogLevel > 1)
1230			sm_syslog(LOG_ALERT, NOQID,
1231				  "user %d attempted to rebuild the alias map",
1232				  RealUid);
1233		usrerr("Permission denied");
1234		finis(FALSE, EX_USAGE);
1235	}
1236
1237	if (MeToo)
1238		BlankEnvelope.e_flags |= EF_METOO;
1239
1240	switch (OpMode)
1241	{
1242	  case MD_TEST:
1243		/* don't have persistent host status in test mode */
1244		HostStatDir = NULL;
1245		if (Verbose == 0)
1246			Verbose = 2;
1247		CurEnv->e_errormode = EM_PRINT;
1248		HoldErrs = FALSE;
1249		break;
1250
1251	  case MD_VERIFY:
1252		CurEnv->e_errormode = EM_PRINT;
1253		HoldErrs = FALSE;
1254
1255		/* arrange to exit cleanly on hangup signal */
1256		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1257			(void) setsignal(SIGHUP, intsig);
1258		break;
1259
1260	  case MD_FGDAEMON:
1261		run_in_foreground = TRUE;
1262		OpMode = MD_DAEMON;
1263		/* FALLTHROUGH */
1264
1265	  case MD_DAEMON:
1266		vendor_daemon_setup(CurEnv);
1267
1268		/* remove things that don't make sense in daemon mode */
1269		FullName = NULL;
1270		GrabTo = FALSE;
1271
1272		/* arrange to restart on hangup signal */
1273		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
1274			sm_syslog(LOG_WARNING, NOQID,
1275				  "daemon invoked without full pathname; kill -1 won't work");
1276		(void) setsignal(SIGTERM, term_daemon);
1277		break;
1278
1279	  case MD_INITALIAS:
1280		Verbose = 2;
1281		CurEnv->e_errormode = EM_PRINT;
1282		HoldErrs = FALSE;
1283		/* FALLTHROUGH */
1284
1285	  default:
1286		/* arrange to exit cleanly on hangup signal */
1287		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1288			(void) setsignal(SIGHUP, intsig);
1289		break;
1290	}
1291
1292	/* special considerations for FullName */
1293	if (FullName != NULL)
1294	{
1295		char *full = NULL;
1296
1297		/* full names can't have newlines */
1298		if (strchr(FullName, '\n') != NULL)
1299		{
1300			full = newstr(denlstring(FullName, TRUE, TRUE));
1301			FullName = full;
1302		}
1303
1304		/* check for characters that may have to be quoted */
1305		if (!rfc822_string(FullName))
1306		{
1307			/*
1308			**  Quote a full name with special characters
1309			**  as a comment so crackaddr() doesn't destroy
1310			**  the name portion of the address.
1311			*/
1312
1313			FullName = addquotes(FullName);
1314			if (full != NULL)
1315				sm_free(full);
1316		}
1317	}
1318
1319	/* do heuristic mode adjustment */
1320	if (Verbose)
1321	{
1322		/* turn off noconnect option */
1323		setoption('c', "F", TRUE, FALSE, CurEnv);
1324
1325		/* turn on interactive delivery */
1326		setoption('d', "", TRUE, FALSE, CurEnv);
1327	}
1328
1329#ifdef VENDOR_CODE
1330	/* check for vendor mismatch */
1331	if (VendorCode != VENDOR_CODE)
1332	{
1333		message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
1334			getvendor(VENDOR_CODE), getvendor(VendorCode));
1335	}
1336#endif /* VENDOR_CODE */
1337
1338	/* check for out of date configuration level */
1339	if (ConfigLevel < MAXCONFIGLEVEL)
1340	{
1341		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
1342			Version, MAXCONFIGLEVEL, ConfigLevel);
1343	}
1344
1345	if (ConfigLevel < 3)
1346		UseErrorsTo = TRUE;
1347
1348	/* set options that were previous macros */
1349	if (SmtpGreeting == NULL)
1350	{
1351		if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
1352			SmtpGreeting = newstr(p);
1353		else
1354			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
1355	}
1356	if (UnixFromLine == NULL)
1357	{
1358		if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
1359			UnixFromLine = newstr(p);
1360		else
1361			UnixFromLine = "From \201g  \201d";
1362	}
1363	SmtpError[0] = '\0';
1364
1365	/* our name for SMTP codes */
1366	expand("\201j", jbuf, sizeof jbuf, CurEnv);
1367	if (jbuf[0] == '\0')
1368		MyHostName = newstr("localhost");
1369	else
1370		MyHostName = jbuf;
1371	if (strchr(MyHostName, '.') == NULL)
1372		message("WARNING: local host name (%s) is not qualified; fix $j in config file",
1373			MyHostName);
1374
1375	/* make certain that this name is part of the $=w class */
1376	setclass('w', MyHostName);
1377
1378	/* the indices of built-in mailers */
1379	st = stab("local", ST_MAILER, ST_FIND);
1380	if (st != NULL)
1381		LocalMailer = st->s_mailer;
1382	else if (OpMode != MD_TEST || !warn_C_flag)
1383		syserr("No local mailer defined");
1384
1385	st = stab("prog", ST_MAILER, ST_FIND);
1386	if (st == NULL)
1387		syserr("No prog mailer defined");
1388	else
1389	{
1390		ProgMailer = st->s_mailer;
1391		clrbitn(M_MUSER, ProgMailer->m_flags);
1392	}
1393
1394	st = stab("*file*", ST_MAILER, ST_FIND);
1395	if (st == NULL)
1396		syserr("No *file* mailer defined");
1397	else
1398	{
1399		FileMailer = st->s_mailer;
1400		clrbitn(M_MUSER, FileMailer->m_flags);
1401	}
1402
1403	st = stab("*include*", ST_MAILER, ST_FIND);
1404	if (st == NULL)
1405		syserr("No *include* mailer defined");
1406	else
1407		InclMailer = st->s_mailer;
1408
1409	if (ConfigLevel < 6)
1410	{
1411		/* heuristic tweaking of local mailer for back compat */
1412		if (LocalMailer != NULL)
1413		{
1414			setbitn(M_ALIASABLE, LocalMailer->m_flags);
1415			setbitn(M_HASPWENT, LocalMailer->m_flags);
1416			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
1417			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
1418			setbitn(M_CHECKPROG, LocalMailer->m_flags);
1419			setbitn(M_CHECKFILE, LocalMailer->m_flags);
1420			setbitn(M_CHECKUDB, LocalMailer->m_flags);
1421		}
1422		if (ProgMailer != NULL)
1423			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
1424		if (FileMailer != NULL)
1425			setbitn(M_RUNASRCPT, FileMailer->m_flags);
1426	}
1427	if (ConfigLevel < 7)
1428	{
1429		if (LocalMailer != NULL)
1430			setbitn(M_VRFY250, LocalMailer->m_flags);
1431		if (ProgMailer != NULL)
1432			setbitn(M_VRFY250, ProgMailer->m_flags);
1433		if (FileMailer != NULL)
1434			setbitn(M_VRFY250, FileMailer->m_flags);
1435	}
1436
1437	/* MIME Content-Types that cannot be transfer encoded */
1438	setclass('n', "multipart/signed");
1439
1440	/* MIME message/xxx subtypes that can be treated as messages */
1441	setclass('s', "rfc822");
1442
1443	/* MIME Content-Transfer-Encodings that can be encoded */
1444	setclass('e', "7bit");
1445	setclass('e', "8bit");
1446	setclass('e', "binary");
1447
1448#ifdef USE_B_CLASS
1449	/* MIME Content-Types that should be treated as binary */
1450	setclass('b', "image");
1451	setclass('b', "audio");
1452	setclass('b', "video");
1453	setclass('b', "application/octet-stream");
1454#endif /* USE_B_CLASS */
1455
1456	/* MIME headers which have fields to check for overflow */
1457	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition");
1458	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type");
1459
1460	/* MIME headers to check for length overflow */
1461	setclass(macid("{checkMIMETextHeaders}", NULL), "content-description");
1462
1463	/* MIME headers to check for overflow and rebalance */
1464	setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition");
1465	setclass(macid("{checkMIMEHeaders}", NULL), "content-id");
1466	setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding");
1467	setclass(macid("{checkMIMEHeaders}", NULL), "content-type");
1468	setclass(macid("{checkMIMEHeaders}", NULL), "mime-version");
1469
1470	/* Macros to save in the qf file -- don't remove any */
1471	setclass(macid("{persistentMacros}", NULL), "r");
1472	setclass(macid("{persistentMacros}", NULL), "s");
1473	setclass(macid("{persistentMacros}", NULL), "_");
1474	setclass(macid("{persistentMacros}", NULL), "{if_addr}");
1475	setclass(macid("{persistentMacros}", NULL), "{daemon_flags}");
1476	setclass(macid("{persistentMacros}", NULL), "{client_flags}");
1477
1478	/* operate in queue directory */
1479	if (QueueDir == NULL)
1480	{
1481		if (OpMode != MD_TEST)
1482		{
1483			syserr("QueueDirectory (Q) option must be set");
1484			ExitStat = EX_CONFIG;
1485		}
1486	}
1487	else
1488	{
1489		/*
1490		**  If multiple queues wildcarded, use one for
1491		**  the daemon's home. Note that this preconditions
1492		**  a wildcarded QueueDir to a real pathname.
1493		*/
1494
1495		if (OpMode != MD_TEST)
1496			multiqueue_cache();
1497	}
1498
1499	/* check host status directory for validity */
1500	if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
1501	{
1502		/* cannot use this value */
1503		if (tTd(0, 2))
1504			dprintf("Cannot use HostStatusDirectory = %s: %s\n",
1505				HostStatDir, errstring(errno));
1506		HostStatDir = NULL;
1507	}
1508
1509#if QUEUE
1510	if (OpMode == MD_QUEUERUN && RealUid != 0 &&
1511	    bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
1512	{
1513		struct stat stbuf;
1514
1515		/* check to see if we own the queue directory */
1516		if (stat(".", &stbuf) < 0)
1517			syserr("main: cannot stat %s", QueueDir);
1518		if (stbuf.st_uid != RealUid)
1519		{
1520			/* nope, really a botch */
1521			usrerr("You do not have permission to process the queue");
1522			finis(FALSE, EX_NOPERM);
1523		}
1524	}
1525#endif /* QUEUE */
1526
1527#if _FFR_MILTER
1528	/* sanity checks on milter filters */
1529	if (OpMode == MD_DAEMON || OpMode == MD_SMTP)
1530		milter_parse_list(InputFilterList, InputFilters, MAXFILTERS);
1531#endif /* _FFR_MILTER */
1532
1533
1534	/* if we've had errors so far, exit now */
1535	if (ExitStat != EX_OK && OpMode != MD_TEST)
1536		finis(FALSE, ExitStat);
1537
1538#if XDEBUG
1539	checkfd012("before main() initmaps");
1540#endif /* XDEBUG */
1541
1542	/*
1543	**  Do operation-mode-dependent initialization.
1544	*/
1545
1546	switch (OpMode)
1547	{
1548	  case MD_PRINT:
1549		/* print the queue */
1550#if QUEUE
1551		dropenvelope(CurEnv, TRUE);
1552		(void) setsignal(SIGPIPE, quiesce);
1553		printqueue();
1554		finis(FALSE, EX_OK);
1555#else /* QUEUE */
1556		usrerr("No queue to print");
1557		finis(FALSE, EX_UNAVAILABLE);
1558#endif /* QUEUE */
1559		break;
1560
1561	  case MD_HOSTSTAT:
1562		(void) setsignal(SIGPIPE, quiesce);
1563		(void) mci_traverse_persistent(mci_print_persistent, NULL);
1564		finis(FALSE, EX_OK);
1565		break;
1566
1567	  case MD_PURGESTAT:
1568		(void) mci_traverse_persistent(mci_purge_persistent, NULL);
1569		finis(FALSE, EX_OK);
1570		break;
1571
1572	  case MD_INITALIAS:
1573		/* initialize maps */
1574		initmaps();
1575		finis(FALSE, ExitStat);
1576		break;
1577
1578	  case MD_SMTP:
1579	  case MD_DAEMON:
1580		/* reset DSN parameters */
1581		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1582		define(macid("{dsn_notify}", NULL), NULL, CurEnv);
1583		CurEnv->e_envid = NULL;
1584		define(macid("{dsn_envid}", NULL), NULL, CurEnv);
1585		CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
1586		define(macid("{dsn_ret}", NULL), NULL, CurEnv);
1587
1588		/* don't open maps for daemon -- done below in child */
1589		break;
1590	}
1591
1592	if (tTd(0, 15))
1593	{
1594		/* print configuration table (or at least part of it) */
1595		if (tTd(0, 90))
1596			printrules();
1597		for (i = 0; i < MAXMAILERS; i++)
1598		{
1599			if (Mailer[i] != NULL)
1600				printmailer(Mailer[i]);
1601		}
1602	}
1603
1604	/*
1605	**  Switch to the main envelope.
1606	*/
1607
1608	CurEnv = newenvelope(&MainEnvelope, CurEnv);
1609	MainEnvelope.e_flags = BlankEnvelope.e_flags;
1610
1611	/*
1612	**  If test mode, read addresses from stdin and process.
1613	*/
1614
1615	if (OpMode == MD_TEST)
1616	{
1617		char buf[MAXLINE];
1618
1619#if _FFR_TESTMODE_DROP_PRIVS
1620		dp = drop_privileges(TRUE);
1621		if (dp != EX_OK)
1622		{
1623			CurEnv->e_id = NULL;
1624			finis(TRUE, dp);
1625		}
1626#endif /* _FFR_TESTMODE_DROP_PRIVS */
1627
1628		if (isatty(fileno(stdin)))
1629			Verbose = 2;
1630
1631		if (Verbose)
1632		{
1633			printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
1634			printf("Enter <ruleset> <address>\n");
1635		}
1636		if (setjmp(TopFrame) > 0)
1637			printf("\n");
1638		(void) setsignal(SIGINT, intindebug);
1639		for (;;)
1640		{
1641			if (Verbose == 2)
1642				printf("> ");
1643			(void) fflush(stdout);
1644			if (fgets(buf, sizeof buf, stdin) == NULL)
1645				testmodeline("/quit", CurEnv);
1646			p = strchr(buf, '\n');
1647			if (p != NULL)
1648				*p = '\0';
1649			if (Verbose < 2)
1650				printf("> %s\n", buf);
1651			testmodeline(buf, CurEnv);
1652		}
1653	}
1654
1655#if SMTP
1656# if STARTTLS
1657	tls_ok = init_tls_library();
1658# endif /* STARTTLS */
1659#endif /* SMTP */
1660
1661#if QUEUE
1662	/*
1663	**  If collecting stuff from the queue, go start doing that.
1664	*/
1665
1666	if (OpMode == MD_QUEUERUN && QueueIntvl == 0)
1667	{
1668# if SMTP
1669#  if STARTTLS
1670		if (tls_ok
1671		   )
1672		{
1673			/* init TLS for client, ignore result for now */
1674			(void) initclttls();
1675		}
1676#  endif /* STARTTLS */
1677# endif /* SMTP */
1678		(void) runqueue(FALSE, Verbose);
1679		finis(TRUE, ExitStat);
1680	}
1681#endif /* QUEUE */
1682
1683# if SASL
1684	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
1685	{
1686		/* give a syserr or just disable AUTH ? */
1687		if ((i = sasl_server_init(srvcallbacks, "Sendmail")) != SASL_OK)
1688			syserr("!sasl_server_init failed! [%s]",
1689			       sasl_errstring(i, NULL, NULL));
1690	}
1691# endif /* SASL */
1692
1693	/*
1694	**  If a daemon, wait for a request.
1695	**	getrequests will always return in a child.
1696	**	If we should also be processing the queue, start
1697	**		doing it in background.
1698	**	We check for any errors that might have happened
1699	**		during startup.
1700	*/
1701
1702	if (OpMode == MD_DAEMON || QueueIntvl != 0)
1703	{
1704		char dtype[200];
1705
1706		if (!run_in_foreground && !tTd(99, 100))
1707		{
1708			/* put us in background */
1709			i = fork();
1710			if (i < 0)
1711				syserr("daemon: cannot fork");
1712			if (i != 0)
1713				finis(FALSE, EX_OK);
1714
1715			/* disconnect from our controlling tty */
1716			disconnect(2, CurEnv);
1717		}
1718
1719		dtype[0] = '\0';
1720		if (OpMode == MD_DAEMON)
1721			(void) strlcat(dtype, "+SMTP", sizeof dtype);
1722		if (QueueIntvl != 0)
1723		{
1724			(void) strlcat(dtype, "+queueing@", sizeof dtype);
1725			(void) strlcat(dtype, pintvl(QueueIntvl, TRUE),
1726				       sizeof dtype);
1727		}
1728		if (tTd(0, 1))
1729			(void) strlcat(dtype, "+debugging", sizeof dtype);
1730
1731		sm_syslog(LOG_INFO, NOQID,
1732			  "starting daemon (%s): %s", Version, dtype + 1);
1733#ifdef XLA
1734		xla_create_file();
1735#endif /* XLA */
1736
1737		/* save daemon type in a macro for possible PidFile use */
1738		define(macid("{daemon_info}", NULL),
1739		       newstr(dtype + 1), &BlankEnvelope);
1740
1741		/* save queue interval in a macro for possible PidFile use */
1742		define(macid("{queue_interval}", NULL),
1743		       newstr(pintvl(QueueIntvl, TRUE)), CurEnv);
1744
1745#if QUEUE
1746		if (QueueIntvl != 0)
1747		{
1748			(void) runqueue(TRUE, FALSE);
1749			if (OpMode != MD_DAEMON)
1750			{
1751				/* write the pid to file */
1752				log_sendmail_pid(CurEnv);
1753				(void) setsignal(SIGTERM, term_daemon);
1754				for (;;)
1755				{
1756					(void) pause();
1757					if (ShutdownRequest != NULL)
1758						shutdown_daemon();
1759					else if (DoQueueRun)
1760						(void) runqueue(TRUE, FALSE);
1761				}
1762			}
1763		}
1764#endif /* QUEUE */
1765		dropenvelope(CurEnv, TRUE);
1766
1767#if DAEMON
1768# if STARTTLS
1769		/* init TLS for server, ignore result for now */
1770		(void) initsrvtls();
1771# endif /* STARTTLS */
1772		p_flags = getrequests(CurEnv);
1773
1774		/* drop privileges */
1775		(void) drop_privileges(FALSE);
1776
1777		/* at this point we are in a child: reset state */
1778		(void) newenvelope(CurEnv, CurEnv);
1779
1780		/*
1781		**  Get authentication data
1782		*/
1783
1784		authinfo = getauthinfo(fileno(InChannel), &forged);
1785		define('_', authinfo, &BlankEnvelope);
1786#endif /* DAEMON */
1787	}
1788
1789	if (LogLevel > 9)
1790	{
1791		/* log connection information */
1792		sm_syslog(LOG_INFO, NULL, "connect from %.100s", authinfo);
1793	}
1794
1795#if SMTP
1796	/*
1797	**  If running SMTP protocol, start collecting and executing
1798	**  commands.  This will never return.
1799	*/
1800
1801	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
1802	{
1803		char pbuf[20];
1804
1805		/*
1806		**  Save some macros for check_* rulesets.
1807		*/
1808
1809		if (forged)
1810		{
1811			char ipbuf[103];
1812
1813			(void) snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
1814					anynet_ntoa(&RealHostAddr));
1815			define(macid("{client_name}", NULL),
1816			       newstr(ipbuf), &BlankEnvelope);
1817			define(macid("{client_resolve}", NULL),
1818			       "FORGED", &BlankEnvelope);
1819		}
1820		else
1821			define(macid("{client_name}", NULL), RealHostName,
1822			       &BlankEnvelope);
1823		define(macid("{client_addr}", NULL),
1824		       newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
1825		(void)sm_getla(&BlankEnvelope);
1826
1827		switch(RealHostAddr.sa.sa_family)
1828		{
1829# if NETINET
1830		  case AF_INET:
1831			(void) snprintf(pbuf, sizeof pbuf, "%d",
1832					RealHostAddr.sin.sin_port);
1833			break;
1834# endif /* NETINET */
1835# if NETINET6
1836		  case AF_INET6:
1837			(void) snprintf(pbuf, sizeof pbuf, "%d",
1838					RealHostAddr.sin6.sin6_port);
1839			break;
1840# endif /* NETINET6 */
1841		  default:
1842			(void) snprintf(pbuf, sizeof pbuf, "0");
1843			break;
1844		}
1845		define(macid("{client_port}", NULL),
1846		       newstr(pbuf), &BlankEnvelope);
1847
1848		if (OpMode == MD_DAEMON)
1849		{
1850			/* validate the connection */
1851			HoldErrs = TRUE;
1852			nullserver = validate_connection(&RealHostAddr,
1853							 RealHostName, CurEnv);
1854			HoldErrs = FALSE;
1855		}
1856		else if (p_flags == NULL)
1857		{
1858			p_flags = (BITMAP256 *) xalloc(sizeof *p_flags);
1859			clrbitmap(p_flags);
1860		}
1861# if STARTTLS
1862		if (OpMode == MD_SMTP)
1863			(void) initsrvtls();
1864# endif /* STARTTLS */
1865
1866
1867		smtp(nullserver, *p_flags, CurEnv);
1868	}
1869#endif /* SMTP */
1870
1871	clearenvelope(CurEnv, FALSE);
1872	if (OpMode == MD_VERIFY)
1873	{
1874		set_delivery_mode(SM_VERIFY, CurEnv);
1875		PostMasterCopy = NULL;
1876	}
1877	else
1878	{
1879		/* interactive -- all errors are global */
1880		CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
1881	}
1882
1883	/*
1884	**  Do basic system initialization and set the sender
1885	*/
1886
1887	initsys(CurEnv);
1888	define(macid("{ntries}", NULL), "0", CurEnv);
1889	setsender(from, CurEnv, NULL, '\0', FALSE);
1890	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't') &&
1891	    (!bitnset(M_LOCALMAILER, CurEnv->e_from.q_mailer->m_flags) ||
1892	     strcmp(CurEnv->e_from.q_user, RealUserName) != 0))
1893	{
1894		auth_warning(CurEnv, "%s set sender to %s using -%c",
1895			RealUserName, from, warn_f_flag);
1896#if SASL
1897		auth = FALSE;
1898#endif /* SASL */
1899	}
1900	if (auth)
1901	{
1902		char *fv;
1903
1904		/* set the initial sender for AUTH= to $f@$j */
1905		fv = macvalue('f', CurEnv);
1906		if (fv == NULL || *fv == '\0')
1907			CurEnv->e_auth_param = NULL;
1908		else
1909		{
1910			if (strchr(fv, '@') == NULL)
1911			{
1912				i = strlen(fv) + strlen(macvalue('j', CurEnv))
1913				    + 2;
1914				p = xalloc(i);
1915				(void) snprintf(p, i, "%s@%s", fv,
1916						macvalue('j', CurEnv));
1917			}
1918			else
1919				p = newstr(fv);
1920			CurEnv->e_auth_param = newstr(xtextify(p, "="));
1921		}
1922	}
1923	if (macvalue('s', CurEnv) == NULL)
1924		define('s', RealHostName, CurEnv);
1925
1926	if (*av == NULL && !GrabTo)
1927	{
1928		CurEnv->e_to = NULL;
1929		CurEnv->e_flags |= EF_GLOBALERRS;
1930		HoldErrs = FALSE;
1931		SuperSafe = FALSE;
1932		usrerr("Recipient names must be specified");
1933
1934		/* collect body for UUCP return */
1935		if (OpMode != MD_VERIFY)
1936			collect(InChannel, FALSE, NULL, CurEnv);
1937		finis(TRUE, EX_USAGE);
1938	}
1939
1940	/*
1941	**  Scan argv and deliver the message to everyone.
1942	*/
1943
1944	sendtoargv(av, CurEnv);
1945
1946	/* if we have had errors sofar, arrange a meaningful exit stat */
1947	if (Errors > 0 && ExitStat == EX_OK)
1948		ExitStat = EX_USAGE;
1949
1950#if _FFR_FIX_DASHT
1951	/*
1952	**  If using -t, force not sending to argv recipients, even
1953	**  if they are mentioned in the headers.
1954	*/
1955
1956	if (GrabTo)
1957	{
1958		ADDRESS *q;
1959
1960		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
1961			q->q_state = QS_REMOVED;
1962	}
1963#endif /* _FFR_FIX_DASHT */
1964
1965	/*
1966	**  Read the input mail.
1967	*/
1968
1969	CurEnv->e_to = NULL;
1970	if (OpMode != MD_VERIFY || GrabTo)
1971	{
1972		int savederrors = Errors;
1973		long savedflags = CurEnv->e_flags & EF_FATALERRS;
1974
1975		CurEnv->e_flags |= EF_GLOBALERRS;
1976		CurEnv->e_flags &= ~EF_FATALERRS;
1977		Errors = 0;
1978		buffer_errors();
1979		collect(InChannel, FALSE, NULL, CurEnv);
1980
1981		/* header checks failed */
1982		if (Errors > 0)
1983		{
1984			/* Log who the mail would have gone to */
1985			if (LogLevel > 8 && CurEnv->e_message != NULL &&
1986			    !GrabTo)
1987			{
1988				ADDRESS *a;
1989
1990				for (a = CurEnv->e_sendqueue;
1991				     a != NULL;
1992				     a = a->q_next)
1993				{
1994					if (!QS_IS_UNDELIVERED(a->q_state))
1995						continue;
1996
1997					CurEnv->e_to = a->q_paddr;
1998					logdelivery(NULL, NULL, NULL,
1999						    CurEnv->e_message,
2000						    NULL, (time_t) 0, CurEnv);
2001				}
2002				CurEnv->e_to = NULL;
2003			}
2004			flush_errors(TRUE);
2005			finis(TRUE, ExitStat);
2006			/* NOTREACHED */
2007			return -1;
2008		}
2009
2010		/* bail out if message too large */
2011		if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
2012		{
2013			finis(TRUE, ExitStat != EX_OK ? ExitStat : EX_DATAERR);
2014			/* NOTREACHED */
2015			return -1;
2016		}
2017		Errors = savederrors;
2018		CurEnv->e_flags |= savedflags;
2019	}
2020	errno = 0;
2021
2022	if (tTd(1, 1))
2023		dprintf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
2024
2025	/*
2026	**  Actually send everything.
2027	**	If verifying, just ack.
2028	*/
2029
2030	CurEnv->e_from.q_state = QS_SENDER;
2031	if (tTd(1, 5))
2032	{
2033		dprintf("main: QS_SENDER ");
2034		printaddr(&CurEnv->e_from, FALSE);
2035	}
2036	CurEnv->e_to = NULL;
2037	CurrentLA = sm_getla(CurEnv);
2038	GrabTo = FALSE;
2039#if NAMED_BIND
2040	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
2041	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
2042#endif /* NAMED_BIND */
2043	sendall(CurEnv, SM_DEFAULT);
2044
2045	/*
2046	**  All done.
2047	**	Don't send return error message if in VERIFY mode.
2048	*/
2049
2050	finis(TRUE, ExitStat);
2051	/* NOTREACHED */
2052	return ExitStat;
2053}
2054/*
2055**  QUIESCE -- signal handler for SIGPIPE
2056**
2057**	Parameters:
2058**		sig -- incoming signal.
2059**
2060**	Returns:
2061**		none.
2062**
2063**	Side Effects:
2064**		Sets StopRequest which should cause the mailq/hoststatus
2065**		display to stop.
2066**
2067**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2068**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2069**		DOING.
2070*/
2071
2072/* ARGSUSED */
2073static SIGFUNC_DECL
2074quiesce(sig)
2075	int sig;
2076{
2077	int save_errno = errno;
2078
2079	FIX_SYSV_SIGNAL(sig, quiesce);
2080	StopRequest = TRUE;
2081	errno = save_errno;
2082	return SIGFUNC_RETURN;
2083}
2084/*
2085**  STOP_SENDMAIL -- Stop the running program
2086**
2087**	Parameters:
2088**		none.
2089**
2090**	Returns:
2091**		none.
2092**
2093**	Side Effects:
2094**		exits.
2095*/
2096
2097void
2098stop_sendmail()
2099{
2100	/* reset uid for process accounting */
2101	endpwent();
2102	(void) setuid(RealUid);
2103	exit(EX_OK);
2104}
2105
2106/*
2107**  INTINDEBUG -- signal handler for SIGINT in -bt mode
2108**
2109**	Parameters:
2110**		sig -- incoming signal.
2111**
2112**	Returns:
2113**		none.
2114**
2115**	Side Effects:
2116**		longjmps back to test mode loop.
2117**
2118**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2119**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2120**		DOING.
2121**
2122**	XXX: More work is needed for this signal handler.
2123*/
2124
2125/* ARGSUSED */
2126static SIGFUNC_DECL
2127intindebug(sig)
2128	int sig;
2129{
2130	int save_errno = errno;
2131
2132	FIX_SYSV_SIGNAL(sig, intindebug);
2133	errno = save_errno;
2134	CHECK_CRITICAL(sig);
2135
2136	errno = save_errno;
2137	longjmp(TopFrame, 1);
2138	return SIGFUNC_RETURN;
2139}
2140/*
2141**  FINIS -- Clean up and exit.
2142**
2143**	Parameters:
2144**		drop -- whether or not to drop CurEnv envelope
2145**		exitstat -- exit status to use for exit() call
2146**
2147**	Returns:
2148**		never
2149**
2150**	Side Effects:
2151**		exits sendmail
2152*/
2153
2154void
2155finis(drop, exitstat)
2156	bool drop;
2157	volatile int exitstat;
2158{
2159	/* Still want to process new timeouts added below */
2160	clear_events();
2161	(void) releasesignal(SIGALRM);
2162
2163	if (tTd(2, 1))
2164	{
2165		dprintf("\n====finis: stat %d e_id=%s e_flags=",
2166			exitstat,
2167			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
2168		printenvflags(CurEnv);
2169	}
2170	if (tTd(2, 9))
2171		printopenfds(FALSE);
2172
2173	/* if we fail in finis(), just exit */
2174	if (setjmp(TopFrame) != 0)
2175	{
2176		/* failed -- just give it up */
2177		goto forceexit;
2178	}
2179
2180	/* clean up temp files */
2181	CurEnv->e_to = NULL;
2182	if (drop)
2183	{
2184		if (CurEnv->e_id != NULL)
2185			dropenvelope(CurEnv, TRUE);
2186		else
2187			poststats(StatFile);
2188	}
2189
2190	/* flush any cached connections */
2191	mci_flush(TRUE, NULL);
2192
2193	/* close maps belonging to this pid */
2194	closemaps();
2195
2196#if USERDB
2197	/* close UserDatabase */
2198	_udbx_close();
2199#endif /* USERDB */
2200
2201#ifdef XLA
2202	/* clean up extended load average stuff */
2203	xla_all_end();
2204#endif /* XLA */
2205
2206	/* and exit */
2207  forceexit:
2208	if (LogLevel > 78)
2209		sm_syslog(LOG_DEBUG, CurEnv->e_id,
2210			  "finis, pid=%d",
2211			  (int) getpid());
2212	if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
2213		exitstat = EX_OK;
2214
2215	sync_queue_time();
2216
2217	/* reset uid for process accounting */
2218	endpwent();
2219	(void) setuid(RealUid);
2220	exit(exitstat);
2221}
2222/*
2223**  TERM_DEAMON -- SIGTERM handler for the daemon
2224**
2225**	Parameters:
2226**		sig -- signal number.
2227**
2228**	Returns:
2229**		none.
2230**
2231**	Side Effects:
2232**		Sets ShutdownRequest which will hopefully trigger
2233**		the daemon to exit.
2234**
2235**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2236**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2237**		DOING.
2238*/
2239
2240/* ARGSUSED */
2241static SIGFUNC_DECL
2242term_daemon(sig)
2243	int sig;
2244{
2245	int save_errno = errno;
2246
2247	FIX_SYSV_SIGNAL(sig, term_daemon);
2248	ShutdownRequest = "signal";
2249	errno = save_errno;
2250	return SIGFUNC_RETURN;
2251}
2252/*
2253**  SHUTDOWN_DAEMON -- Performs a clean shutdown of the daemon
2254**
2255**	Parameters:
2256**		none.
2257**
2258**	Returns:
2259**		none.
2260**
2261**	Side Effects:
2262**		closes control socket, exits.
2263*/
2264
2265void
2266shutdown_daemon()
2267{
2268	char *reason;
2269
2270	allsignals(TRUE);
2271
2272	reason = ShutdownRequest;
2273	ShutdownRequest = NULL;
2274	PendingSignal = 0;
2275
2276	if (LogLevel > 79)
2277		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt (%s)",
2278			  reason == NULL ? "implicit call" : reason);
2279
2280	FileName = NULL;
2281	closecontrolsocket(TRUE);
2282#ifdef XLA
2283	xla_all_end();
2284#endif /* XLA */
2285
2286	finis(FALSE, EX_OK);
2287}
2288/*
2289**  INTSIG -- clean up on interrupt
2290**
2291**	This just arranges to exit.  It pessimizes in that it
2292**	may resend a message.
2293**
2294**	Parameters:
2295**		none.
2296**
2297**	Returns:
2298**		none.
2299**
2300**	Side Effects:
2301**		Unlocks the current job.
2302**
2303**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2304**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2305**		DOING.
2306**
2307**		XXX: More work is needed for this signal handler.
2308*/
2309
2310/* ARGSUSED */
2311SIGFUNC_DECL
2312intsig(sig)
2313	int sig;
2314{
2315	bool drop = FALSE;
2316	int save_errno = errno;
2317
2318	FIX_SYSV_SIGNAL(sig, intsig);
2319	errno = save_errno;
2320	CHECK_CRITICAL(sig);
2321	allsignals(TRUE);
2322	if (sig != 0 && LogLevel > 79)
2323		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
2324	FileName = NULL;
2325
2326	/* Clean-up on aborted stdin message submission */
2327	if (CurEnv->e_id != NULL &&
2328	    (OpMode == MD_SMTP ||
2329	     OpMode == MD_DELIVER ||
2330	     OpMode == MD_ARPAFTP))
2331	{
2332		register ADDRESS *q;
2333
2334		/* don't return an error indication */
2335		CurEnv->e_to = NULL;
2336		CurEnv->e_flags &= ~EF_FATALERRS;
2337		CurEnv->e_flags |= EF_CLRQUEUE;
2338
2339		/*
2340		**  Spin through the addresses and
2341		**  mark them dead to prevent bounces
2342		*/
2343
2344		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
2345			q->q_state = QS_DONTSEND;
2346
2347		/* and don't try to deliver the partial message either */
2348		if (InChild)
2349			ExitStat = EX_QUIT;
2350
2351		drop = TRUE;
2352	}
2353	else if (OpMode != MD_TEST)
2354		unlockqueue(CurEnv);
2355
2356	finis(drop, EX_OK);
2357}
2358/*
2359**  INITMACROS -- initialize the macro system
2360**
2361**	This just involves defining some macros that are actually
2362**	used internally as metasymbols to be themselves.
2363**
2364**	Parameters:
2365**		none.
2366**
2367**	Returns:
2368**		none.
2369**
2370**	Side Effects:
2371**		initializes several macros to be themselves.
2372*/
2373
2374struct metamac	MetaMacros[] =
2375{
2376	/* LHS pattern matching characters */
2377	{ '*', MATCHZANY },	{ '+', MATCHANY },	{ '-', MATCHONE },
2378	{ '=', MATCHCLASS },	{ '~', MATCHNCLASS },
2379
2380	/* these are RHS metasymbols */
2381	{ '#', CANONNET },	{ '@', CANONHOST },	{ ':', CANONUSER },
2382	{ '>', CALLSUBR },
2383
2384	/* the conditional operations */
2385	{ '?', CONDIF },	{ '|', CONDELSE },	{ '.', CONDFI },
2386
2387	/* the hostname lookup characters */
2388	{ '[', HOSTBEGIN },	{ ']', HOSTEND },
2389	{ '(', LOOKUPBEGIN },	{ ')', LOOKUPEND },
2390
2391	/* miscellaneous control characters */
2392	{ '&', MACRODEXPAND },
2393
2394	{ '\0', '\0' }
2395};
2396
2397#define MACBINDING(name, mid) \
2398		stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
2399		MacroName[mid] = name;
2400
2401void
2402initmacros(e)
2403	register ENVELOPE *e;
2404{
2405	register struct metamac *m;
2406	register int c;
2407	char buf[5];
2408	extern char *MacroName[MAXMACROID + 1];
2409
2410	for (m = MetaMacros; m->metaname != '\0'; m++)
2411	{
2412		buf[0] = m->metaval;
2413		buf[1] = '\0';
2414		define(m->metaname, newstr(buf), e);
2415	}
2416	buf[0] = MATCHREPL;
2417	buf[2] = '\0';
2418	for (c = '0'; c <= '9'; c++)
2419	{
2420		buf[1] = c;
2421		define(c, newstr(buf), e);
2422	}
2423
2424	/* set defaults for some macros sendmail will use later */
2425	define('n', "MAILER-DAEMON", e);
2426
2427	/* set up external names for some internal macros */
2428	MACBINDING("opMode", MID_OPMODE);
2429	/*XXX should probably add equivalents for all short macros here XXX*/
2430}
2431/*
2432**  DISCONNECT -- remove our connection with any foreground process
2433**
2434**	Parameters:
2435**		droplev -- how "deeply" we should drop the line.
2436**			0 -- ignore signals, mail back errors, make sure
2437**			     output goes to stdout.
2438**			1 -- also, make stdout go to /dev/null.
2439**			2 -- also, disconnect from controlling terminal
2440**			     (only for daemon mode).
2441**		e -- the current envelope.
2442**
2443**	Returns:
2444**		none
2445**
2446**	Side Effects:
2447**		Trys to insure that we are immune to vagaries of
2448**		the controlling tty.
2449*/
2450
2451void
2452disconnect(droplev, e)
2453	int droplev;
2454	register ENVELOPE *e;
2455{
2456	int fd;
2457
2458	if (tTd(52, 1))
2459		dprintf("disconnect: In %d Out %d, e=%lx\n",
2460			fileno(InChannel), fileno(OutChannel), (u_long) e);
2461	if (tTd(52, 100))
2462	{
2463		dprintf("don't\n");
2464		return;
2465	}
2466	if (LogLevel > 93)
2467		sm_syslog(LOG_DEBUG, e->e_id,
2468			  "disconnect level %d",
2469			  droplev);
2470
2471	/* be sure we don't get nasty signals */
2472	(void) setsignal(SIGINT, SIG_IGN);
2473	(void) setsignal(SIGQUIT, SIG_IGN);
2474
2475	/* we can't communicate with our caller, so.... */
2476	HoldErrs = TRUE;
2477	CurEnv->e_errormode = EM_MAIL;
2478	Verbose = 0;
2479	DisConnected = TRUE;
2480
2481	/* all input from /dev/null */
2482	if (InChannel != stdin)
2483	{
2484		(void) fclose(InChannel);
2485		InChannel = stdin;
2486	}
2487	if (freopen("/dev/null", "r", stdin) == NULL)
2488		sm_syslog(LOG_ERR, e->e_id,
2489			  "disconnect: freopen(\"/dev/null\") failed: %s",
2490			  errstring(errno));
2491
2492	/* output to the transcript */
2493	if (OutChannel != stdout)
2494	{
2495		(void) fclose(OutChannel);
2496		OutChannel = stdout;
2497	}
2498	if (droplev > 0)
2499	{
2500		fd = open("/dev/null", O_WRONLY, 0666);
2501		if (fd == -1)
2502			sm_syslog(LOG_ERR, e->e_id,
2503				  "disconnect: open(\"/dev/null\") failed: %s",
2504				  errstring(errno));
2505		(void) fflush(stdout);
2506		(void) dup2(fd, STDOUT_FILENO);
2507		(void) dup2(fd, STDERR_FILENO);
2508		(void) close(fd);
2509	}
2510
2511	/* drop our controlling TTY completely if possible */
2512	if (droplev > 1)
2513	{
2514		(void) setsid();
2515		errno = 0;
2516	}
2517
2518#if XDEBUG
2519	checkfd012("disconnect");
2520#endif /* XDEBUG */
2521
2522	if (LogLevel > 71)
2523		sm_syslog(LOG_DEBUG, e->e_id,
2524			  "in background, pid=%d",
2525			  (int) getpid());
2526
2527	errno = 0;
2528}
2529
2530static void
2531obsolete(argv)
2532	char *argv[];
2533{
2534	register char *ap;
2535	register char *op;
2536
2537	while ((ap = *++argv) != NULL)
2538	{
2539		/* Return if "--" or not an option of any form. */
2540		if (ap[0] != '-' || ap[1] == '-')
2541			return;
2542
2543		/* skip over options that do have a value */
2544		op = strchr(OPTIONS, ap[1]);
2545		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
2546		    ap[1] != 'd' &&
2547#if defined(sony_news)
2548		    ap[1] != 'E' && ap[1] != 'J' &&
2549#endif /* defined(sony_news) */
2550		    argv[1] != NULL && argv[1][0] != '-')
2551		{
2552			argv++;
2553			continue;
2554		}
2555
2556		/* If -C doesn't have an argument, use sendmail.cf. */
2557#define	__DEFPATH	"sendmail.cf"
2558		if (ap[1] == 'C' && ap[2] == '\0')
2559		{
2560			*argv = xalloc(sizeof(__DEFPATH) + 2);
2561			(void) snprintf(argv[0], sizeof(__DEFPATH) + 2, "-C%s",
2562					__DEFPATH);
2563		}
2564
2565		/* If -q doesn't have an argument, run it once. */
2566		if (ap[1] == 'q' && ap[2] == '\0')
2567			*argv = "-q0";
2568
2569		/* if -d doesn't have an argument, use 0-99.1 */
2570		if (ap[1] == 'd' && ap[2] == '\0')
2571			*argv = "-d0-99.1";
2572
2573#if defined(sony_news)
2574		/* if -E doesn't have an argument, use -EC */
2575		if (ap[1] == 'E' && ap[2] == '\0')
2576			*argv = "-EC";
2577
2578		/* if -J doesn't have an argument, use -JJ */
2579		if (ap[1] == 'J' && ap[2] == '\0')
2580			*argv = "-JJ";
2581#endif /* defined(sony_news) */
2582	}
2583}
2584/*
2585**  AUTH_WARNING -- specify authorization warning
2586**
2587**	Parameters:
2588**		e -- the current envelope.
2589**		msg -- the text of the message.
2590**		args -- arguments to the message.
2591**
2592**	Returns:
2593**		none.
2594*/
2595
2596void
2597#ifdef __STDC__
2598auth_warning(register ENVELOPE *e, const char *msg, ...)
2599#else /* __STDC__ */
2600auth_warning(e, msg, va_alist)
2601	register ENVELOPE *e;
2602	const char *msg;
2603	va_dcl
2604#endif /* __STDC__ */
2605{
2606	char buf[MAXLINE];
2607	VA_LOCAL_DECL
2608
2609	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
2610	{
2611		register char *p;
2612		static char hostbuf[48];
2613
2614		if (hostbuf[0] == '\0')
2615		{
2616			struct hostent *hp;
2617
2618			hp = myhostname(hostbuf, sizeof hostbuf);
2619#if _FFR_FREEHOSTENT && NETINET6
2620			if (hp != NULL)
2621			{
2622				freehostent(hp);
2623				hp = NULL;
2624			}
2625#endif /* _FFR_FREEHOSTENT && NETINET6 */
2626		}
2627
2628		(void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
2629		p = &buf[strlen(buf)];
2630		VA_START(msg);
2631		vsnprintf(p, SPACELEFT(buf, p), msg, ap);
2632		VA_END;
2633		addheader("X-Authentication-Warning", buf, 0, &e->e_header);
2634		if (LogLevel > 3)
2635			sm_syslog(LOG_INFO, e->e_id,
2636				  "Authentication-Warning: %.400s",
2637				  buf);
2638	}
2639}
2640/*
2641**  GETEXTENV -- get from external environment
2642**
2643**	Parameters:
2644**		envar -- the name of the variable to retrieve
2645**
2646**	Returns:
2647**		The value, if any.
2648*/
2649
2650char *
2651getextenv(envar)
2652	const char *envar;
2653{
2654	char **envp;
2655	int l;
2656
2657	l = strlen(envar);
2658	for (envp = ExternalEnviron; *envp != NULL; envp++)
2659	{
2660		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
2661			return &(*envp)[l + 1];
2662	}
2663	return NULL;
2664}
2665/*
2666**  SETUSERENV -- set an environment in the propogated environment
2667**
2668**	Parameters:
2669**		envar -- the name of the environment variable.
2670**		value -- the value to which it should be set.  If
2671**			null, this is extracted from the incoming
2672**			environment.  If that is not set, the call
2673**			to setuserenv is ignored.
2674**
2675**	Returns:
2676**		none.
2677*/
2678
2679void
2680setuserenv(envar, value)
2681	const char *envar;
2682	const char *value;
2683{
2684	int i, l;
2685	char **evp = UserEnviron;
2686	char *p;
2687
2688	if (value == NULL)
2689	{
2690		value = getextenv(envar);
2691		if (value == NULL)
2692			return;
2693	}
2694
2695	i = strlen(envar) + 1;
2696	l = strlen(value) + i + 1;
2697	p = (char *) xalloc(l);
2698	(void) snprintf(p, l, "%s=%s", envar, value);
2699
2700	while (*evp != NULL && strncmp(*evp, p, i) != 0)
2701		evp++;
2702	if (*evp != NULL)
2703	{
2704		*evp++ = p;
2705	}
2706	else if (evp < &UserEnviron[MAXUSERENVIRON])
2707	{
2708		*evp++ = p;
2709		*evp = NULL;
2710	}
2711
2712	/* make sure it is in our environment as well */
2713	if (putenv(p) < 0)
2714		syserr("setuserenv: putenv(%s) failed", p);
2715}
2716/*
2717**  DUMPSTATE -- dump state
2718**
2719**	For debugging.
2720*/
2721
2722void
2723dumpstate(when)
2724	char *when;
2725{
2726	register char *j = macvalue('j', CurEnv);
2727	int rs;
2728	extern int NextMacroId;
2729
2730	sm_syslog(LOG_DEBUG, CurEnv->e_id,
2731		  "--- dumping state on %s: $j = %s ---",
2732		  when,
2733		  j == NULL ? "<NULL>" : j);
2734	if (j != NULL)
2735	{
2736		if (!wordinclass(j, 'w'))
2737			sm_syslog(LOG_DEBUG, CurEnv->e_id,
2738				  "*** $j not in $=w ***");
2739	}
2740	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
2741	sm_syslog(LOG_DEBUG, CurEnv->e_id, "NextMacroId = %d (Max %d)\n",
2742		  NextMacroId, MAXMACROID);
2743	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
2744	printopenfds(TRUE);
2745	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
2746	mci_dump_all(TRUE);
2747	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
2748	if (rs > 0)
2749	{
2750		int status;
2751		register char **pvp;
2752		char *pv[MAXATOM + 1];
2753
2754		pv[0] = NULL;
2755		status = rewrite(pv, rs, 0, CurEnv);
2756		sm_syslog(LOG_DEBUG, CurEnv->e_id,
2757			  "--- ruleset debug_dumpstate returns stat %d, pv: ---",
2758			  status);
2759		for (pvp = pv; *pvp != NULL; pvp++)
2760			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
2761	}
2762	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
2763}
2764#ifdef SIGUSR1
2765/*
2766**  SIGUSR1 -- Signal a request to dump state.
2767**
2768**	Parameters:
2769**		sig -- calling signal.
2770**
2771**	Returns:
2772**		none.
2773**
2774**	NOTE:	THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
2775**		ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
2776**		DOING.
2777**
2778**		XXX: More work is needed for this signal handler.
2779*/
2780
2781/* ARGSUSED */
2782static SIGFUNC_DECL
2783sigusr1(sig)
2784	int sig;
2785{
2786	int save_errno = errno;
2787
2788	FIX_SYSV_SIGNAL(sig, sigusr1);
2789	errno = save_errno;
2790	CHECK_CRITICAL(sig);
2791	dumpstate("user signal");
2792	errno = save_errno;
2793	return SIGFUNC_RETURN;
2794}
2795# endif /* SIGUSR1 */
2796/*
2797**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
2798**
2799**	Parameters:
2800**		to_real_uid -- if set, drop to the real uid instead
2801**			of the RunAsUser.
2802**
2803**	Returns:
2804**		EX_OSERR if the setuid failed.
2805**		EX_OK otherwise.
2806*/
2807
2808int
2809drop_privileges(to_real_uid)
2810	bool to_real_uid;
2811{
2812	int rval = EX_OK;
2813	GIDSET_T emptygidset[1];
2814
2815	if (tTd(47, 1))
2816		dprintf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
2817			(int)to_real_uid, (int)RealUid,
2818			(int)RealGid, (int)RunAsUid, (int)RunAsGid);
2819
2820	if (to_real_uid)
2821	{
2822		RunAsUserName = RealUserName;
2823		RunAsUid = RealUid;
2824		RunAsGid = RealGid;
2825	}
2826
2827	/* make sure no one can grab open descriptors for secret files */
2828	endpwent();
2829
2830	/* reset group permissions; these can be set later */
2831	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
2832	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
2833	{
2834		syserr("drop_privileges: setgroups(1, %d) failed",
2835		       (int)emptygidset[0]);
2836		rval = EX_OSERR;
2837	}
2838
2839	/* reset primary group and user id */
2840	if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
2841	{
2842		syserr("drop_privileges: setgid(%d) failed", (int)RunAsGid);
2843		rval = EX_OSERR;
2844	}
2845	if (to_real_uid || RunAsUid != 0)
2846	{
2847		uid_t euid = geteuid();
2848
2849		if (setuid(RunAsUid) < 0)
2850		{
2851			syserr("drop_privileges: setuid(%d) failed",
2852			       (int)RunAsUid);
2853			rval = EX_OSERR;
2854		}
2855		else if (RunAsUid != 0 && setuid(0) == 0)
2856		{
2857			/*
2858			**  Believe it or not, the Linux capability model
2859			**  allows a non-root process to override setuid()
2860			**  on a process running as root and prevent that
2861			**  process from dropping privileges.
2862			*/
2863
2864			syserr("drop_privileges: setuid(0) succeeded (when it should not)");
2865			rval = EX_OSERR;
2866		}
2867		else if (RunAsUid != euid && setuid(euid) == 0)
2868		{
2869			/*
2870			**  Some operating systems will keep the saved-uid
2871			**  if a non-root effective-uid calls setuid(real-uid)
2872			**  making it possible to set it back again later.
2873			*/
2874
2875			syserr("drop_privileges: Unable to drop non-root set-user-id privileges");
2876			rval = EX_OSERR;
2877		}
2878	}
2879	if (tTd(47, 5))
2880	{
2881		dprintf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
2882			(int)geteuid(), (int)getuid(),
2883			(int)getegid(), (int)getgid());
2884		dprintf("drop_privileges: RunAsUser = %d:%d\n",
2885			(int)RunAsUid, (int)RunAsGid);
2886		if (tTd(47, 10))
2887			dprintf("drop_privileges: rval = %d\n", rval);
2888	}
2889	return rval;
2890}
2891/*
2892**  FILL_FD -- make sure a file descriptor has been properly allocated
2893**
2894**	Used to make sure that stdin/out/err are allocated on startup
2895**
2896**	Parameters:
2897**		fd -- the file descriptor to be filled.
2898**		where -- a string used for logging.  If NULL, this is
2899**			being called on startup, and logging should
2900**			not be done.
2901**
2902**	Returns:
2903**		none
2904*/
2905
2906void
2907fill_fd(fd, where)
2908	int fd;
2909	char *where;
2910{
2911	int i;
2912	struct stat stbuf;
2913
2914	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
2915		return;
2916
2917	if (where != NULL)
2918		syserr("fill_fd: %s: fd %d not open", where, fd);
2919	else
2920		MissingFds |= 1 << fd;
2921	i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
2922	if (i < 0)
2923	{
2924		syserr("!fill_fd: %s: cannot open /dev/null",
2925			where == NULL ? "startup" : where);
2926	}
2927	if (fd != i)
2928	{
2929		(void) dup2(i, fd);
2930		(void) close(i);
2931	}
2932}
2933/*
2934**  TESTMODELINE -- process a test mode input line
2935**
2936**	Parameters:
2937**		line -- the input line.
2938**		e -- the current environment.
2939**	Syntax:
2940**		#  a comment
2941**		.X process X as a configuration line
2942**		=X dump a configuration item (such as mailers)
2943**		$X dump a macro or class
2944**		/X try an activity
2945**		X  normal process through rule set X
2946*/
2947
2948static void
2949testmodeline(line, e)
2950	char *line;
2951	ENVELOPE *e;
2952{
2953	register char *p;
2954	char *q;
2955	auto char *delimptr;
2956	int mid;
2957	int i, rs;
2958	STAB *map;
2959	char **s;
2960	struct rewrite *rw;
2961	ADDRESS a;
2962	static int tryflags = RF_COPYNONE;
2963	char exbuf[MAXLINE];
2964	extern u_char TokTypeNoC[];
2965
2966#if _FFR_ADDR_TYPE
2967	define(macid("{addr_type}", NULL), "e r", e);
2968#endif /* _FFR_ADDR_TYPE */
2969
2970	/* skip leading spaces */
2971	while (*line == ' ')
2972		line++;
2973
2974	switch (line[0])
2975	{
2976	  case '#':
2977	  case '\0':
2978		return;
2979
2980	  case '?':
2981		help("-bt", e);
2982		return;
2983
2984	  case '.':		/* config-style settings */
2985		switch (line[1])
2986		{
2987		  case 'D':
2988			mid = macid(&line[2], &delimptr);
2989			if (mid == 0)
2990				return;
2991			translate_dollars(delimptr);
2992			define(mid, newstr(delimptr), e);
2993			break;
2994
2995		  case 'C':
2996			if (line[2] == '\0')	/* not to call syserr() */
2997				return;
2998
2999			mid = macid(&line[2], &delimptr);
3000			if (mid == 0)
3001				return;
3002			translate_dollars(delimptr);
3003			expand(delimptr, exbuf, sizeof exbuf, e);
3004			p = exbuf;
3005			while (*p != '\0')
3006			{
3007				register char *wd;
3008				char delim;
3009
3010				while (*p != '\0' && isascii(*p) && isspace(*p))
3011					p++;
3012				wd = p;
3013				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
3014					p++;
3015				delim = *p;
3016				*p = '\0';
3017				if (wd[0] != '\0')
3018					setclass(mid, wd);
3019				*p = delim;
3020			}
3021			break;
3022
3023		  case '\0':
3024			printf("Usage: .[DC]macro value(s)\n");
3025			break;
3026
3027		  default:
3028			printf("Unknown \".\" command %s\n", line);
3029			break;
3030		}
3031		return;
3032
3033	  case '=':		/* config-style settings */
3034		switch (line[1])
3035		{
3036		  case 'S':		/* dump rule set */
3037			rs = strtorwset(&line[2], NULL, ST_FIND);
3038			if (rs < 0)
3039			{
3040				printf("Undefined ruleset %s\n", &line[2]);
3041				return;
3042			}
3043			rw = RewriteRules[rs];
3044			if (rw == NULL)
3045				return;
3046			do
3047			{
3048				(void) putchar('R');
3049				s = rw->r_lhs;
3050				while (*s != NULL)
3051				{
3052					xputs(*s++);
3053					(void) putchar(' ');
3054				}
3055				(void) putchar('\t');
3056				(void) putchar('\t');
3057				s = rw->r_rhs;
3058				while (*s != NULL)
3059				{
3060					xputs(*s++);
3061					(void) putchar(' ');
3062				}
3063				(void) putchar('\n');
3064			} while ((rw = rw->r_next) != NULL);
3065			break;
3066
3067		  case 'M':
3068			for (i = 0; i < MAXMAILERS; i++)
3069			{
3070				if (Mailer[i] != NULL)
3071					printmailer(Mailer[i]);
3072			}
3073			break;
3074
3075		  case '\0':
3076			printf("Usage: =Sruleset or =M\n");
3077			break;
3078
3079		  default:
3080			printf("Unknown \"=\" command %s\n", line);
3081			break;
3082		}
3083		return;
3084
3085	  case '-':		/* set command-line-like opts */
3086		switch (line[1])
3087		{
3088		  case 'd':
3089			tTflag(&line[2]);
3090			break;
3091
3092		  case '\0':
3093			printf("Usage: -d{debug arguments}\n");
3094			break;
3095
3096		  default:
3097			printf("Unknown \"-\" command %s\n", line);
3098			break;
3099		}
3100		return;
3101
3102	  case '$':
3103		if (line[1] == '=')
3104		{
3105			mid = macid(&line[2], NULL);
3106			if (mid != 0)
3107				stabapply(dump_class, mid);
3108			return;
3109		}
3110		mid = macid(&line[1], NULL);
3111		if (mid == 0)
3112			return;
3113		p = macvalue(mid, e);
3114		if (p == NULL)
3115			printf("Undefined\n");
3116		else
3117		{
3118			xputs(p);
3119			printf("\n");
3120		}
3121		return;
3122
3123	  case '/':		/* miscellaneous commands */
3124		p = &line[strlen(line)];
3125		while (--p >= line && isascii(*p) && isspace(*p))
3126			*p = '\0';
3127		p = strpbrk(line, " \t");
3128		if (p != NULL)
3129		{
3130			while (isascii(*p) && isspace(*p))
3131				*p++ = '\0';
3132		}
3133		else
3134			p = "";
3135		if (line[1] == '\0')
3136		{
3137			printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
3138			return;
3139		}
3140		if (strcasecmp(&line[1], "quit") == 0)
3141		{
3142			CurEnv->e_id = NULL;
3143			finis(TRUE, ExitStat);
3144		}
3145		if (strcasecmp(&line[1], "mx") == 0)
3146		{
3147#if NAMED_BIND
3148			/* look up MX records */
3149			int nmx;
3150			auto int rcode;
3151			char *mxhosts[MAXMXHOSTS + 1];
3152
3153			if (*p == '\0')
3154			{
3155				printf("Usage: /mx address\n");
3156				return;
3157			}
3158			nmx = getmxrr(p, mxhosts, NULL, FALSE, &rcode);
3159			printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
3160			for (i = 0; i < nmx; i++)
3161				printf("\t%s\n", mxhosts[i]);
3162#else /* NAMED_BIND */
3163			printf("No MX code compiled in\n");
3164#endif /* NAMED_BIND */
3165		}
3166		else if (strcasecmp(&line[1], "canon") == 0)
3167		{
3168			char host[MAXHOSTNAMELEN];
3169
3170			if (*p == '\0')
3171			{
3172				printf("Usage: /canon address\n");
3173				return;
3174			}
3175			else if (strlcpy(host, p, sizeof host) >= sizeof host)
3176			{
3177				printf("Name too long\n");
3178				return;
3179			}
3180			(void) getcanonname(host, sizeof host, HasWildcardMX);
3181			printf("getcanonname(%s) returns %s\n", p, host);
3182		}
3183		else if (strcasecmp(&line[1], "map") == 0)
3184		{
3185			auto int rcode = EX_OK;
3186			char *av[2];
3187
3188			if (*p == '\0')
3189			{
3190				printf("Usage: /map mapname key\n");
3191				return;
3192			}
3193			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
3194				continue;
3195			if (*q == '\0')
3196			{
3197				printf("No key specified\n");
3198				return;
3199			}
3200			*q++ = '\0';
3201			map = stab(p, ST_MAP, ST_FIND);
3202			if (map == NULL)
3203			{
3204				printf("Map named \"%s\" not found\n", p);
3205				return;
3206			}
3207			if (!bitset(MF_OPEN, map->s_map.map_mflags) &&
3208			    !openmap(&(map->s_map)))
3209			{
3210				printf("Map named \"%s\" not open\n", p);
3211				return;
3212			}
3213			printf("map_lookup: %s (%s) ", p, q);
3214			av[0] = q;
3215			av[1] = NULL;
3216			p = (*map->s_map.map_class->map_lookup)
3217					(&map->s_map, q, av, &rcode);
3218			if (p == NULL)
3219				printf("no match (%d)\n", rcode);
3220			else
3221				printf("returns %s (%d)\n", p, rcode);
3222		}
3223		else if (strcasecmp(&line[1], "try") == 0)
3224		{
3225			MAILER *m;
3226			STAB *st;
3227			auto int rcode = EX_OK;
3228
3229			q = strpbrk(p, " \t");
3230			if (q != NULL)
3231			{
3232				while (isascii(*q) && isspace(*q))
3233					*q++ = '\0';
3234			}
3235			if (q == NULL || *q == '\0')
3236			{
3237				printf("Usage: /try mailer address\n");
3238				return;
3239			}
3240			st = stab(p, ST_MAILER, ST_FIND);
3241			if (st == NULL)
3242			{
3243				printf("Unknown mailer %s\n", p);
3244				return;
3245			}
3246			m = st->s_mailer;
3247			printf("Trying %s %s address %s for mailer %s\n",
3248				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
3249				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
3250				q, p);
3251			p = remotename(q, m, tryflags, &rcode, CurEnv);
3252			printf("Rcode = %d, addr = %s\n",
3253				rcode, p == NULL ? "<NULL>" : p);
3254			e->e_to = NULL;
3255		}
3256		else if (strcasecmp(&line[1], "tryflags") == 0)
3257		{
3258			if (*p == '\0')
3259			{
3260				printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
3261				return;
3262			}
3263			for (; *p != '\0'; p++)
3264			{
3265				switch (*p)
3266				{
3267				  case 'H':
3268				  case 'h':
3269					tryflags |= RF_HEADERADDR;
3270					break;
3271
3272				  case 'E':
3273				  case 'e':
3274					tryflags &= ~RF_HEADERADDR;
3275					break;
3276
3277				  case 'S':
3278				  case 's':
3279					tryflags |= RF_SENDERADDR;
3280					break;
3281
3282				  case 'R':
3283				  case 'r':
3284					tryflags &= ~RF_SENDERADDR;
3285					break;
3286				}
3287			}
3288#if _FFR_ADDR_TYPE
3289			exbuf[0] = bitset(RF_HEADERADDR, tryflags) ? 'h' : 'e';
3290			exbuf[1] = ' ';
3291			exbuf[2] = bitset(RF_SENDERADDR, tryflags) ? 's' : 'r';
3292			exbuf[3] = '\0';
3293			define(macid("{addr_type}", NULL), newstr(exbuf), e);
3294#endif /* _FFR_ADDR_TYPE */
3295		}
3296		else if (strcasecmp(&line[1], "parse") == 0)
3297		{
3298			if (*p == '\0')
3299			{
3300				printf("Usage: /parse address\n");
3301				return;
3302			}
3303			q = crackaddr(p);
3304			printf("Cracked address = ");
3305			xputs(q);
3306			printf("\nParsing %s %s address\n",
3307				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
3308				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
3309			if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
3310				printf("Cannot parse\n");
3311			else if (a.q_host != NULL && a.q_host[0] != '\0')
3312				printf("mailer %s, host %s, user %s\n",
3313					a.q_mailer->m_name, a.q_host, a.q_user);
3314			else
3315				printf("mailer %s, user %s\n",
3316					a.q_mailer->m_name, a.q_user);
3317			e->e_to = NULL;
3318		}
3319		else
3320		{
3321			printf("Unknown \"/\" command %s\n", line);
3322		}
3323		return;
3324	}
3325
3326	for (p = line; isascii(*p) && isspace(*p); p++)
3327		continue;
3328	q = p;
3329	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
3330		p++;
3331	if (*p == '\0')
3332	{
3333		printf("No address!\n");
3334		return;
3335	}
3336	*p = '\0';
3337	if (invalidaddr(p + 1, NULL))
3338		return;
3339	do
3340	{
3341		register char **pvp;
3342		char pvpbuf[PSBUFSIZE];
3343
3344		pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
3345			      &delimptr, ConfigLevel >= 9 ? TokTypeNoC : NULL);
3346		if (pvp == NULL)
3347			continue;
3348		p = q;
3349		while (*p != '\0')
3350		{
3351			int status;
3352
3353			rs = strtorwset(p, NULL, ST_FIND);
3354			if (rs < 0)
3355			{
3356				printf("Undefined ruleset %s\n", p);
3357				break;
3358			}
3359			status = rewrite(pvp, rs, 0, e);
3360			if (status != EX_OK)
3361				printf("== Ruleset %s (%d) status %d\n",
3362					p, rs, status);
3363			while (*p != '\0' && *p++ != ',')
3364				continue;
3365		}
3366	} while (*(p = delimptr) != '\0');
3367}
3368
3369static void
3370dump_class(s, id)
3371	register STAB *s;
3372	int id;
3373{
3374	if (s->s_type != ST_CLASS)
3375		return;
3376	if (bitnset(bitidx(id), s->s_class))
3377		printf("%s\n", s->s_name);
3378}
3379