main.c revision 42575
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13#ifndef lint
14static char copyright[] =
15"@(#) Copyright (c) 1998 Sendmail, Inc.  All rights reserved.\n\
16     Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.\n\
17     Copyright (c) 1988, 1993\n\
18	The Regents of the University of California.  All rights reserved.\n";
19#endif /* not lint */
20
21#ifndef lint
22static char sccsid[] = "@(#)main.c	8.322 (Berkeley) 12/18/1998";
23#endif /* not lint */
24
25#define	_DEFINE
26
27#include "sendmail.h"
28#include <arpa/inet.h>
29#include <grp.h>
30#if NAMED_BIND
31#include <resolv.h>
32#endif
33
34/*
35**  SENDMAIL -- Post mail to a set of destinations.
36**
37**	This is the basic mail router.  All user mail programs should
38**	call this routine to actually deliver mail.  Sendmail in
39**	turn calls a bunch of mail servers that do the real work of
40**	delivering the mail.
41**
42**	Sendmail is driven by settings read in from /etc/sendmail.cf
43**	(read by readcf.c).
44**
45**	Usage:
46**		/usr/lib/sendmail [flags] addr ...
47**
48**		See the associated documentation for details.
49**
50**	Author:
51**		Eric Allman, UCB/INGRES (until 10/81).
52**			     Britton-Lee, Inc., purveyors of fine
53**				database computers (11/81 - 10/88).
54**			     International Computer Science Institute
55**				(11/88 - 9/89).
56**			     UCB/Mammoth Project (10/89 - 7/95).
57**			     InReference, Inc. (8/95 - 1/97).
58**			     Sendmail, Inc. (1/98 - present).
59**		The support of the my employers is gratefully acknowledged.
60**			Few of them (Britton-Lee in particular) have had
61**			anything to gain from my involvement in this project.
62*/
63
64
65int		NextMailer;	/* "free" index into Mailer struct */
66char		*FullName;	/* sender's full name */
67ENVELOPE	BlankEnvelope;	/* a "blank" envelope */
68ENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
69ADDRESS		NullAddress =	/* a null address */
70		{ "", "", NULL, "" };
71char		*CommandLineArgs;	/* command line args for pid file */
72bool		Warn_Q_option = FALSE;	/* warn about Q option use */
73char		**SaveArgv;	/* argument vector for re-execing */
74int		MissingFds = 0;	/* bit map of fds missing on startup */
75
76#ifdef NGROUPS_MAX
77GIDSET_T	InitialGidSet[NGROUPS_MAX];
78#endif
79
80static void	obsolete __P((char **));
81extern void	printmailer __P((MAILER *));
82extern void	tTflag __P((char *));
83
84#if DAEMON && !SMTP
85ERROR %%%%   Cannot have DAEMON mode without SMTP   %%%% ERROR
86#endif /* DAEMON && !SMTP */
87#if SMTP && !QUEUE
88ERROR %%%%   Cannot have SMTP mode without QUEUE   %%%% ERROR
89#endif /* DAEMON && !SMTP */
90
91#define MAXCONFIGLEVEL	8	/* highest config version level known */
92
93int
94main(argc, argv, envp)
95	int argc;
96	char **argv;
97	char **envp;
98{
99	register char *p;
100	char **av;
101	extern char Version[];
102	char *ep, *from;
103	STAB *st;
104	register int i;
105	int j;
106	bool queuemode = FALSE;		/* process queue requests */
107	bool safecf = TRUE;
108	bool warn_C_flag = FALSE;
109	char warn_f_flag = '\0';
110	bool run_in_foreground = FALSE;	/* -bD mode */
111	static bool reenter = FALSE;
112	struct passwd *pw;
113	struct hostent *hp;
114	char *nullserver = NULL;
115	bool forged;
116	char jbuf[MAXHOSTNAMELEN];	/* holds MyHostName */
117	static char rnamebuf[MAXNAME];	/* holds RealUserName */
118	char *emptyenviron[1];
119	QUEUE_CHAR *new;
120	extern int DtableSize;
121	extern int optind;
122	extern int opterr;
123	extern char *optarg;
124	extern char **environ;
125	extern time_t convtime __P((char *, char));
126	extern SIGFUNC_DECL intsig __P((int));
127	extern struct hostent *myhostname __P((char *, int));
128	extern char *getauthinfo __P((int, bool *));
129	extern char *getcfname __P((void));
130	extern SIGFUNC_DECL sigusr1 __P((int));
131	extern SIGFUNC_DECL sighup __P((int));
132	extern SIGFUNC_DECL quiesce __P((int));
133	extern void initmacros __P((ENVELOPE *));
134	extern void init_md __P((int, char **));
135	extern int getdtsize __P((void));
136	extern void tTsetup __P((u_char *, int, char *));
137	extern void setdefaults __P((ENVELOPE *));
138	extern void initsetproctitle __P((int, char **, char **));
139	extern void init_vendor_macros __P((ENVELOPE *));
140	extern void load_if_names __P((void));
141	extern void vendor_pre_defaults __P((ENVELOPE *));
142	extern void vendor_post_defaults __P((ENVELOPE *));
143	extern void readcf __P((char *, bool, ENVELOPE *));
144	extern void printqueue __P((void));
145	extern void sendtoargv __P((char **, ENVELOPE *));
146	extern void resetlimits __P((void));
147#ifndef HASUNSETENV
148	extern void unsetenv __P((char *));
149#endif
150
151	/*
152	**  Check to see if we reentered.
153	**	This would normally happen if e_putheader or e_putbody
154	**	were NULL when invoked.
155	*/
156
157	if (reenter)
158	{
159		syserr("main: reentered!");
160		abort();
161	}
162	reenter = TRUE;
163
164	/* avoid null pointer dereferences */
165	TermEscape.te_rv_on = TermEscape.te_rv_off = "";
166
167	/* do machine-dependent initializations */
168	init_md(argc, argv);
169
170	/* in 4.4BSD, the table can be huge; impose a reasonable limit */
171	DtableSize = getdtsize();
172	if (DtableSize > 256)
173		DtableSize = 256;
174
175	/*
176	**  Be sure we have enough file descriptors.
177	**	But also be sure that 0, 1, & 2 are open.
178	*/
179
180	fill_fd(STDIN_FILENO, NULL);
181	fill_fd(STDOUT_FILENO, NULL);
182	fill_fd(STDERR_FILENO, NULL);
183
184	i = DtableSize;
185	while (--i > 0)
186	{
187		if (i != STDIN_FILENO && i != STDOUT_FILENO && i != STDERR_FILENO)
188			(void) close(i);
189	}
190	errno = 0;
191
192#if LOG
193# ifdef LOG_MAIL
194	openlog("sendmail", LOG_PID, LOG_MAIL);
195# else
196	openlog("sendmail", LOG_PID);
197# endif
198#endif
199
200	if (MissingFds != 0)
201	{
202		char mbuf[MAXLINE];
203
204		mbuf[0] = '\0';
205		if (bitset(1 << STDIN_FILENO, MissingFds))
206			strcat(mbuf, ", stdin");
207		if (bitset(1 << STDOUT_FILENO, MissingFds))
208			strcat(mbuf, ", stdout");
209		if (bitset(1 << STDERR_FILENO, MissingFds))
210			strcat(mbuf, ", stderr");
211		syserr("File descriptors missing on startup: %s", &mbuf[2]);
212	}
213
214	/* reset status from syserr() calls for missing file descriptors */
215	Errors = 0;
216	ExitStat = EX_OK;
217
218#if XDEBUG
219	checkfd012("after openlog");
220#endif
221
222	tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
223
224#ifdef NGROUPS_MAX
225	/* save initial group set for future checks */
226	i = getgroups(NGROUPS_MAX, InitialGidSet);
227	if (i == 0)
228		InitialGidSet[0] = (GID_T) -1;
229	while (i < NGROUPS_MAX)
230		InitialGidSet[i++] = InitialGidSet[0];
231#endif
232
233	/* drop group id privileges (RunAsUser not yet set) */
234	(void) drop_privileges(FALSE);
235
236#ifdef SIGUSR1
237	/* arrange to dump state on user-1 signal */
238	setsignal(SIGUSR1, sigusr1);
239#endif
240
241	/* initialize for setproctitle */
242	initsetproctitle(argc, argv, envp);
243
244	/* Handle any non-getoptable constructions. */
245	obsolete(argv);
246
247	/*
248	**  Do a quick prescan of the argument list.
249	*/
250
251#if defined(__osf__) || defined(_AIX3)
252# define OPTIONS	"B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:x"
253#endif
254#if defined(sony_news)
255# define OPTIONS	"B:b:C:cd:E:e:F:f:h:IiJ:M:mN:nO:o:p:q:R:r:sTtUV:vX:"
256#endif
257#ifndef OPTIONS
258# define OPTIONS	"B:b:C:cd:e:F:f:h:IiM:mN:nO:o:p:q:R:r:sTtUV:vX:"
259#endif
260	opterr = 0;
261	while ((j = getopt(argc, argv, OPTIONS)) != -1)
262	{
263		switch (j)
264		{
265		  case 'd':
266			/* hack attack -- see if should use ANSI mode */
267			if (strcmp(optarg, "ANSI") == 0)
268			{
269				TermEscape.te_rv_on = "\033[7m";
270				TermEscape.te_rv_off = "\033[0m";
271				break;
272			}
273			tTflag(optarg);
274			setbuf(stdout, (char *) NULL);
275			break;
276		}
277	}
278	opterr = 1;
279
280	/* set up the blank envelope */
281	BlankEnvelope.e_puthdr = putheader;
282	BlankEnvelope.e_putbody = putbody;
283	BlankEnvelope.e_xfp = NULL;
284	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
285	CurEnv = &BlankEnvelope;
286	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
287
288	/*
289	**  Set default values for variables.
290	**	These cannot be in initialized data space.
291	*/
292
293	setdefaults(&BlankEnvelope);
294
295	RealUid = getuid();
296	RealGid = getgid();
297
298	pw = sm_getpwuid(RealUid);
299	if (pw != NULL)
300		(void) snprintf(rnamebuf, sizeof rnamebuf, "%s", pw->pw_name);
301	else
302		(void) snprintf(rnamebuf, sizeof rnamebuf, "Unknown UID %d", RealUid);
303	RealUserName = rnamebuf;
304
305	if (tTd(0, 101))
306	{
307		printf("Version %s\n", Version);
308		finis(FALSE, EX_OK);
309	}
310
311	/*
312	**  if running non-setuid binary as non-root, pretend
313	**  we are the RunAsUid
314	*/
315	if (RealUid != 0 && geteuid() == RealUid)
316	{
317		if (tTd(47, 1))
318			printf("Non-setuid binary: RunAsUid = RealUid = %d\n",
319				(int)RealUid);
320		RunAsUid = RealUid;
321	}
322	else if (geteuid() != 0)
323		RunAsUid = geteuid();
324
325	if (RealUid != 0 && getegid() == RealGid)
326		RunAsGid = RealGid;
327
328	if (tTd(47, 5))
329	{
330		printf("main: e/ruid = %d/%d e/rgid = %d/%d\n",
331			(int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
332		printf("main: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
333	}
334
335	/* save command line arguments */
336	i = 0;
337	for (av = argv; *av != NULL; )
338		i += strlen(*av++) + 1;
339	SaveArgv = (char **) xalloc(sizeof (char *) * (argc + 1));
340	CommandLineArgs = xalloc(i);
341	p = CommandLineArgs;
342	for (av = argv, i = 0; *av != NULL; )
343	{
344		SaveArgv[i++] = newstr(*av);
345		if (av != argv)
346			*p++ = ' ';
347		strcpy(p, *av++);
348		p += strlen(p);
349	}
350	SaveArgv[i] = NULL;
351
352	if (tTd(0, 1))
353	{
354		int ll;
355		extern char *CompileOptions[];
356
357		printf("Version %s\n Compiled with:", Version);
358		av = CompileOptions;
359		ll = 7;
360		while (*av != NULL)
361		{
362			if (ll + strlen(*av) > 63)
363			{
364				putchar('\n');
365				ll = 0;
366			}
367			if (ll == 0)
368			{
369				putchar('\t');
370				putchar('\t');
371			}
372			else
373				putchar(' ');
374			printf("%s", *av);
375			ll += strlen(*av++) + 1;
376		}
377		putchar('\n');
378	}
379	if (tTd(0, 10))
380	{
381		int ll;
382		extern char *OsCompileOptions[];
383
384		printf("    OS Defines:");
385		av = OsCompileOptions;
386		ll = 7;
387		while (*av != NULL)
388		{
389			if (ll + strlen(*av) > 63)
390			{
391				putchar('\n');
392				ll = 0;
393			}
394			if (ll == 0)
395			{
396				putchar('\t');
397				putchar('\t');
398			}
399			else
400				putchar(' ');
401			printf("%s", *av);
402			ll += strlen(*av++) + 1;
403		}
404		putchar('\n');
405#ifdef _PATH_UNIX
406		printf("Kernel symbols:\t%s\n", _PATH_UNIX);
407#endif
408		printf(" Def Conf file:\t%s\n", getcfname());
409		printf("      Pid file:\t%s\n", PidFile);
410	}
411
412	InChannel = stdin;
413	OutChannel = stdout;
414
415	/* clear sendmail's environment */
416	ExternalEnviron = environ;
417	emptyenviron[0] = NULL;
418	environ = emptyenviron;
419
420	/*
421	**  restore any original TZ setting until TimeZoneSpec has been
422	**  determined - or early log messages may get bogus time stamps
423	*/
424	if ((p = getextenv("TZ")) != NULL)
425	{
426		char *tz;
427		int tzlen;
428
429		tzlen = strlen(p) + 4;
430		tz = xalloc(tzlen);
431		snprintf(tz, tzlen, "TZ=%s", p);
432		putenv(tz);
433	}
434
435	/* prime the child environment */
436	setuserenv("AGENT", "sendmail");
437
438	if (setsignal(SIGINT, SIG_IGN) != SIG_IGN)
439		(void) setsignal(SIGINT, intsig);
440	(void) setsignal(SIGTERM, intsig);
441	(void) setsignal(SIGPIPE, SIG_IGN);
442	OldUmask = umask(022);
443	OpMode = MD_DELIVER;
444	FullName = getextenv("NAME");
445
446	/*
447	**  Initialize name server if it is going to be used.
448	*/
449
450#if NAMED_BIND
451	if (!bitset(RES_INIT, _res.options))
452		res_init();
453	if (tTd(8, 8))
454		_res.options |= RES_DEBUG;
455	else
456		_res.options &= ~RES_DEBUG;
457# ifdef RES_NOALIASES
458	_res.options |= RES_NOALIASES;
459# endif
460#endif
461
462	errno = 0;
463	from = NULL;
464
465	/* initialize some macros, etc. */
466	initmacros(CurEnv);
467	init_vendor_macros(CurEnv);
468
469	/* version */
470	define('v', Version, CurEnv);
471
472	/* hostname */
473	hp = myhostname(jbuf, sizeof jbuf);
474	if (jbuf[0] != '\0')
475	{
476		struct	utsname	utsname;
477
478		if (tTd(0, 4))
479			printf("canonical name: %s\n", jbuf);
480		define('w', newstr(jbuf), CurEnv);	/* must be new string */
481		define('j', newstr(jbuf), CurEnv);
482		setclass('w', jbuf);
483
484		p = strchr(jbuf, '.');
485		if (p != NULL)
486		{
487			if (p[1] != '\0')
488			{
489				define('m', newstr(&p[1]), CurEnv);
490			}
491			while (p != NULL && strchr(&p[1], '.') != NULL)
492			{
493				*p = '\0';
494				if (tTd(0, 4))
495					printf("\ta.k.a.: %s\n", jbuf);
496				setclass('w', jbuf);
497				*p++ = '.';
498				p = strchr(p, '.');
499			}
500		}
501
502		if (uname(&utsname) >= 0)
503			p = utsname.nodename;
504		else
505		{
506			if (tTd(0, 22))
507				printf("uname failed (%s)\n", errstring(errno));
508			makelower(jbuf);
509			p = jbuf;
510		}
511		if (tTd(0, 4))
512			printf(" UUCP nodename: %s\n", p);
513		p = newstr(p);
514		define('k', p, CurEnv);
515		setclass('k', p);
516		setclass('w', p);
517	}
518	if (hp != NULL)
519	{
520		for (av = hp->h_aliases; av != NULL && *av != NULL; av++)
521		{
522			if (tTd(0, 4))
523				printf("\ta.k.a.: %s\n", *av);
524			setclass('w', *av);
525		}
526#if NETINET
527		if (hp->h_addrtype == AF_INET && hp->h_length == INADDRSZ)
528		{
529			for (i = 0; hp->h_addr_list[i] != NULL; i++)
530			{
531				char ipbuf[103];
532
533				snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
534					inet_ntoa(*((struct in_addr *) hp->h_addr_list[i])));
535				if (tTd(0, 4))
536					printf("\ta.k.a.: %s\n", ipbuf);
537				setclass('w', ipbuf);
538			}
539		}
540#endif
541	}
542
543	/* current time */
544	define('b', arpadate((char *) NULL), CurEnv);
545
546	QueueLimitRecipient = (QUEUE_CHAR *) NULL;
547	QueueLimitSender = (QUEUE_CHAR *) NULL;
548	QueueLimitId = (QUEUE_CHAR *) NULL;
549
550	/*
551	**  Crack argv.
552	*/
553
554	av = argv;
555	p = strrchr(*av, '/');
556	if (p++ == NULL)
557		p = *av;
558	if (strcmp(p, "newaliases") == 0)
559		OpMode = MD_INITALIAS;
560	else if (strcmp(p, "mailq") == 0)
561		OpMode = MD_PRINT;
562	else if (strcmp(p, "smtpd") == 0)
563		OpMode = MD_DAEMON;
564	else if (strcmp(p, "hoststat") == 0)
565		OpMode = MD_HOSTSTAT;
566	else if (strcmp(p, "purgestat") == 0)
567		OpMode = MD_PURGESTAT;
568
569	optind = 1;
570	while ((j = getopt(argc, argv, OPTIONS)) != -1)
571	{
572		switch (j)
573		{
574		  case 'b':	/* operations mode */
575			switch (j = *optarg)
576			{
577			  case MD_DAEMON:
578			  case MD_FGDAEMON:
579# if !DAEMON
580				usrerr("Daemon mode not implemented");
581				ExitStat = EX_USAGE;
582				break;
583# endif /* DAEMON */
584			  case MD_SMTP:
585# if !SMTP
586				usrerr("I don't speak SMTP");
587				ExitStat = EX_USAGE;
588				break;
589# endif /* SMTP */
590
591			  case MD_INITALIAS:
592			  case MD_DELIVER:
593			  case MD_VERIFY:
594			  case MD_TEST:
595			  case MD_PRINT:
596			  case MD_HOSTSTAT:
597			  case MD_PURGESTAT:
598			  case MD_ARPAFTP:
599				OpMode = j;
600				break;
601
602			  case MD_FREEZE:
603				usrerr("Frozen configurations unsupported");
604				ExitStat = EX_USAGE;
605				break;
606
607			  default:
608				usrerr("Invalid operation mode %c", j);
609				ExitStat = EX_USAGE;
610				break;
611			}
612			break;
613
614		  case 'B':	/* body type */
615			CurEnv->e_bodytype = optarg;
616			break;
617
618		  case 'C':	/* select configuration file (already done) */
619			if (RealUid != 0)
620				warn_C_flag = TRUE;
621			ConfFile = optarg;
622			(void) drop_privileges(TRUE);
623			safecf = FALSE;
624			break;
625
626		  case 'd':	/* debugging -- already done */
627			break;
628
629		  case 'f':	/* from address */
630		  case 'r':	/* obsolete -f flag */
631			if (from != NULL)
632			{
633				usrerr("More than one \"from\" person");
634				ExitStat = EX_USAGE;
635				break;
636			}
637			from = newstr(denlstring(optarg, TRUE, TRUE));
638			if (strcmp(RealUserName, from) != 0)
639				warn_f_flag = j;
640			break;
641
642		  case 'F':	/* set full name */
643			FullName = newstr(optarg);
644			break;
645
646		  case 'h':	/* hop count */
647			CurEnv->e_hopcount = strtol(optarg, &ep, 10);
648			if (*ep)
649			{
650				usrerr("Bad hop count (%s)", optarg);
651				ExitStat = EX_USAGE;
652			}
653			break;
654
655		  case 'n':	/* don't alias */
656			NoAlias = TRUE;
657			break;
658
659		  case 'N':	/* delivery status notifications */
660			DefaultNotify |= QHASNOTIFY;
661			if (strcasecmp(optarg, "never") == 0)
662				break;
663			for (p = optarg; p != NULL; optarg = p)
664			{
665				p = strchr(p, ',');
666				if (p != NULL)
667					*p++ = '\0';
668				if (strcasecmp(optarg, "success") == 0)
669					DefaultNotify |= QPINGONSUCCESS;
670				else if (strcasecmp(optarg, "failure") == 0)
671					DefaultNotify |= QPINGONFAILURE;
672				else if (strcasecmp(optarg, "delay") == 0)
673					DefaultNotify |= QPINGONDELAY;
674				else
675				{
676					usrerr("Invalid -N argument");
677					ExitStat = EX_USAGE;
678				}
679			}
680			break;
681
682		  case 'o':	/* set option */
683			setoption(*optarg, optarg + 1, FALSE, TRUE, CurEnv);
684			break;
685
686		  case 'O':	/* set option (long form) */
687			setoption(' ', optarg, FALSE, TRUE, CurEnv);
688			break;
689
690		  case 'p':	/* set protocol */
691			p = strchr(optarg, ':');
692			if (p != NULL)
693			{
694				*p++ = '\0';
695				if (*p != '\0')
696				{
697					ep = xalloc(strlen(p) + 1);
698					cleanstrcpy(ep, p, MAXNAME);
699					define('s', ep, CurEnv);
700				}
701			}
702			if (*optarg != '\0')
703			{
704				ep = xalloc(strlen(optarg) + 1);
705				cleanstrcpy(ep, optarg, MAXNAME);
706				define('r', ep, CurEnv);
707			}
708			break;
709
710		  case 'q':	/* run queue files at intervals */
711# if QUEUE
712			FullName = NULL;
713			queuemode = TRUE;
714			switch (optarg[0])
715			{
716			  case 'I':
717				if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
718					syserr("!Out of memory!!");
719				new->queue_match = newstr(&optarg[1]);
720				new->queue_next = QueueLimitId;
721				QueueLimitId = new;
722				break;
723
724			  case 'R':
725				if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
726					syserr("!Out of memory!!");
727				new->queue_match = newstr(&optarg[1]);
728				new->queue_next = QueueLimitRecipient;
729				QueueLimitRecipient = new;
730				break;
731
732			  case 'S':
733				if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
734					syserr("!Out of memory!!");
735				new->queue_match = newstr(&optarg[1]);
736				new->queue_next = QueueLimitSender;
737				QueueLimitSender = new;
738				break;
739
740			  default:
741				QueueIntvl = convtime(optarg, 'm');
742				break;
743			}
744# else /* QUEUE */
745			usrerr("I don't know about queues");
746			ExitStat = EX_USAGE;
747# endif /* QUEUE */
748			break;
749
750		  case 'R':	/* DSN RET: what to return */
751			if (bitset(EF_RET_PARAM, CurEnv->e_flags))
752			{
753				usrerr("Duplicate -R flag");
754				ExitStat = EX_USAGE;
755				break;
756			}
757			CurEnv->e_flags |= EF_RET_PARAM;
758			if (strcasecmp(optarg, "hdrs") == 0)
759				CurEnv->e_flags |= EF_NO_BODY_RETN;
760			else if (strcasecmp(optarg, "full") != 0)
761			{
762				usrerr("Invalid -R value");
763				ExitStat = EX_USAGE;
764			}
765			break;
766
767		  case 't':	/* read recipients from message */
768			GrabTo = TRUE;
769			break;
770
771		  case 'U':	/* initial (user) submission */
772			UserSubmission = TRUE;
773			break;
774
775		  case 'V':	/* DSN ENVID: set "original" envelope id */
776			if (!xtextok(optarg))
777			{
778				usrerr("Invalid syntax in -V flag");
779				ExitStat = EX_USAGE;
780			}
781			else
782				CurEnv->e_envid = newstr(optarg);
783			break;
784
785		  case 'X':	/* traffic log file */
786			(void) drop_privileges(TRUE);
787			TrafficLogFile = fopen(optarg, "a");
788			if (TrafficLogFile == NULL)
789			{
790				syserr("cannot open %s", optarg);
791				ExitStat = EX_CANTCREAT;
792				break;
793			}
794#ifdef HASSETVBUF
795			setvbuf(TrafficLogFile, NULL, _IOLBF, 0);
796#else
797			setlinebuf(TrafficLogFile);
798#endif
799			break;
800
801			/* compatibility flags */
802		  case 'c':	/* connect to non-local mailers */
803		  case 'i':	/* don't let dot stop me */
804		  case 'm':	/* send to me too */
805		  case 'T':	/* set timeout interval */
806		  case 'v':	/* give blow-by-blow description */
807			setoption(j, "T", FALSE, TRUE, CurEnv);
808			break;
809
810		  case 'e':	/* error message disposition */
811		  case 'M':	/* define macro */
812			setoption(j, optarg, FALSE, TRUE, CurEnv);
813			break;
814
815		  case 's':	/* save From lines in headers */
816			setoption('f', "T", FALSE, TRUE, CurEnv);
817			break;
818
819# ifdef DBM
820		  case 'I':	/* initialize alias DBM file */
821			OpMode = MD_INITALIAS;
822			break;
823# endif /* DBM */
824
825# if defined(__osf__) || defined(_AIX3)
826		  case 'x':	/* random flag that OSF/1 & AIX mailx passes */
827			break;
828# endif
829# if defined(sony_news)
830		  case 'E':
831		  case 'J':	/* ignore flags for Japanese code conversion
832				   impremented on Sony NEWS */
833			break;
834# endif
835
836		  default:
837			finis(TRUE, EX_USAGE);
838			break;
839		}
840	}
841	av += optind;
842
843	/*
844	**  Do basic initialization.
845	**	Read system control file.
846	**	Extract special fields for local use.
847	*/
848
849	/* set up ${opMode} for use in config file */
850	{
851		char mbuf[2];
852
853		mbuf[0] = OpMode;
854		mbuf[1] = '\0';
855		define(MID_OPMODE, newstr(mbuf), CurEnv);
856	}
857
858#if XDEBUG
859	checkfd012("before readcf");
860#endif
861	vendor_pre_defaults(CurEnv);
862	readcf(getcfname(), safecf, CurEnv);
863	ConfigFileRead = TRUE;
864	vendor_post_defaults(CurEnv);
865
866	/* Enforce use of local time (null string overrides this) */
867	if (TimeZoneSpec == NULL)
868		unsetenv("TZ");
869	else if (TimeZoneSpec[0] != '\0')
870		setuserenv("TZ", TimeZoneSpec);
871	else
872		setuserenv("TZ", NULL);
873	tzset();
874
875	/* avoid denial-of-service attacks */
876	resetlimits();
877
878	if (OpMode != MD_DAEMON && OpMode != MD_FGDAEMON)
879	{
880		/* drop privileges -- daemon mode done after socket/bind */
881		(void) drop_privileges(FALSE);
882	}
883
884	/*
885	**  Find our real host name for future logging.
886	*/
887
888	p = getauthinfo(STDIN_FILENO, &forged);
889	define('_', p, CurEnv);
890
891	/* suppress error printing if errors mailed back or whatever */
892	if (CurEnv->e_errormode != EM_PRINT)
893		HoldErrs = TRUE;
894
895	/* set up the $=m class now, after .cf has a chance to redefine $m */
896	expand("\201m", jbuf, sizeof jbuf, CurEnv);
897	setclass('m', jbuf);
898
899	/* probe interfaces and locate any additional names */
900	if (!DontProbeInterfaces)
901		load_if_names();
902
903	if (tTd(0, 1))
904	{
905		printf("\n============ SYSTEM IDENTITY (after readcf) ============");
906		printf("\n      (short domain name) $w = ");
907		xputs(macvalue('w', CurEnv));
908		printf("\n  (canonical domain name) $j = ");
909		xputs(macvalue('j', CurEnv));
910		printf("\n         (subdomain name) $m = ");
911		xputs(macvalue('m', CurEnv));
912		printf("\n              (node name) $k = ");
913		xputs(macvalue('k', CurEnv));
914		printf("\n========================================================\n\n");
915	}
916
917	/*
918	**  Do more command line checking -- these are things that
919	**  have to modify the results of reading the config file.
920	*/
921
922	/* process authorization warnings from command line */
923	if (warn_C_flag)
924		auth_warning(CurEnv, "Processed by %s with -C %s",
925			RealUserName, ConfFile);
926	if (Warn_Q_option)
927		auth_warning(CurEnv, "Processed from queue %s", QueueDir);
928
929	/* check body type for legality */
930	if (CurEnv->e_bodytype == NULL)
931		/* nothing */ ;
932	else if (strcasecmp(CurEnv->e_bodytype, "7BIT") == 0)
933		SevenBitInput = TRUE;
934	else if (strcasecmp(CurEnv->e_bodytype, "8BITMIME") == 0)
935		SevenBitInput = FALSE;
936	else
937	{
938		usrerr("Illegal body type %s", CurEnv->e_bodytype);
939		CurEnv->e_bodytype = NULL;
940	}
941
942	/* tweak default DSN notifications */
943	if (DefaultNotify == 0)
944		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
945
946	/* be sure we don't pick up bogus HOSTALIASES environment variable */
947	if (queuemode && RealUid != 0)
948		(void) unsetenv("HOSTALIASES");
949
950	/* check for sane configuration level */
951	if (ConfigLevel > MAXCONFIGLEVEL)
952	{
953		syserr("Warning: .cf version level (%d) exceeds sendmail version %s functionality (%d)",
954			ConfigLevel, Version, MAXCONFIGLEVEL);
955	}
956
957	/* need MCI cache to have persistence */
958	if (HostStatDir != NULL && MaxMciCache == 0)
959	{
960		HostStatDir = NULL;
961		printf("Warning: HostStatusDirectory disabled with ConnectionCacheSize = 0\n");
962	}
963
964	/* need HostStatusDir in order to have SingleThreadDelivery */
965	if (SingleThreadDelivery && HostStatDir == NULL)
966	{
967		SingleThreadDelivery = FALSE;
968		printf("Warning: HostStatusDirectory required for SingleThreadDelivery\n");
969	}
970
971	/* check for permissions */
972	if ((OpMode == MD_DAEMON ||
973	     OpMode == MD_FGDAEMON ||
974	     OpMode == MD_PURGESTAT) &&
975	    RealUid != 0 &&
976	    RealUid != TrustedUid)
977	{
978		if (LogLevel > 1)
979			sm_syslog(LOG_ALERT, NOQID,
980				"user %d attempted to %s",
981				RealUid,
982				OpMode != MD_PURGESTAT ? "run daemon"
983						       : "purge host status");
984		usrerr("Permission denied");
985		finis(FALSE, EX_USAGE);
986	}
987
988	if (MeToo)
989		BlankEnvelope.e_flags |= EF_METOO;
990
991	switch (OpMode)
992	{
993	  case MD_TEST:
994		/* don't have persistent host status in test mode */
995		HostStatDir = NULL;
996		if (Verbose == 0)
997			Verbose = 2;
998		CurEnv->e_errormode = EM_PRINT;
999		HoldErrs = FALSE;
1000		break;
1001
1002	  case MD_VERIFY:
1003		CurEnv->e_errormode = EM_PRINT;
1004		HoldErrs = FALSE;
1005		/* arrange to exit cleanly on hangup signal */
1006		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1007			setsignal(SIGHUP, intsig);
1008		break;
1009
1010	  case MD_FGDAEMON:
1011		run_in_foreground = TRUE;
1012		OpMode = MD_DAEMON;
1013		/* fall through ... */
1014
1015	  case MD_DAEMON:
1016		vendor_daemon_setup(CurEnv);
1017
1018		/* remove things that don't make sense in daemon mode */
1019		FullName = NULL;
1020		GrabTo = FALSE;
1021
1022		/* arrange to restart on hangup signal */
1023		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
1024			sm_syslog(LOG_WARNING, NOQID,
1025				"daemon invoked without full pathname; kill -1 won't work");
1026		setsignal(SIGHUP, sighup);
1027
1028		/* workaround: can't seem to release the signal in the parent */
1029		releasesignal(SIGHUP);
1030		break;
1031
1032	  case MD_INITALIAS:
1033		Verbose = 2;
1034		CurEnv->e_errormode = EM_PRINT;
1035		HoldErrs = FALSE;
1036		/* fall through... */
1037
1038	  case MD_PRINT:
1039		/* to handle sendmail -bp -qSfoobar properly */
1040		queuemode = FALSE;
1041		/* fall through... */
1042
1043	  default:
1044		/* arrange to exit cleanly on hangup signal */
1045		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1046			setsignal(SIGHUP, intsig);
1047		break;
1048	}
1049
1050	/* special considerations for FullName */
1051	if (FullName != NULL)
1052	{
1053		char *full = NULL;
1054		extern bool rfc822_string __P((char *));
1055
1056		/* full names can't have newlines */
1057		if (strchr(FullName, '\n') != NULL)
1058		{
1059			FullName = full = newstr(denlstring(FullName, TRUE, TRUE));
1060		}
1061		/* check for characters that may have to be quoted */
1062		if (!rfc822_string(FullName))
1063		{
1064			extern char *addquotes __P((char *));
1065
1066			/*
1067			**  Quote a full name with special characters
1068			**  as a comment so crackaddr() doesn't destroy
1069			**  the name portion of the address.
1070			*/
1071			FullName = addquotes(FullName);
1072			if (full != NULL)
1073				free(full);
1074		}
1075	}
1076
1077	/* do heuristic mode adjustment */
1078	if (Verbose)
1079	{
1080		/* turn off noconnect option */
1081		setoption('c', "F", TRUE, FALSE, CurEnv);
1082
1083		/* turn on interactive delivery */
1084		setoption('d', "", TRUE, FALSE, CurEnv);
1085	}
1086
1087#ifdef VENDOR_CODE
1088	/* check for vendor mismatch */
1089	if (VendorCode != VENDOR_CODE)
1090	{
1091		extern char *getvendor __P((int));
1092
1093		message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
1094			getvendor(VENDOR_CODE), getvendor(VendorCode));
1095	}
1096#endif
1097
1098	/* check for out of date configuration level */
1099	if (ConfigLevel < MAXCONFIGLEVEL)
1100	{
1101		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
1102			Version, MAXCONFIGLEVEL, ConfigLevel);
1103	}
1104
1105	if (ConfigLevel < 3)
1106	{
1107		UseErrorsTo = TRUE;
1108	}
1109
1110	/* set options that were previous macros */
1111	if (SmtpGreeting == NULL)
1112	{
1113		if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
1114			SmtpGreeting = newstr(p);
1115		else
1116			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
1117	}
1118	if (UnixFromLine == NULL)
1119	{
1120		if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
1121			UnixFromLine = newstr(p);
1122		else
1123			UnixFromLine = "From \201g  \201d";
1124	}
1125
1126	/* our name for SMTP codes */
1127	expand("\201j", jbuf, sizeof jbuf, CurEnv);
1128	MyHostName = jbuf;
1129	if (strchr(jbuf, '.') == NULL)
1130		message("WARNING: local host name (%s) is not qualified; fix $j in config file",
1131			jbuf);
1132
1133	/* make certain that this name is part of the $=w class */
1134	setclass('w', MyHostName);
1135
1136	/* the indices of built-in mailers */
1137	st = stab("local", ST_MAILER, ST_FIND);
1138	if (st != NULL)
1139		LocalMailer = st->s_mailer;
1140	else if (OpMode != MD_TEST || !warn_C_flag)
1141		syserr("No local mailer defined");
1142
1143	st = stab("prog", ST_MAILER, ST_FIND);
1144	if (st == NULL)
1145		syserr("No prog mailer defined");
1146	else
1147	{
1148		ProgMailer = st->s_mailer;
1149		clrbitn(M_MUSER, ProgMailer->m_flags);
1150	}
1151
1152	st = stab("*file*", ST_MAILER, ST_FIND);
1153	if (st == NULL)
1154		syserr("No *file* mailer defined");
1155	else
1156	{
1157		FileMailer = st->s_mailer;
1158		clrbitn(M_MUSER, FileMailer->m_flags);
1159	}
1160
1161	st = stab("*include*", ST_MAILER, ST_FIND);
1162	if (st == NULL)
1163		syserr("No *include* mailer defined");
1164	else
1165		InclMailer = st->s_mailer;
1166
1167	if (ConfigLevel < 6)
1168	{
1169		/* heuristic tweaking of local mailer for back compat */
1170		if (LocalMailer != NULL)
1171		{
1172			setbitn(M_ALIASABLE, LocalMailer->m_flags);
1173			setbitn(M_HASPWENT, LocalMailer->m_flags);
1174			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
1175			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
1176			setbitn(M_CHECKPROG, LocalMailer->m_flags);
1177			setbitn(M_CHECKFILE, LocalMailer->m_flags);
1178			setbitn(M_CHECKUDB, LocalMailer->m_flags);
1179		}
1180		if (ProgMailer != NULL)
1181			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
1182		if (FileMailer != NULL)
1183			setbitn(M_RUNASRCPT, FileMailer->m_flags);
1184	}
1185	if (ConfigLevel < 7)
1186	{
1187		if (LocalMailer != NULL)
1188			setbitn(M_VRFY250, LocalMailer->m_flags);
1189		if (ProgMailer != NULL)
1190			setbitn(M_VRFY250, ProgMailer->m_flags);
1191		if (FileMailer != NULL)
1192			setbitn(M_VRFY250, FileMailer->m_flags);
1193	}
1194
1195	/* MIME Content-Types that cannot be transfer encoded */
1196	setclass('n', "multipart/signed");
1197
1198	/* MIME message/xxx subtypes that can be treated as messages */
1199	setclass('s', "rfc822");
1200
1201	/* MIME Content-Transfer-Encodings that can be encoded */
1202	setclass('e', "7bit");
1203	setclass('e', "8bit");
1204	setclass('e', "binary");
1205
1206#ifdef USE_B_CLASS
1207	/* MIME Content-Types that should be treated as binary */
1208	setclass('b', "image");
1209	setclass('b', "audio");
1210	setclass('b', "video");
1211	setclass('b', "application/octet-stream");
1212#endif
1213
1214#if _FFR_MAX_MIME_HEADER_LENGTH
1215	/* MIME headers which have fields to check for overflow */
1216	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition");
1217	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type");
1218
1219	/* MIME headers to check for length overflow */
1220	setclass(macid("{checkMIMETextHeaders}", NULL), "content-description");
1221
1222	/* MIME headers to check for overflow and rebalance */
1223	setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition");
1224	setclass(macid("{checkMIMEHeaders}", NULL), "content-id");
1225	setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding");
1226	setclass(macid("{checkMIMEHeaders}", NULL), "content-type");
1227	setclass(macid("{checkMIMEHeaders}", NULL), "mime-version");
1228#endif
1229
1230	/* operate in queue directory */
1231	if (QueueDir == NULL)
1232	{
1233		if (OpMode != MD_TEST)
1234		{
1235			syserr("QueueDirectory (Q) option must be set");
1236			ExitStat = EX_CONFIG;
1237		}
1238	}
1239	else
1240	{
1241		/* test path to get warning messages */
1242		(void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE);
1243		if (OpMode != MD_TEST && chdir(QueueDir) < 0)
1244		{
1245			syserr("cannot chdir(%s)", QueueDir);
1246			ExitStat = EX_CONFIG;
1247		}
1248	}
1249
1250	/* check host status directory for validity */
1251	if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
1252	{
1253		/* cannot use this value */
1254		if (tTd(0, 2))
1255			printf("Cannot use HostStatusDirectory = %s: %s\n",
1256				HostStatDir, errstring(errno));
1257		HostStatDir = NULL;
1258	}
1259
1260# if QUEUE
1261	if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
1262	{
1263		struct stat stbuf;
1264
1265		/* check to see if we own the queue directory */
1266		if (stat(".", &stbuf) < 0)
1267			syserr("main: cannot stat %s", QueueDir);
1268		if (stbuf.st_uid != RealUid)
1269		{
1270			/* nope, really a botch */
1271			usrerr("You do not have permission to process the queue");
1272			finis(FALSE, EX_NOPERM);
1273		}
1274	}
1275# endif /* QUEUE */
1276
1277	/* if we've had errors so far, exit now */
1278	if (ExitStat != EX_OK && OpMode != MD_TEST)
1279		finis(FALSE, ExitStat);
1280
1281#if XDEBUG
1282	checkfd012("before main() initmaps");
1283#endif
1284
1285	/*
1286	**  Do operation-mode-dependent initialization.
1287	*/
1288
1289	switch (OpMode)
1290	{
1291	  case MD_PRINT:
1292		/* print the queue */
1293#if QUEUE
1294		dropenvelope(CurEnv, TRUE);
1295		signal(SIGPIPE, quiesce);
1296		printqueue();
1297		finis(FALSE, EX_OK);
1298#else /* QUEUE */
1299		usrerr("No queue to print");
1300		finis(FALSE, ExitStat);
1301#endif /* QUEUE */
1302		break;
1303
1304	  case MD_HOSTSTAT:
1305		signal(SIGPIPE, quiesce);
1306		mci_traverse_persistent(mci_print_persistent, NULL);
1307		finis(FALSE, EX_OK);
1308	    	break;
1309
1310	  case MD_PURGESTAT:
1311		mci_traverse_persistent(mci_purge_persistent, NULL);
1312		finis(FALSE, EX_OK);
1313	    	break;
1314
1315	  case MD_INITALIAS:
1316		/* initialize maps */
1317		initmaps(TRUE, CurEnv);
1318		finis(FALSE, ExitStat);
1319		break;
1320
1321	  case MD_SMTP:
1322	  case MD_DAEMON:
1323		/* reset DSN parameters */
1324		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1325		CurEnv->e_envid = NULL;
1326		CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
1327
1328		/* don't open maps for daemon -- done below in child */
1329		break;
1330
1331	  default:
1332		/* open the maps */
1333		initmaps(FALSE, CurEnv);
1334		break;
1335	}
1336
1337	if (tTd(0, 15))
1338	{
1339		extern void printrules __P((void));
1340
1341		/* print configuration table (or at least part of it) */
1342		if (tTd(0, 90))
1343			printrules();
1344		for (i = 0; i < MAXMAILERS; i++)
1345		{
1346			if (Mailer[i] != NULL)
1347				printmailer(Mailer[i]);
1348		}
1349	}
1350
1351	/*
1352	**  Switch to the main envelope.
1353	*/
1354
1355	CurEnv = newenvelope(&MainEnvelope, CurEnv);
1356	MainEnvelope.e_flags = BlankEnvelope.e_flags;
1357
1358	/*
1359	**  If test mode, read addresses from stdin and process.
1360	*/
1361
1362	if (OpMode == MD_TEST)
1363	{
1364		char buf[MAXLINE];
1365		SIGFUNC_DECL intindebug __P((int));
1366
1367		if (isatty(fileno(stdin)))
1368			Verbose = 2;
1369
1370		if (Verbose)
1371		{
1372			printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
1373			printf("Enter <ruleset> <address>\n");
1374		}
1375		if (setjmp(TopFrame) > 0)
1376			printf("\n");
1377		(void) setsignal(SIGINT, intindebug);
1378		for (;;)
1379		{
1380			extern void testmodeline __P((char *, ENVELOPE *));
1381
1382			if (Verbose == 2)
1383				printf("> ");
1384			(void) fflush(stdout);
1385			if (fgets(buf, sizeof buf, stdin) == NULL)
1386				finis(TRUE, ExitStat);
1387			p = strchr(buf, '\n');
1388			if (p != NULL)
1389				*p = '\0';
1390			if (Verbose < 2)
1391				printf("> %s\n", buf);
1392			testmodeline(buf, CurEnv);
1393		}
1394	}
1395
1396# if QUEUE
1397	/*
1398	**  If collecting stuff from the queue, go start doing that.
1399	*/
1400
1401	if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
1402	{
1403		(void) runqueue(FALSE, Verbose);
1404		finis(TRUE, ExitStat);
1405	}
1406# endif /* QUEUE */
1407
1408	/*
1409	**  If a daemon, wait for a request.
1410	**	getrequests will always return in a child.
1411	**	If we should also be processing the queue, start
1412	**		doing it in background.
1413	**	We check for any errors that might have happened
1414	**		during startup.
1415	*/
1416
1417	if (OpMode == MD_DAEMON || QueueIntvl != 0)
1418	{
1419		char dtype[200];
1420		extern void getrequests __P((ENVELOPE *));
1421
1422		if (!run_in_foreground && !tTd(99, 100))
1423		{
1424			/* put us in background */
1425			i = fork();
1426			if (i < 0)
1427				syserr("daemon: cannot fork");
1428			if (i != 0)
1429				finis(FALSE, EX_OK);
1430
1431			/* disconnect from our controlling tty */
1432			disconnect(2, CurEnv);
1433		}
1434
1435		dtype[0] = '\0';
1436		if (OpMode == MD_DAEMON)
1437			strcat(dtype, "+SMTP");
1438		if (QueueIntvl != 0)
1439		{
1440			strcat(dtype, "+queueing@");
1441			strcat(dtype, pintvl(QueueIntvl, TRUE));
1442		}
1443		if (tTd(0, 1))
1444			strcat(dtype, "+debugging");
1445
1446		sm_syslog(LOG_INFO, NOQID,
1447			"starting daemon (%s): %s", Version, dtype + 1);
1448#ifdef XLA
1449		xla_create_file();
1450#endif
1451
1452# if QUEUE
1453		if (queuemode)
1454		{
1455			(void) runqueue(TRUE, FALSE);
1456			if (OpMode != MD_DAEMON)
1457			{
1458				for (;;)
1459				{
1460					pause();
1461					if (DoQueueRun)
1462						(void) runqueue(TRUE, FALSE);
1463				}
1464			}
1465		}
1466# endif /* QUEUE */
1467		dropenvelope(CurEnv, TRUE);
1468
1469#if DAEMON
1470		getrequests(CurEnv);
1471
1472		/* drop privileges */
1473		(void) drop_privileges(FALSE);
1474
1475		/* at this point we are in a child: reset state */
1476		(void) newenvelope(CurEnv, CurEnv);
1477
1478		/*
1479		**  Get authentication data
1480		*/
1481
1482		p = getauthinfo(fileno(InChannel), &forged);
1483		define('_', p, &BlankEnvelope);
1484#endif /* DAEMON */
1485	}
1486
1487# if SMTP
1488	/*
1489	**  If running SMTP protocol, start collecting and executing
1490	**  commands.  This will never return.
1491	*/
1492
1493	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
1494	{
1495		char pbuf[20];
1496		extern void smtp __P((char *, ENVELOPE *));
1497
1498		/*
1499		**  Save some macros for check_* rulesets.
1500		*/
1501
1502		if (forged)
1503		{
1504			char ipbuf[103];
1505
1506			snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
1507				 inet_ntoa(RealHostAddr.sin.sin_addr));
1508
1509			define(macid("{client_name}", NULL),
1510			       newstr(ipbuf), &BlankEnvelope);
1511		}
1512		else
1513			define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope);
1514		define(macid("{client_addr}", NULL),
1515		       newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
1516		if (RealHostAddr.sa.sa_family == AF_INET)
1517			snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port);
1518		else
1519			snprintf(pbuf, sizeof pbuf, "0");
1520		define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope);
1521
1522		/* initialize maps now for check_relay ruleset */
1523		initmaps(FALSE, CurEnv);
1524
1525		if (OpMode == MD_DAEMON)
1526		{
1527			/* validate the connection */
1528			HoldErrs = TRUE;
1529			nullserver = validate_connection(&RealHostAddr,
1530							 RealHostName, CurEnv);
1531			HoldErrs = FALSE;
1532		}
1533		smtp(nullserver, CurEnv);
1534	}
1535# endif /* SMTP */
1536
1537	clearenvelope(CurEnv, FALSE);
1538	if (OpMode == MD_VERIFY)
1539	{
1540		CurEnv->e_sendmode = SM_VERIFY;
1541		PostMasterCopy = NULL;
1542	}
1543	else
1544	{
1545		/* interactive -- all errors are global */
1546		CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
1547	}
1548
1549	/*
1550	**  Do basic system initialization and set the sender
1551	*/
1552
1553	initsys(CurEnv);
1554	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't'))
1555		auth_warning(CurEnv, "%s set sender to %s using -%c",
1556			RealUserName, from, warn_f_flag);
1557	setsender(from, CurEnv, NULL, '\0', FALSE);
1558	if (macvalue('s', CurEnv) == NULL)
1559		define('s', RealHostName, CurEnv);
1560
1561	if (*av == NULL && !GrabTo)
1562	{
1563		CurEnv->e_flags |= EF_GLOBALERRS;
1564		usrerr("Recipient names must be specified");
1565
1566		/* collect body for UUCP return */
1567		if (OpMode != MD_VERIFY)
1568			collect(InChannel, FALSE, NULL, CurEnv);
1569		finis(TRUE, ExitStat);
1570	}
1571
1572	/*
1573	**  Scan argv and deliver the message to everyone.
1574	*/
1575
1576	sendtoargv(av, CurEnv);
1577
1578	/* if we have had errors sofar, arrange a meaningful exit stat */
1579	if (Errors > 0 && ExitStat == EX_OK)
1580		ExitStat = EX_USAGE;
1581
1582#if _FFR_FIX_DASHT
1583	/*
1584	**  If using -t, force not sending to argv recipients, even
1585	**  if they are mentioned in the headers.
1586	*/
1587
1588	if (GrabTo)
1589	{
1590		ADDRESS *q;
1591
1592		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
1593			q->q_flags |= QDONTSEND;
1594	}
1595#endif
1596
1597	/*
1598	**  Read the input mail.
1599	*/
1600
1601	CurEnv->e_to = NULL;
1602	if (OpMode != MD_VERIFY || GrabTo)
1603	{
1604		long savedflags = CurEnv->e_flags & EF_FATALERRS;
1605
1606		CurEnv->e_flags |= EF_GLOBALERRS;
1607		CurEnv->e_flags &= ~EF_FATALERRS;
1608		collect(InChannel, FALSE, NULL, CurEnv);
1609
1610		/* bail out if message too large */
1611		if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
1612		{
1613			finis(TRUE, ExitStat);
1614			/*NOTREACHED*/
1615			return -1;
1616		}
1617		CurEnv->e_flags |= savedflags;
1618	}
1619	errno = 0;
1620
1621	if (tTd(1, 1))
1622		printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
1623
1624	/*
1625	**  Actually send everything.
1626	**	If verifying, just ack.
1627	*/
1628
1629	CurEnv->e_from.q_flags |= QDONTSEND;
1630	if (tTd(1, 5))
1631	{
1632		printf("main: QDONTSEND ");
1633		printaddr(&CurEnv->e_from, FALSE);
1634	}
1635	CurEnv->e_to = NULL;
1636	CurrentLA = getla();
1637	GrabTo = FALSE;
1638	sendall(CurEnv, SM_DEFAULT);
1639
1640	/*
1641	**  All done.
1642	**	Don't send return error message if in VERIFY mode.
1643	*/
1644
1645	finis(TRUE, ExitStat);
1646	/*NOTREACHED*/
1647	return -1;
1648}
1649
1650/* ARGSUSED */
1651SIGFUNC_DECL
1652quiesce(sig)
1653	int sig;
1654{
1655	finis(FALSE, EX_OK);
1656}
1657
1658/* ARGSUSED */
1659SIGFUNC_DECL
1660intindebug(sig)
1661	int sig;
1662{
1663	longjmp(TopFrame, 1);
1664	return SIGFUNC_RETURN;
1665}
1666
1667
1668/*
1669**  FINIS -- Clean up and exit.
1670**
1671**	Parameters:
1672**		drop -- whether or not to drop CurEnv envelope
1673**		exitstat -- exit status to use for exit() call
1674**
1675**	Returns:
1676**		never
1677**
1678**	Side Effects:
1679**		exits sendmail
1680*/
1681
1682void
1683finis(drop, exitstat)
1684	bool drop;
1685	volatile int exitstat;
1686{
1687	extern void closemaps __P((void));
1688#ifdef USERDB
1689	extern void _udbx_close __P((void));
1690#endif
1691
1692	if (tTd(2, 1))
1693	{
1694		extern void printenvflags __P((ENVELOPE *));
1695
1696		printf("\n====finis: stat %d e_id=%s e_flags=",
1697			exitstat,
1698			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
1699		printenvflags(CurEnv);
1700	}
1701	if (tTd(2, 9))
1702		printopenfds(FALSE);
1703
1704	/* if we fail in finis(), just exit */
1705	if (setjmp(TopFrame) != 0)
1706	{
1707		/* failed -- just give it up */
1708		goto forceexit;
1709	}
1710
1711	/* clean up temp files */
1712	CurEnv->e_to = NULL;
1713	if (drop && CurEnv->e_id != NULL)
1714		dropenvelope(CurEnv, TRUE);
1715
1716	/* flush any cached connections */
1717	mci_flush(TRUE, NULL);
1718
1719	/* close maps belonging to this pid */
1720	closemaps();
1721
1722#ifdef USERDB
1723	/* close UserDatabase */
1724	_udbx_close();
1725#endif
1726
1727# ifdef XLA
1728	/* clean up extended load average stuff */
1729	xla_all_end();
1730# endif
1731
1732	/* and exit */
1733  forceexit:
1734	if (LogLevel > 78)
1735		sm_syslog(LOG_DEBUG, CurEnv->e_id,
1736			"finis, pid=%d",
1737			getpid());
1738	if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
1739		exitstat = EX_OK;
1740
1741	/* reset uid for process accounting */
1742	endpwent();
1743	setuid(RealUid);
1744
1745	exit(exitstat);
1746}
1747/*
1748**  INTSIG -- clean up on interrupt
1749**
1750**	This just arranges to exit.  It pessimises in that it
1751**	may resend a message.
1752**
1753**	Parameters:
1754**		none.
1755**
1756**	Returns:
1757**		none.
1758**
1759**	Side Effects:
1760**		Unlocks the current job.
1761*/
1762
1763/* ARGSUSED */
1764SIGFUNC_DECL
1765intsig(sig)
1766	int sig;
1767{
1768	if (LogLevel > 79)
1769		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
1770	FileName = NULL;
1771	unlockqueue(CurEnv);
1772	closecontrolsocket(TRUE);
1773#ifdef XLA
1774	xla_all_end();
1775#endif
1776	finis(FALSE, EX_OK);
1777}
1778/*
1779**  INITMACROS -- initialize the macro system
1780**
1781**	This just involves defining some macros that are actually
1782**	used internally as metasymbols to be themselves.
1783**
1784**	Parameters:
1785**		none.
1786**
1787**	Returns:
1788**		none.
1789**
1790**	Side Effects:
1791**		initializes several macros to be themselves.
1792*/
1793
1794struct metamac	MetaMacros[] =
1795{
1796	/* LHS pattern matching characters */
1797	{ '*', MATCHZANY },	{ '+', MATCHANY },	{ '-', MATCHONE },
1798	{ '=', MATCHCLASS },	{ '~', MATCHNCLASS },
1799
1800	/* these are RHS metasymbols */
1801	{ '#', CANONNET },	{ '@', CANONHOST },	{ ':', CANONUSER },
1802	{ '>', CALLSUBR },
1803
1804	/* the conditional operations */
1805	{ '?', CONDIF },	{ '|', CONDELSE },	{ '.', CONDFI },
1806
1807	/* the hostname lookup characters */
1808	{ '[', HOSTBEGIN },	{ ']', HOSTEND },
1809	{ '(', LOOKUPBEGIN },	{ ')', LOOKUPEND },
1810
1811	/* miscellaneous control characters */
1812	{ '&', MACRODEXPAND },
1813
1814	{ '\0' }
1815};
1816
1817#define MACBINDING(name, mid) \
1818		stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
1819		MacroName[mid] = name;
1820
1821void
1822initmacros(e)
1823	register ENVELOPE *e;
1824{
1825	register struct metamac *m;
1826	register int c;
1827	char buf[5];
1828	extern char *MacroName[256];
1829
1830	for (m = MetaMacros; m->metaname != '\0'; m++)
1831	{
1832		buf[0] = m->metaval;
1833		buf[1] = '\0';
1834		define(m->metaname, newstr(buf), e);
1835	}
1836	buf[0] = MATCHREPL;
1837	buf[2] = '\0';
1838	for (c = '0'; c <= '9'; c++)
1839	{
1840		buf[1] = c;
1841		define(c, newstr(buf), e);
1842	}
1843
1844	/* set defaults for some macros sendmail will use later */
1845	define('n', "MAILER-DAEMON", e);
1846
1847	/* set up external names for some internal macros */
1848	MACBINDING("opMode", MID_OPMODE);
1849	/*XXX should probably add equivalents for all short macros here XXX*/
1850}
1851/*
1852**  DISCONNECT -- remove our connection with any foreground process
1853**
1854**	Parameters:
1855**		droplev -- how "deeply" we should drop the line.
1856**			0 -- ignore signals, mail back errors, make sure
1857**			     output goes to stdout.
1858**			1 -- also, make stdout go to transcript.
1859**			2 -- also, disconnect from controlling terminal
1860**			     (only for daemon mode).
1861**		e -- the current envelope.
1862**
1863**	Returns:
1864**		none
1865**
1866**	Side Effects:
1867**		Trys to insure that we are immune to vagaries of
1868**		the controlling tty.
1869*/
1870
1871void
1872disconnect(droplev, e)
1873	int droplev;
1874	register ENVELOPE *e;
1875{
1876	int fd;
1877
1878	if (tTd(52, 1))
1879		printf("disconnect: In %d Out %d, e=%lx\n",
1880			fileno(InChannel), fileno(OutChannel), (u_long) e);
1881	if (tTd(52, 100))
1882	{
1883		printf("don't\n");
1884		return;
1885	}
1886	if (LogLevel > 93)
1887		sm_syslog(LOG_DEBUG, e->e_id,
1888			"disconnect level %d",
1889			droplev);
1890
1891	/* be sure we don't get nasty signals */
1892	(void) setsignal(SIGINT, SIG_IGN);
1893	(void) setsignal(SIGQUIT, SIG_IGN);
1894
1895	/* we can't communicate with our caller, so.... */
1896	HoldErrs = TRUE;
1897	CurEnv->e_errormode = EM_MAIL;
1898	Verbose = 0;
1899	DisConnected = TRUE;
1900
1901	/* all input from /dev/null */
1902	if (InChannel != stdin)
1903	{
1904		(void) fclose(InChannel);
1905		InChannel = stdin;
1906	}
1907	if (freopen("/dev/null", "r", stdin) == NULL)
1908		sm_syslog(LOG_ERR, e->e_id,
1909			  "disconnect: freopen(\"/dev/null\") failed: %s",
1910			  errstring(errno));
1911
1912	/* output to the transcript */
1913	if (OutChannel != stdout)
1914	{
1915		(void) fclose(OutChannel);
1916		OutChannel = stdout;
1917	}
1918	if (droplev > 0)
1919	{
1920		if (e->e_xfp == NULL)
1921		{
1922			fd = open("/dev/null", O_WRONLY, 0666);
1923			if (fd == -1)
1924				sm_syslog(LOG_ERR, e->e_id,
1925					  "disconnect: open(\"/dev/null\") failed: %s",
1926					  errstring(errno));
1927		}
1928		else
1929		{
1930			fd = fileno(e->e_xfp);
1931			if (fd == -1)
1932				sm_syslog(LOG_ERR, e->e_id,
1933					  "disconnect: fileno(e->e_xfp) failed: %s",
1934					  errstring(errno));
1935		}
1936		(void) fflush(stdout);
1937		dup2(fd, STDOUT_FILENO);
1938		dup2(fd, STDERR_FILENO);
1939		if (e->e_xfp == NULL)
1940			close(fd);
1941	}
1942
1943	/* drop our controlling TTY completely if possible */
1944	if (droplev > 1)
1945	{
1946		(void) setsid();
1947		errno = 0;
1948	}
1949
1950#if XDEBUG
1951	checkfd012("disconnect");
1952#endif
1953
1954	if (LogLevel > 71)
1955		sm_syslog(LOG_DEBUG, e->e_id,
1956			"in background, pid=%d",
1957			getpid());
1958
1959	errno = 0;
1960}
1961
1962static void
1963obsolete(argv)
1964	char *argv[];
1965{
1966	register char *ap;
1967	register char *op;
1968
1969	while ((ap = *++argv) != NULL)
1970	{
1971		/* Return if "--" or not an option of any form. */
1972		if (ap[0] != '-' || ap[1] == '-')
1973			return;
1974
1975		/* skip over options that do have a value */
1976		op = strchr(OPTIONS, ap[1]);
1977		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
1978		    ap[1] != 'd' &&
1979#if defined(sony_news)
1980		    ap[1] != 'E' && ap[1] != 'J' &&
1981#endif
1982		    argv[1] != NULL && argv[1][0] != '-')
1983		{
1984			argv++;
1985			continue;
1986		}
1987
1988		/* If -C doesn't have an argument, use sendmail.cf. */
1989#define	__DEFPATH	"sendmail.cf"
1990		if (ap[1] == 'C' && ap[2] == '\0')
1991		{
1992			*argv = xalloc(sizeof(__DEFPATH) + 2);
1993			argv[0][0] = '-';
1994			argv[0][1] = 'C';
1995			(void)strcpy(&argv[0][2], __DEFPATH);
1996		}
1997
1998		/* If -q doesn't have an argument, run it once. */
1999		if (ap[1] == 'q' && ap[2] == '\0')
2000			*argv = "-q0";
2001
2002		/* if -d doesn't have an argument, use 0-99.1 */
2003		if (ap[1] == 'd' && ap[2] == '\0')
2004			*argv = "-d0-99.1";
2005
2006# if defined(sony_news)
2007		/* if -E doesn't have an argument, use -EC */
2008		if (ap[1] == 'E' && ap[2] == '\0')
2009			*argv = "-EC";
2010
2011		/* if -J doesn't have an argument, use -JJ */
2012		if (ap[1] == 'J' && ap[2] == '\0')
2013			*argv = "-JJ";
2014# endif
2015	}
2016}
2017/*
2018**  AUTH_WARNING -- specify authorization warning
2019**
2020**	Parameters:
2021**		e -- the current envelope.
2022**		msg -- the text of the message.
2023**		args -- arguments to the message.
2024**
2025**	Returns:
2026**		none.
2027*/
2028
2029void
2030#ifdef __STDC__
2031auth_warning(register ENVELOPE *e, const char *msg, ...)
2032#else
2033auth_warning(e, msg, va_alist)
2034	register ENVELOPE *e;
2035	const char *msg;
2036	va_dcl
2037#endif
2038{
2039	char buf[MAXLINE];
2040	VA_LOCAL_DECL
2041
2042	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
2043	{
2044		register char *p;
2045		static char hostbuf[48];
2046		extern struct hostent *myhostname __P((char *, int));
2047
2048		if (hostbuf[0] == '\0')
2049			(void) myhostname(hostbuf, sizeof hostbuf);
2050
2051		(void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
2052		p = &buf[strlen(buf)];
2053		VA_START(msg);
2054		vsnprintf(p, SPACELEFT(buf, p), msg, ap);
2055		VA_END;
2056		addheader("X-Authentication-Warning", buf, &e->e_header);
2057		if (LogLevel > 3)
2058			sm_syslog(LOG_INFO, e->e_id,
2059				"Authentication-Warning: %.400s",
2060				buf);
2061	}
2062}
2063/*
2064**  GETEXTENV -- get from external environment
2065**
2066**	Parameters:
2067**		envar -- the name of the variable to retrieve
2068**
2069**	Returns:
2070**		The value, if any.
2071*/
2072
2073char *
2074getextenv(envar)
2075	const char *envar;
2076{
2077	char **envp;
2078	int l;
2079
2080	l = strlen(envar);
2081	for (envp = ExternalEnviron; *envp != NULL; envp++)
2082	{
2083		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
2084			return &(*envp)[l + 1];
2085	}
2086	return NULL;
2087}
2088/*
2089**  SETUSERENV -- set an environment in the propogated environment
2090**
2091**	Parameters:
2092**		envar -- the name of the environment variable.
2093**		value -- the value to which it should be set.  If
2094**			null, this is extracted from the incoming
2095**			environment.  If that is not set, the call
2096**			to setuserenv is ignored.
2097**
2098**	Returns:
2099**		none.
2100*/
2101
2102void
2103setuserenv(envar, value)
2104	const char *envar;
2105	const char *value;
2106{
2107	int i;
2108	char **evp = UserEnviron;
2109	char *p;
2110
2111	if (value == NULL)
2112	{
2113		value = getextenv(envar);
2114		if (value == NULL)
2115			return;
2116	}
2117
2118	i = strlen(envar);
2119	p = (char *) xalloc(strlen(value) + i + 2);
2120	strcpy(p, envar);
2121	p[i++] = '=';
2122	strcpy(&p[i], value);
2123
2124	while (*evp != NULL && strncmp(*evp, p, i) != 0)
2125		evp++;
2126	if (*evp != NULL)
2127	{
2128		*evp++ = p;
2129	}
2130	else if (evp < &UserEnviron[MAXUSERENVIRON])
2131	{
2132		*evp++ = p;
2133		*evp = NULL;
2134	}
2135
2136	/* make sure it is in our environment as well */
2137	if (putenv(p) < 0)
2138		syserr("setuserenv: putenv(%s) failed", p);
2139}
2140/*
2141**  DUMPSTATE -- dump state
2142**
2143**	For debugging.
2144*/
2145
2146void
2147dumpstate(when)
2148	char *when;
2149{
2150	register char *j = macvalue('j', CurEnv);
2151	int rs;
2152
2153	sm_syslog(LOG_DEBUG, CurEnv->e_id,
2154		"--- dumping state on %s: $j = %s ---",
2155		when,
2156		j == NULL ? "<NULL>" : j);
2157	if (j != NULL)
2158	{
2159		if (!wordinclass(j, 'w'))
2160			sm_syslog(LOG_DEBUG, CurEnv->e_id,
2161				"*** $j not in $=w ***");
2162	}
2163	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
2164	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
2165	printopenfds(TRUE);
2166	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
2167	mci_dump_all(TRUE);
2168	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
2169	if (rs > 0)
2170	{
2171		int stat;
2172		register char **pvp;
2173		char *pv[MAXATOM + 1];
2174
2175		pv[0] = NULL;
2176		stat = rewrite(pv, rs, 0, CurEnv);
2177		sm_syslog(LOG_DEBUG, CurEnv->e_id,
2178		       "--- ruleset debug_dumpstate returns stat %d, pv: ---",
2179		       stat);
2180		for (pvp = pv; *pvp != NULL; pvp++)
2181			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
2182	}
2183	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
2184}
2185
2186
2187/* ARGSUSED */
2188SIGFUNC_DECL
2189sigusr1(sig)
2190	int sig;
2191{
2192	dumpstate("user signal");
2193	return SIGFUNC_RETURN;
2194}
2195
2196
2197/* ARGSUSED */
2198SIGFUNC_DECL
2199sighup(sig)
2200	int sig;
2201{
2202	if (SaveArgv[0][0] != '/')
2203	{
2204		if (LogLevel > 3)
2205			sm_syslog(LOG_INFO, NOQID, "could not restart: need full path");
2206		finis(FALSE, EX_OSFILE);
2207	}
2208	if (LogLevel > 3)
2209		sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]);
2210	alarm(0);
2211	releasesignal(SIGHUP);
2212	closecontrolsocket(TRUE);
2213	if (drop_privileges(TRUE) != EX_OK)
2214	{
2215		if (LogLevel > 0)
2216			sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m",
2217				RunAsUid, RunAsGid);
2218		finis(FALSE, EX_OSERR);
2219	}
2220	execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
2221	if (LogLevel > 0)
2222		sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]);
2223	finis(FALSE, EX_OSFILE);
2224}
2225/*
2226**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
2227**
2228**	Parameters:
2229**		to_real_uid -- if set, drop to the real uid instead
2230**			of the RunAsUser.
2231**
2232**	Returns:
2233**		EX_OSERR if the setuid failed.
2234**		EX_OK otherwise.
2235*/
2236
2237int
2238drop_privileges(to_real_uid)
2239	bool to_real_uid;
2240{
2241	int rval = EX_OK;
2242	GIDSET_T emptygidset[1];
2243
2244	if (tTd(47, 1))
2245		printf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
2246			(int)to_real_uid, (int)RealUid, (int)RealGid, (int)RunAsUid, (int)RunAsGid);
2247
2248	if (to_real_uid)
2249	{
2250		RunAsUserName = RealUserName;
2251		RunAsUid = RealUid;
2252		RunAsGid = RealGid;
2253	}
2254
2255	/* make sure no one can grab open descriptors for secret files */
2256	endpwent();
2257
2258	/* reset group permissions; these can be set later */
2259	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
2260	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
2261		rval = EX_OSERR;
2262
2263	/* reset primary group and user id */
2264	if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
2265		rval = EX_OSERR;
2266	if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
2267		rval = EX_OSERR;
2268	if (tTd(47, 5))
2269	{
2270		printf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
2271			(int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
2272		printf("drop_privileges: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
2273	}
2274	return rval;
2275}
2276/*
2277**  FILL_FD -- make sure a file descriptor has been properly allocated
2278**
2279**	Used to make sure that stdin/out/err are allocated on startup
2280**
2281**	Parameters:
2282**		fd -- the file descriptor to be filled.
2283**		where -- a string used for logging.  If NULL, this is
2284**			being called on startup, and logging should
2285**			not be done.
2286**
2287**	Returns:
2288**		none
2289*/
2290
2291void
2292fill_fd(fd, where)
2293	int fd;
2294	char *where;
2295{
2296	int i;
2297	struct stat stbuf;
2298
2299	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
2300		return;
2301
2302	if (where != NULL)
2303		syserr("fill_fd: %s: fd %d not open", where, fd);
2304	else
2305		MissingFds |= 1 << fd;
2306	i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
2307	if (i < 0)
2308	{
2309		syserr("!fill_fd: %s: cannot open /dev/null",
2310			where == NULL ? "startup" : where);
2311	}
2312	if (fd != i)
2313	{
2314		(void) dup2(i, fd);
2315		(void) close(i);
2316	}
2317}
2318/*
2319**  TESTMODELINE -- process a test mode input line
2320**
2321**	Parameters:
2322**		line -- the input line.
2323**		e -- the current environment.
2324**	Syntax:
2325**		#  a comment
2326**		.X process X as a configuration line
2327**		=X dump a configuration item (such as mailers)
2328**		$X dump a macro or class
2329**		/X try an activity
2330**		X  normal process through rule set X
2331*/
2332
2333void
2334testmodeline(line, e)
2335	char *line;
2336	ENVELOPE *e;
2337{
2338	register char *p;
2339	char *q;
2340	auto char *delimptr;
2341	int mid;
2342	int i, rs;
2343	STAB *map;
2344	char **s;
2345	struct rewrite *rw;
2346	ADDRESS a;
2347	static int tryflags = RF_COPYNONE;
2348	char exbuf[MAXLINE];
2349	extern bool invalidaddr __P((char *, char *));
2350	extern char *crackaddr __P((char *));
2351	extern void dump_class __P((STAB *, int));
2352	extern void translate_dollars __P((char *));
2353	extern void help __P((char *));
2354
2355	switch (line[0])
2356	{
2357	  case '#':
2358	  case 0:
2359		return;
2360
2361	  case '?':
2362		help("-bt");
2363		return;
2364
2365	  case '.':		/* config-style settings */
2366		switch (line[1])
2367		{
2368		  case 'D':
2369			mid = macid(&line[2], &delimptr);
2370			if (mid == '\0')
2371				return;
2372			translate_dollars(delimptr);
2373			define(mid, newstr(delimptr), e);
2374			break;
2375
2376		  case 'C':
2377			if (line[2] == '\0')	/* not to call syserr() */
2378				return;
2379
2380			mid = macid(&line[2], &delimptr);
2381			if (mid == '\0')
2382				return;
2383			translate_dollars(delimptr);
2384			expand(delimptr, exbuf, sizeof exbuf, e);
2385			p = exbuf;
2386			while (*p != '\0')
2387			{
2388				register char *wd;
2389				char delim;
2390
2391				while (*p != '\0' && isascii(*p) && isspace(*p))
2392					p++;
2393				wd = p;
2394				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2395					p++;
2396				delim = *p;
2397				*p = '\0';
2398				if (wd[0] != '\0')
2399					setclass(mid, wd);
2400				*p = delim;
2401			}
2402			break;
2403
2404		  case '\0':
2405			printf("Usage: .[DC]macro value(s)\n");
2406			break;
2407
2408		  default:
2409			printf("Unknown \".\" command %s\n", line);
2410			break;
2411		}
2412		return;
2413
2414	  case '=':		/* config-style settings */
2415		switch (line[1])
2416		{
2417		  case 'S':		/* dump rule set */
2418			rs = strtorwset(&line[2], NULL, ST_FIND);
2419			if (rs < 0)
2420			{
2421				printf("Undefined ruleset %s\n", &line[2]);
2422				return;
2423			}
2424			rw = RewriteRules[rs];
2425			if (rw == NULL)
2426				return;
2427			do
2428			{
2429				putchar('R');
2430				s = rw->r_lhs;
2431				while (*s != NULL)
2432				{
2433					xputs(*s++);
2434					putchar(' ');
2435				}
2436				putchar('\t');
2437				putchar('\t');
2438				s = rw->r_rhs;
2439				while (*s != NULL)
2440				{
2441					xputs(*s++);
2442					putchar(' ');
2443				}
2444				putchar('\n');
2445			} while ((rw = rw->r_next) != NULL);
2446			break;
2447
2448		  case 'M':
2449			for (i = 0; i < MAXMAILERS; i++)
2450			{
2451				if (Mailer[i] != NULL)
2452					printmailer(Mailer[i]);
2453			}
2454			break;
2455
2456		  case '\0':
2457			printf("Usage: =Sruleset or =M\n");
2458			break;
2459
2460		  default:
2461			printf("Unknown \"=\" command %s\n", line);
2462			break;
2463		}
2464		return;
2465
2466	  case '-':		/* set command-line-like opts */
2467		switch (line[1])
2468		{
2469		  case 'd':
2470			tTflag(&line[2]);
2471			break;
2472
2473		  case '\0':
2474			printf("Usage: -d{debug arguments}\n");
2475			break;
2476
2477		  default:
2478			printf("Unknown \"-\" command %s\n", line);
2479			break;
2480		}
2481		return;
2482
2483	  case '$':
2484		if (line[1] == '=')
2485		{
2486			mid = macid(&line[2], NULL);
2487			if (mid != '\0')
2488				stabapply(dump_class, mid);
2489			return;
2490		}
2491		mid = macid(&line[1], NULL);
2492		if (mid == '\0')
2493			return;
2494		p = macvalue(mid, e);
2495		if (p == NULL)
2496			printf("Undefined\n");
2497		else
2498		{
2499			xputs(p);
2500			printf("\n");
2501		}
2502		return;
2503
2504	  case '/':		/* miscellaneous commands */
2505		p = &line[strlen(line)];
2506		while (--p >= line && isascii(*p) && isspace(*p))
2507			*p = '\0';
2508		p = strpbrk(line, " \t");
2509		if (p != NULL)
2510		{
2511			while (isascii(*p) && isspace(*p))
2512				*p++ = '\0';
2513		}
2514		else
2515			p = "";
2516		if (line[1] == '\0')
2517		{
2518			printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
2519			return;
2520		}
2521		if (strcasecmp(&line[1], "mx") == 0)
2522		{
2523#if NAMED_BIND
2524			/* look up MX records */
2525			int nmx;
2526			auto int rcode;
2527			char *mxhosts[MAXMXHOSTS + 1];
2528
2529			if (*p == '\0')
2530			{
2531				printf("Usage: /mx address\n");
2532				return;
2533			}
2534			nmx = getmxrr(p, mxhosts, FALSE, &rcode);
2535			printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
2536			for (i = 0; i < nmx; i++)
2537				printf("\t%s\n", mxhosts[i]);
2538#else
2539			printf("No MX code compiled in\n");
2540#endif
2541		}
2542		else if (strcasecmp(&line[1], "canon") == 0)
2543		{
2544			char host[MAXHOSTNAMELEN];
2545
2546			if (*p == '\0')
2547			{
2548				printf("Usage: /canon address\n");
2549				return;
2550			}
2551			else if (strlen(p) >= sizeof host)
2552			{
2553				printf("Name too long\n");
2554				return;
2555			}
2556			strcpy(host, p);
2557			(void) getcanonname(host, sizeof(host), HasWildcardMX);
2558			printf("getcanonname(%s) returns %s\n", p, host);
2559		}
2560		else if (strcasecmp(&line[1], "map") == 0)
2561		{
2562			auto int rcode = EX_OK;
2563			char *av[2];
2564
2565			if (*p == '\0')
2566			{
2567				printf("Usage: /map mapname key\n");
2568				return;
2569			}
2570			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
2571				continue;
2572			if (*q == '\0')
2573			{
2574				printf("No key specified\n");
2575				return;
2576			}
2577			*q++ = '\0';
2578			map = stab(p, ST_MAP, ST_FIND);
2579			if (map == NULL)
2580			{
2581				printf("Map named \"%s\" not found\n", p);
2582				return;
2583			}
2584			if (!bitset(MF_OPEN, map->s_map.map_mflags))
2585			{
2586				printf("Map named \"%s\" not open\n", p);
2587				return;
2588			}
2589			printf("map_lookup: %s (%s) ", p, q);
2590			av[0] = q;
2591			av[1] = NULL;
2592			p = (*map->s_map.map_class->map_lookup)
2593					(&map->s_map, q, av, &rcode);
2594			if (p == NULL)
2595				printf("no match (%d)\n", rcode);
2596			else
2597				printf("returns %s (%d)\n", p, rcode);
2598		}
2599		else if (strcasecmp(&line[1], "try") == 0)
2600		{
2601			MAILER *m;
2602			STAB *s;
2603			auto int rcode = EX_OK;
2604
2605			q = strpbrk(p, " \t");
2606			if (q != NULL)
2607			{
2608				while (isascii(*q) && isspace(*q))
2609					*q++ = '\0';
2610			}
2611			if (q == NULL || *q == '\0')
2612			{
2613				printf("Usage: /try mailer address\n");
2614				return;
2615			}
2616			s = stab(p, ST_MAILER, ST_FIND);
2617			if (s == NULL)
2618			{
2619				printf("Unknown mailer %s\n", p);
2620				return;
2621			}
2622			m = s->s_mailer;
2623			printf("Trying %s %s address %s for mailer %s\n",
2624				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
2625				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
2626				q, p);
2627			p = remotename(q, m, tryflags, &rcode, CurEnv);
2628			printf("Rcode = %d, addr = %s\n",
2629				rcode, p == NULL ? "<NULL>" : p);
2630			e->e_to = NULL;
2631		}
2632		else if (strcasecmp(&line[1], "tryflags") == 0)
2633		{
2634			if (*p == '\0')
2635			{
2636				printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
2637				return;
2638			}
2639			for (; *p != '\0'; p++)
2640			{
2641				switch (*p)
2642				{
2643				  case 'H':
2644				  case 'h':
2645					tryflags |= RF_HEADERADDR;
2646					break;
2647
2648				  case 'E':
2649				  case 'e':
2650					tryflags &= ~RF_HEADERADDR;
2651					break;
2652
2653				  case 'S':
2654				  case 's':
2655					tryflags |= RF_SENDERADDR;
2656					break;
2657
2658				  case 'R':
2659				  case 'r':
2660					tryflags &= ~RF_SENDERADDR;
2661					break;
2662				}
2663			}
2664		}
2665		else if (strcasecmp(&line[1], "parse") == 0)
2666		{
2667			if (*p == '\0')
2668			{
2669				printf("Usage: /parse address\n");
2670				return;
2671			}
2672			q = crackaddr(p);
2673			printf("Cracked address = ");
2674			xputs(q);
2675			printf("\nParsing %s %s address\n",
2676				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
2677				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
2678			if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
2679				printf("Cannot parse\n");
2680			else if (a.q_host != NULL && a.q_host[0] != '\0')
2681				printf("mailer %s, host %s, user %s\n",
2682					a.q_mailer->m_name, a.q_host, a.q_user);
2683			else
2684				printf("mailer %s, user %s\n",
2685					a.q_mailer->m_name, a.q_user);
2686			e->e_to = NULL;
2687		}
2688		else
2689		{
2690			printf("Unknown \"/\" command %s\n", line);
2691		}
2692		return;
2693	}
2694
2695	for (p = line; isascii(*p) && isspace(*p); p++)
2696		continue;
2697	q = p;
2698	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2699		p++;
2700	if (*p == '\0')
2701	{
2702		printf("No address!\n");
2703		return;
2704	}
2705	*p = '\0';
2706	if (invalidaddr(p + 1, NULL))
2707		return;
2708	do
2709	{
2710		register char **pvp;
2711		char pvpbuf[PSBUFSIZE];
2712
2713		pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
2714			      &delimptr, NULL);
2715		if (pvp == NULL)
2716			continue;
2717		p = q;
2718		while (*p != '\0')
2719		{
2720			int stat;
2721
2722			rs = strtorwset(p, NULL, ST_FIND);
2723			if (rs < 0)
2724			{
2725				printf("Undefined ruleset %s\n", p);
2726				break;
2727			}
2728			stat = rewrite(pvp, rs, 0, e);
2729			if (stat != EX_OK)
2730				printf("== Ruleset %s (%d) status %d\n",
2731					p, rs, stat);
2732			while (*p != '\0' && *p++ != ',')
2733				continue;
2734		}
2735	} while (*(p = delimptr) != '\0');
2736}
2737
2738
2739void
2740dump_class(s, id)
2741	register STAB *s;
2742	int id;
2743{
2744	if (s->s_type != ST_CLASS)
2745		return;
2746	if (bitnset(id & 0xff, s->s_class))
2747		printf("%s\n", s->s_name);
2748}
2749