main.c revision 53696
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	if (OpMode == MD_INITALIAS &&
988	    RealUid != 0 &&
989	    RealUid != TrustedUid &&
990	    !wordinclass(RealUserName, 't'))
991	{
992		if (LogLevel > 1)
993			sm_syslog(LOG_ALERT, NOQID,
994				  "user %d attempted to rebuild the alias map",
995				  RealUid);
996 		usrerr("Permission denied");
997 		finis(FALSE, EX_USAGE);
998 	}
999
1000	if (MeToo)
1001		BlankEnvelope.e_flags |= EF_METOO;
1002
1003	switch (OpMode)
1004	{
1005	  case MD_TEST:
1006		/* don't have persistent host status in test mode */
1007		HostStatDir = NULL;
1008		if (Verbose == 0)
1009			Verbose = 2;
1010		CurEnv->e_errormode = EM_PRINT;
1011		HoldErrs = FALSE;
1012		break;
1013
1014	  case MD_VERIFY:
1015		CurEnv->e_errormode = EM_PRINT;
1016		HoldErrs = FALSE;
1017		/* arrange to exit cleanly on hangup signal */
1018		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1019			setsignal(SIGHUP, intsig);
1020		break;
1021
1022	  case MD_FGDAEMON:
1023		run_in_foreground = TRUE;
1024		OpMode = MD_DAEMON;
1025		/* fall through ... */
1026
1027	  case MD_DAEMON:
1028		vendor_daemon_setup(CurEnv);
1029
1030		/* remove things that don't make sense in daemon mode */
1031		FullName = NULL;
1032		GrabTo = FALSE;
1033
1034		/* arrange to restart on hangup signal */
1035		if (SaveArgv[0] == NULL || SaveArgv[0][0] != '/')
1036			sm_syslog(LOG_WARNING, NOQID,
1037				"daemon invoked without full pathname; kill -1 won't work");
1038		setsignal(SIGHUP, sighup);
1039
1040		/* workaround: can't seem to release the signal in the parent */
1041		releasesignal(SIGHUP);
1042		break;
1043
1044	  case MD_INITALIAS:
1045		Verbose = 2;
1046		CurEnv->e_errormode = EM_PRINT;
1047		HoldErrs = FALSE;
1048		/* fall through... */
1049
1050	  case MD_PRINT:
1051		/* to handle sendmail -bp -qSfoobar properly */
1052		queuemode = FALSE;
1053		/* fall through... */
1054
1055	  default:
1056		/* arrange to exit cleanly on hangup signal */
1057		if (setsignal(SIGHUP, SIG_IGN) == (sigfunc_t) SIG_DFL)
1058			setsignal(SIGHUP, intsig);
1059		break;
1060	}
1061
1062	/* special considerations for FullName */
1063	if (FullName != NULL)
1064	{
1065		char *full = NULL;
1066		extern bool rfc822_string __P((char *));
1067
1068		/* full names can't have newlines */
1069		if (strchr(FullName, '\n') != NULL)
1070		{
1071			FullName = full = newstr(denlstring(FullName, TRUE, TRUE));
1072		}
1073		/* check for characters that may have to be quoted */
1074		if (!rfc822_string(FullName))
1075		{
1076			extern char *addquotes __P((char *));
1077
1078			/*
1079			**  Quote a full name with special characters
1080			**  as a comment so crackaddr() doesn't destroy
1081			**  the name portion of the address.
1082			*/
1083			FullName = addquotes(FullName);
1084			if (full != NULL)
1085				free(full);
1086		}
1087	}
1088
1089	/* do heuristic mode adjustment */
1090	if (Verbose)
1091	{
1092		/* turn off noconnect option */
1093		setoption('c', "F", TRUE, FALSE, CurEnv);
1094
1095		/* turn on interactive delivery */
1096		setoption('d', "", TRUE, FALSE, CurEnv);
1097	}
1098
1099#ifdef VENDOR_CODE
1100	/* check for vendor mismatch */
1101	if (VendorCode != VENDOR_CODE)
1102	{
1103		extern char *getvendor __P((int));
1104
1105		message("Warning: .cf file vendor code mismatch: sendmail expects vendor %s, .cf file vendor is %s",
1106			getvendor(VENDOR_CODE), getvendor(VendorCode));
1107	}
1108#endif
1109
1110	/* check for out of date configuration level */
1111	if (ConfigLevel < MAXCONFIGLEVEL)
1112	{
1113		message("Warning: .cf file is out of date: sendmail %s supports version %d, .cf file is version %d",
1114			Version, MAXCONFIGLEVEL, ConfigLevel);
1115	}
1116
1117	if (ConfigLevel < 3)
1118	{
1119		UseErrorsTo = TRUE;
1120	}
1121
1122	/* set options that were previous macros */
1123	if (SmtpGreeting == NULL)
1124	{
1125		if (ConfigLevel < 7 && (p = macvalue('e', CurEnv)) != NULL)
1126			SmtpGreeting = newstr(p);
1127		else
1128			SmtpGreeting = "\201j Sendmail \201v ready at \201b";
1129	}
1130	if (UnixFromLine == NULL)
1131	{
1132		if (ConfigLevel < 7 && (p = macvalue('l', CurEnv)) != NULL)
1133			UnixFromLine = newstr(p);
1134		else
1135			UnixFromLine = "From \201g  \201d";
1136	}
1137
1138	/* our name for SMTP codes */
1139	expand("\201j", jbuf, sizeof jbuf, CurEnv);
1140	MyHostName = jbuf;
1141	if (strchr(jbuf, '.') == NULL)
1142		message("WARNING: local host name (%s) is not qualified; fix $j in config file",
1143			jbuf);
1144
1145	/* make certain that this name is part of the $=w class */
1146	setclass('w', MyHostName);
1147
1148	/* the indices of built-in mailers */
1149	st = stab("local", ST_MAILER, ST_FIND);
1150	if (st != NULL)
1151		LocalMailer = st->s_mailer;
1152	else if (OpMode != MD_TEST || !warn_C_flag)
1153		syserr("No local mailer defined");
1154
1155	st = stab("prog", ST_MAILER, ST_FIND);
1156	if (st == NULL)
1157		syserr("No prog mailer defined");
1158	else
1159	{
1160		ProgMailer = st->s_mailer;
1161		clrbitn(M_MUSER, ProgMailer->m_flags);
1162	}
1163
1164	st = stab("*file*", ST_MAILER, ST_FIND);
1165	if (st == NULL)
1166		syserr("No *file* mailer defined");
1167	else
1168	{
1169		FileMailer = st->s_mailer;
1170		clrbitn(M_MUSER, FileMailer->m_flags);
1171	}
1172
1173	st = stab("*include*", ST_MAILER, ST_FIND);
1174	if (st == NULL)
1175		syserr("No *include* mailer defined");
1176	else
1177		InclMailer = st->s_mailer;
1178
1179	if (ConfigLevel < 6)
1180	{
1181		/* heuristic tweaking of local mailer for back compat */
1182		if (LocalMailer != NULL)
1183		{
1184			setbitn(M_ALIASABLE, LocalMailer->m_flags);
1185			setbitn(M_HASPWENT, LocalMailer->m_flags);
1186			setbitn(M_TRYRULESET5, LocalMailer->m_flags);
1187			setbitn(M_CHECKINCLUDE, LocalMailer->m_flags);
1188			setbitn(M_CHECKPROG, LocalMailer->m_flags);
1189			setbitn(M_CHECKFILE, LocalMailer->m_flags);
1190			setbitn(M_CHECKUDB, LocalMailer->m_flags);
1191		}
1192		if (ProgMailer != NULL)
1193			setbitn(M_RUNASRCPT, ProgMailer->m_flags);
1194		if (FileMailer != NULL)
1195			setbitn(M_RUNASRCPT, FileMailer->m_flags);
1196	}
1197	if (ConfigLevel < 7)
1198	{
1199		if (LocalMailer != NULL)
1200			setbitn(M_VRFY250, LocalMailer->m_flags);
1201		if (ProgMailer != NULL)
1202			setbitn(M_VRFY250, ProgMailer->m_flags);
1203		if (FileMailer != NULL)
1204			setbitn(M_VRFY250, FileMailer->m_flags);
1205	}
1206
1207	/* MIME Content-Types that cannot be transfer encoded */
1208	setclass('n', "multipart/signed");
1209
1210	/* MIME message/xxx subtypes that can be treated as messages */
1211	setclass('s', "rfc822");
1212
1213	/* MIME Content-Transfer-Encodings that can be encoded */
1214	setclass('e', "7bit");
1215	setclass('e', "8bit");
1216	setclass('e', "binary");
1217
1218#ifdef USE_B_CLASS
1219	/* MIME Content-Types that should be treated as binary */
1220	setclass('b', "image");
1221	setclass('b', "audio");
1222	setclass('b', "video");
1223	setclass('b', "application/octet-stream");
1224#endif
1225
1226#if _FFR_MAX_MIME_HEADER_LENGTH
1227	/* MIME headers which have fields to check for overflow */
1228	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-disposition");
1229	setclass(macid("{checkMIMEFieldHeaders}", NULL), "content-type");
1230
1231	/* MIME headers to check for length overflow */
1232	setclass(macid("{checkMIMETextHeaders}", NULL), "content-description");
1233
1234	/* MIME headers to check for overflow and rebalance */
1235	setclass(macid("{checkMIMEHeaders}", NULL), "content-disposition");
1236	setclass(macid("{checkMIMEHeaders}", NULL), "content-id");
1237	setclass(macid("{checkMIMEHeaders}", NULL), "content-transfer-encoding");
1238	setclass(macid("{checkMIMEHeaders}", NULL), "content-type");
1239	setclass(macid("{checkMIMEHeaders}", NULL), "mime-version");
1240#endif
1241
1242	/* operate in queue directory */
1243	if (QueueDir == NULL)
1244	{
1245		if (OpMode != MD_TEST)
1246		{
1247			syserr("QueueDirectory (Q) option must be set");
1248			ExitStat = EX_CONFIG;
1249		}
1250	}
1251	else
1252	{
1253		/* test path to get warning messages */
1254		(void) safedirpath(QueueDir, (uid_t) 0, (gid_t) 0, NULL, SFF_ANYFILE);
1255		if (OpMode != MD_TEST && chdir(QueueDir) < 0)
1256		{
1257			syserr("cannot chdir(%s)", QueueDir);
1258			ExitStat = EX_CONFIG;
1259		}
1260	}
1261
1262	/* check host status directory for validity */
1263	if (HostStatDir != NULL && !path_is_dir(HostStatDir, FALSE))
1264	{
1265		/* cannot use this value */
1266		if (tTd(0, 2))
1267			printf("Cannot use HostStatusDirectory = %s: %s\n",
1268				HostStatDir, errstring(errno));
1269		HostStatDir = NULL;
1270	}
1271
1272# if QUEUE
1273	if (queuemode && RealUid != 0 && bitset(PRIV_RESTRICTQRUN, PrivacyFlags))
1274	{
1275		struct stat stbuf;
1276
1277		/* check to see if we own the queue directory */
1278		if (stat(".", &stbuf) < 0)
1279			syserr("main: cannot stat %s", QueueDir);
1280		if (stbuf.st_uid != RealUid)
1281		{
1282			/* nope, really a botch */
1283			usrerr("You do not have permission to process the queue");
1284			finis(FALSE, EX_NOPERM);
1285		}
1286	}
1287# endif /* QUEUE */
1288
1289	/* if we've had errors so far, exit now */
1290	if (ExitStat != EX_OK && OpMode != MD_TEST)
1291		finis(FALSE, ExitStat);
1292
1293#if XDEBUG
1294	checkfd012("before main() initmaps");
1295#endif
1296
1297	/*
1298	**  Do operation-mode-dependent initialization.
1299	*/
1300
1301	switch (OpMode)
1302	{
1303	  case MD_PRINT:
1304		/* print the queue */
1305#if QUEUE
1306		dropenvelope(CurEnv, TRUE);
1307		signal(SIGPIPE, quiesce);
1308		printqueue();
1309		finis(FALSE, EX_OK);
1310#else /* QUEUE */
1311		usrerr("No queue to print");
1312		finis(FALSE, ExitStat);
1313#endif /* QUEUE */
1314		break;
1315
1316	  case MD_HOSTSTAT:
1317		signal(SIGPIPE, quiesce);
1318		mci_traverse_persistent(mci_print_persistent, NULL);
1319		finis(FALSE, EX_OK);
1320	    	break;
1321
1322	  case MD_PURGESTAT:
1323		mci_traverse_persistent(mci_purge_persistent, NULL);
1324		finis(FALSE, EX_OK);
1325	    	break;
1326
1327	  case MD_INITALIAS:
1328		/* initialize maps */
1329		initmaps(TRUE, CurEnv);
1330		finis(FALSE, ExitStat);
1331		break;
1332
1333	  case MD_SMTP:
1334	  case MD_DAEMON:
1335		/* reset DSN parameters */
1336		DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
1337		CurEnv->e_envid = NULL;
1338		CurEnv->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
1339
1340		/* don't open maps for daemon -- done below in child */
1341		break;
1342
1343	  default:
1344		/* open the maps */
1345		initmaps(FALSE, CurEnv);
1346		break;
1347	}
1348
1349	if (tTd(0, 15))
1350	{
1351		extern void printrules __P((void));
1352
1353		/* print configuration table (or at least part of it) */
1354		if (tTd(0, 90))
1355			printrules();
1356		for (i = 0; i < MAXMAILERS; i++)
1357		{
1358			if (Mailer[i] != NULL)
1359				printmailer(Mailer[i]);
1360		}
1361	}
1362
1363	/*
1364	**  Switch to the main envelope.
1365	*/
1366
1367	CurEnv = newenvelope(&MainEnvelope, CurEnv);
1368	MainEnvelope.e_flags = BlankEnvelope.e_flags;
1369
1370	/*
1371	**  If test mode, read addresses from stdin and process.
1372	*/
1373
1374	if (OpMode == MD_TEST)
1375	{
1376		char buf[MAXLINE];
1377		SIGFUNC_DECL intindebug __P((int));
1378
1379		if (isatty(fileno(stdin)))
1380			Verbose = 2;
1381
1382		if (Verbose)
1383		{
1384			printf("ADDRESS TEST MODE (ruleset 3 NOT automatically invoked)\n");
1385			printf("Enter <ruleset> <address>\n");
1386		}
1387		if (setjmp(TopFrame) > 0)
1388			printf("\n");
1389		(void) setsignal(SIGINT, intindebug);
1390		for (;;)
1391		{
1392			extern void testmodeline __P((char *, ENVELOPE *));
1393
1394			if (Verbose == 2)
1395				printf("> ");
1396			(void) fflush(stdout);
1397			if (fgets(buf, sizeof buf, stdin) == NULL)
1398				finis(TRUE, ExitStat);
1399			p = strchr(buf, '\n');
1400			if (p != NULL)
1401				*p = '\0';
1402			if (Verbose < 2)
1403				printf("> %s\n", buf);
1404			testmodeline(buf, CurEnv);
1405		}
1406	}
1407
1408# if QUEUE
1409	/*
1410	**  If collecting stuff from the queue, go start doing that.
1411	*/
1412
1413	if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
1414	{
1415		(void) runqueue(FALSE, Verbose);
1416		finis(TRUE, ExitStat);
1417	}
1418# endif /* QUEUE */
1419
1420	/*
1421	**  If a daemon, wait for a request.
1422	**	getrequests will always return in a child.
1423	**	If we should also be processing the queue, start
1424	**		doing it in background.
1425	**	We check for any errors that might have happened
1426	**		during startup.
1427	*/
1428
1429	if (OpMode == MD_DAEMON || QueueIntvl != 0)
1430	{
1431		char dtype[200];
1432		extern void getrequests __P((ENVELOPE *));
1433
1434		if (!run_in_foreground && !tTd(99, 100))
1435		{
1436			/* put us in background */
1437			i = fork();
1438			if (i < 0)
1439				syserr("daemon: cannot fork");
1440			if (i != 0)
1441				finis(FALSE, EX_OK);
1442
1443			/* disconnect from our controlling tty */
1444			disconnect(2, CurEnv);
1445		}
1446
1447		dtype[0] = '\0';
1448		if (OpMode == MD_DAEMON)
1449			strcat(dtype, "+SMTP");
1450		if (QueueIntvl != 0)
1451		{
1452			strcat(dtype, "+queueing@");
1453			strcat(dtype, pintvl(QueueIntvl, TRUE));
1454		}
1455		if (tTd(0, 1))
1456			strcat(dtype, "+debugging");
1457
1458		sm_syslog(LOG_INFO, NOQID,
1459			"starting daemon (%s): %s", Version, dtype + 1);
1460#ifdef XLA
1461		xla_create_file();
1462#endif
1463
1464# if QUEUE
1465		if (queuemode)
1466		{
1467			(void) runqueue(TRUE, FALSE);
1468			if (OpMode != MD_DAEMON)
1469			{
1470				for (;;)
1471				{
1472					pause();
1473					if (DoQueueRun)
1474						(void) runqueue(TRUE, FALSE);
1475				}
1476			}
1477		}
1478# endif /* QUEUE */
1479		dropenvelope(CurEnv, TRUE);
1480
1481#if DAEMON
1482		getrequests(CurEnv);
1483
1484		/* drop privileges */
1485		(void) drop_privileges(FALSE);
1486
1487		/* at this point we are in a child: reset state */
1488		(void) newenvelope(CurEnv, CurEnv);
1489
1490		/*
1491		**  Get authentication data
1492		*/
1493
1494		p = getauthinfo(fileno(InChannel), &forged);
1495		define('_', p, &BlankEnvelope);
1496#endif /* DAEMON */
1497	}
1498
1499# if SMTP
1500	/*
1501	**  If running SMTP protocol, start collecting and executing
1502	**  commands.  This will never return.
1503	*/
1504
1505	if (OpMode == MD_SMTP || OpMode == MD_DAEMON)
1506	{
1507		char pbuf[20];
1508		extern void smtp __P((char *, ENVELOPE *));
1509
1510		/*
1511		**  Save some macros for check_* rulesets.
1512		*/
1513
1514		if (forged)
1515		{
1516			char ipbuf[103];
1517
1518			snprintf(ipbuf, sizeof ipbuf, "[%.100s]",
1519				 inet_ntoa(RealHostAddr.sin.sin_addr));
1520
1521			define(macid("{client_name}", NULL),
1522			       newstr(ipbuf), &BlankEnvelope);
1523		}
1524		else
1525			define(macid("{client_name}", NULL), RealHostName, &BlankEnvelope);
1526		define(macid("{client_addr}", NULL),
1527		       newstr(anynet_ntoa(&RealHostAddr)), &BlankEnvelope);
1528		if (RealHostAddr.sa.sa_family == AF_INET)
1529			snprintf(pbuf, sizeof pbuf, "%d", RealHostAddr.sin.sin_port);
1530		else
1531			snprintf(pbuf, sizeof pbuf, "0");
1532		define(macid("{client_port}", NULL), newstr(pbuf), &BlankEnvelope);
1533
1534		/* initialize maps now for check_relay ruleset */
1535		initmaps(FALSE, CurEnv);
1536
1537		if (OpMode == MD_DAEMON)
1538		{
1539			/* validate the connection */
1540			HoldErrs = TRUE;
1541			nullserver = validate_connection(&RealHostAddr,
1542							 RealHostName, CurEnv);
1543			HoldErrs = FALSE;
1544		}
1545		smtp(nullserver, CurEnv);
1546	}
1547# endif /* SMTP */
1548
1549	clearenvelope(CurEnv, FALSE);
1550	if (OpMode == MD_VERIFY)
1551	{
1552		CurEnv->e_sendmode = SM_VERIFY;
1553		PostMasterCopy = NULL;
1554	}
1555	else
1556	{
1557		/* interactive -- all errors are global */
1558		CurEnv->e_flags |= EF_GLOBALERRS|EF_LOGSENDER;
1559	}
1560
1561	/*
1562	**  Do basic system initialization and set the sender
1563	*/
1564
1565	initsys(CurEnv);
1566	if (warn_f_flag != '\0' && !wordinclass(RealUserName, 't'))
1567		auth_warning(CurEnv, "%s set sender to %s using -%c",
1568			RealUserName, from, warn_f_flag);
1569	setsender(from, CurEnv, NULL, '\0', FALSE);
1570	if (macvalue('s', CurEnv) == NULL)
1571		define('s', RealHostName, CurEnv);
1572
1573	if (*av == NULL && !GrabTo)
1574	{
1575		CurEnv->e_flags |= EF_GLOBALERRS;
1576		usrerr("Recipient names must be specified");
1577
1578		/* collect body for UUCP return */
1579		if (OpMode != MD_VERIFY)
1580			collect(InChannel, FALSE, NULL, CurEnv);
1581		finis(TRUE, ExitStat);
1582	}
1583
1584	/*
1585	**  Scan argv and deliver the message to everyone.
1586	*/
1587
1588	sendtoargv(av, CurEnv);
1589
1590	/* if we have had errors sofar, arrange a meaningful exit stat */
1591	if (Errors > 0 && ExitStat == EX_OK)
1592		ExitStat = EX_USAGE;
1593
1594#if _FFR_FIX_DASHT
1595	/*
1596	**  If using -t, force not sending to argv recipients, even
1597	**  if they are mentioned in the headers.
1598	*/
1599
1600	if (GrabTo)
1601	{
1602		ADDRESS *q;
1603
1604		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
1605			q->q_flags |= QDONTSEND;
1606	}
1607#endif
1608
1609	/*
1610	**  Read the input mail.
1611	*/
1612
1613	CurEnv->e_to = NULL;
1614	if (OpMode != MD_VERIFY || GrabTo)
1615	{
1616		long savedflags = CurEnv->e_flags & EF_FATALERRS;
1617
1618		CurEnv->e_flags |= EF_GLOBALERRS;
1619		CurEnv->e_flags &= ~EF_FATALERRS;
1620		collect(InChannel, FALSE, NULL, CurEnv);
1621
1622		/* bail out if message too large */
1623		if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
1624		{
1625			finis(TRUE, ExitStat);
1626			/*NOTREACHED*/
1627			return -1;
1628		}
1629		CurEnv->e_flags |= savedflags;
1630	}
1631	errno = 0;
1632
1633	if (tTd(1, 1))
1634		printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
1635
1636	/*
1637	**  Actually send everything.
1638	**	If verifying, just ack.
1639	*/
1640
1641	CurEnv->e_from.q_flags |= QDONTSEND;
1642	if (tTd(1, 5))
1643	{
1644		printf("main: QDONTSEND ");
1645		printaddr(&CurEnv->e_from, FALSE);
1646	}
1647	CurEnv->e_to = NULL;
1648	CurrentLA = getla();
1649	GrabTo = FALSE;
1650	sendall(CurEnv, SM_DEFAULT);
1651
1652	/*
1653	**  All done.
1654	**	Don't send return error message if in VERIFY mode.
1655	*/
1656
1657	finis(TRUE, ExitStat);
1658	/*NOTREACHED*/
1659	return -1;
1660}
1661
1662/* ARGSUSED */
1663SIGFUNC_DECL
1664quiesce(sig)
1665	int sig;
1666{
1667	finis(FALSE, EX_OK);
1668}
1669
1670/* ARGSUSED */
1671SIGFUNC_DECL
1672intindebug(sig)
1673	int sig;
1674{
1675	longjmp(TopFrame, 1);
1676	return SIGFUNC_RETURN;
1677}
1678
1679
1680/*
1681**  FINIS -- Clean up and exit.
1682**
1683**	Parameters:
1684**		drop -- whether or not to drop CurEnv envelope
1685**		exitstat -- exit status to use for exit() call
1686**
1687**	Returns:
1688**		never
1689**
1690**	Side Effects:
1691**		exits sendmail
1692*/
1693
1694void
1695finis(drop, exitstat)
1696	bool drop;
1697	volatile int exitstat;
1698{
1699	extern void closemaps __P((void));
1700#ifdef USERDB
1701	extern void _udbx_close __P((void));
1702#endif
1703
1704	if (tTd(2, 1))
1705	{
1706		extern void printenvflags __P((ENVELOPE *));
1707
1708		printf("\n====finis: stat %d e_id=%s e_flags=",
1709			exitstat,
1710			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id);
1711		printenvflags(CurEnv);
1712	}
1713	if (tTd(2, 9))
1714		printopenfds(FALSE);
1715
1716	/* if we fail in finis(), just exit */
1717	if (setjmp(TopFrame) != 0)
1718	{
1719		/* failed -- just give it up */
1720		goto forceexit;
1721	}
1722
1723	/* clean up temp files */
1724	CurEnv->e_to = NULL;
1725	if (drop && CurEnv->e_id != NULL)
1726		dropenvelope(CurEnv, TRUE);
1727
1728	/* flush any cached connections */
1729	mci_flush(TRUE, NULL);
1730
1731	/* close maps belonging to this pid */
1732	closemaps();
1733
1734#ifdef USERDB
1735	/* close UserDatabase */
1736	_udbx_close();
1737#endif
1738
1739# ifdef XLA
1740	/* clean up extended load average stuff */
1741	xla_all_end();
1742# endif
1743
1744	/* and exit */
1745  forceexit:
1746	if (LogLevel > 78)
1747		sm_syslog(LOG_DEBUG, CurEnv->e_id,
1748			"finis, pid=%d",
1749			getpid());
1750	if (exitstat == EX_TEMPFAIL || CurEnv->e_errormode == EM_BERKNET)
1751		exitstat = EX_OK;
1752
1753	/* reset uid for process accounting */
1754	endpwent();
1755	setuid(RealUid);
1756
1757	exit(exitstat);
1758}
1759/*
1760**  INTSIG -- clean up on interrupt
1761**
1762**	This just arranges to exit.  It pessimises in that it
1763**	may resend a message.
1764**
1765**	Parameters:
1766**		none.
1767**
1768**	Returns:
1769**		none.
1770**
1771**	Side Effects:
1772**		Unlocks the current job.
1773*/
1774
1775/* ARGSUSED */
1776SIGFUNC_DECL
1777intsig(sig)
1778	int sig;
1779{
1780	if (LogLevel > 79)
1781		sm_syslog(LOG_DEBUG, CurEnv->e_id, "interrupt");
1782	FileName = NULL;
1783	unlockqueue(CurEnv);
1784	closecontrolsocket(TRUE);
1785#ifdef XLA
1786	xla_all_end();
1787#endif
1788	finis(FALSE, EX_OK);
1789}
1790/*
1791**  INITMACROS -- initialize the macro system
1792**
1793**	This just involves defining some macros that are actually
1794**	used internally as metasymbols to be themselves.
1795**
1796**	Parameters:
1797**		none.
1798**
1799**	Returns:
1800**		none.
1801**
1802**	Side Effects:
1803**		initializes several macros to be themselves.
1804*/
1805
1806struct metamac	MetaMacros[] =
1807{
1808	/* LHS pattern matching characters */
1809	{ '*', MATCHZANY },	{ '+', MATCHANY },	{ '-', MATCHONE },
1810	{ '=', MATCHCLASS },	{ '~', MATCHNCLASS },
1811
1812	/* these are RHS metasymbols */
1813	{ '#', CANONNET },	{ '@', CANONHOST },	{ ':', CANONUSER },
1814	{ '>', CALLSUBR },
1815
1816	/* the conditional operations */
1817	{ '?', CONDIF },	{ '|', CONDELSE },	{ '.', CONDFI },
1818
1819	/* the hostname lookup characters */
1820	{ '[', HOSTBEGIN },	{ ']', HOSTEND },
1821	{ '(', LOOKUPBEGIN },	{ ')', LOOKUPEND },
1822
1823	/* miscellaneous control characters */
1824	{ '&', MACRODEXPAND },
1825
1826	{ '\0' }
1827};
1828
1829#define MACBINDING(name, mid) \
1830		stab(name, ST_MACRO, ST_ENTER)->s_macro = mid; \
1831		MacroName[mid] = name;
1832
1833void
1834initmacros(e)
1835	register ENVELOPE *e;
1836{
1837	register struct metamac *m;
1838	register int c;
1839	char buf[5];
1840	extern char *MacroName[256];
1841
1842	for (m = MetaMacros; m->metaname != '\0'; m++)
1843	{
1844		buf[0] = m->metaval;
1845		buf[1] = '\0';
1846		define(m->metaname, newstr(buf), e);
1847	}
1848	buf[0] = MATCHREPL;
1849	buf[2] = '\0';
1850	for (c = '0'; c <= '9'; c++)
1851	{
1852		buf[1] = c;
1853		define(c, newstr(buf), e);
1854	}
1855
1856	/* set defaults for some macros sendmail will use later */
1857	define('n', "MAILER-DAEMON", e);
1858
1859	/* set up external names for some internal macros */
1860	MACBINDING("opMode", MID_OPMODE);
1861	/*XXX should probably add equivalents for all short macros here XXX*/
1862}
1863/*
1864**  DISCONNECT -- remove our connection with any foreground process
1865**
1866**	Parameters:
1867**		droplev -- how "deeply" we should drop the line.
1868**			0 -- ignore signals, mail back errors, make sure
1869**			     output goes to stdout.
1870**			1 -- also, make stdout go to transcript.
1871**			2 -- also, disconnect from controlling terminal
1872**			     (only for daemon mode).
1873**		e -- the current envelope.
1874**
1875**	Returns:
1876**		none
1877**
1878**	Side Effects:
1879**		Trys to insure that we are immune to vagaries of
1880**		the controlling tty.
1881*/
1882
1883void
1884disconnect(droplev, e)
1885	int droplev;
1886	register ENVELOPE *e;
1887{
1888	int fd;
1889
1890	if (tTd(52, 1))
1891		printf("disconnect: In %d Out %d, e=%lx\n",
1892			fileno(InChannel), fileno(OutChannel), (u_long) e);
1893	if (tTd(52, 100))
1894	{
1895		printf("don't\n");
1896		return;
1897	}
1898	if (LogLevel > 93)
1899		sm_syslog(LOG_DEBUG, e->e_id,
1900			"disconnect level %d",
1901			droplev);
1902
1903	/* be sure we don't get nasty signals */
1904	(void) setsignal(SIGINT, SIG_IGN);
1905	(void) setsignal(SIGQUIT, SIG_IGN);
1906
1907	/* we can't communicate with our caller, so.... */
1908	HoldErrs = TRUE;
1909	CurEnv->e_errormode = EM_MAIL;
1910	Verbose = 0;
1911	DisConnected = TRUE;
1912
1913	/* all input from /dev/null */
1914	if (InChannel != stdin)
1915	{
1916		(void) fclose(InChannel);
1917		InChannel = stdin;
1918	}
1919	if (freopen("/dev/null", "r", stdin) == NULL)
1920		sm_syslog(LOG_ERR, e->e_id,
1921			  "disconnect: freopen(\"/dev/null\") failed: %s",
1922			  errstring(errno));
1923
1924	/* output to the transcript */
1925	if (OutChannel != stdout)
1926	{
1927		(void) fclose(OutChannel);
1928		OutChannel = stdout;
1929	}
1930	if (droplev > 0)
1931	{
1932		if (e->e_xfp == NULL)
1933		{
1934			fd = open("/dev/null", O_WRONLY, 0666);
1935			if (fd == -1)
1936				sm_syslog(LOG_ERR, e->e_id,
1937					  "disconnect: open(\"/dev/null\") failed: %s",
1938					  errstring(errno));
1939		}
1940		else
1941		{
1942			fd = fileno(e->e_xfp);
1943			if (fd == -1)
1944				sm_syslog(LOG_ERR, e->e_id,
1945					  "disconnect: fileno(e->e_xfp) failed: %s",
1946					  errstring(errno));
1947		}
1948		(void) fflush(stdout);
1949		dup2(fd, STDOUT_FILENO);
1950		dup2(fd, STDERR_FILENO);
1951		if (e->e_xfp == NULL)
1952			close(fd);
1953	}
1954
1955	/* drop our controlling TTY completely if possible */
1956	if (droplev > 1)
1957	{
1958		(void) setsid();
1959		errno = 0;
1960	}
1961
1962#if XDEBUG
1963	checkfd012("disconnect");
1964#endif
1965
1966	if (LogLevel > 71)
1967		sm_syslog(LOG_DEBUG, e->e_id,
1968			"in background, pid=%d",
1969			getpid());
1970
1971	errno = 0;
1972}
1973
1974static void
1975obsolete(argv)
1976	char *argv[];
1977{
1978	register char *ap;
1979	register char *op;
1980
1981	while ((ap = *++argv) != NULL)
1982	{
1983		/* Return if "--" or not an option of any form. */
1984		if (ap[0] != '-' || ap[1] == '-')
1985			return;
1986
1987		/* skip over options that do have a value */
1988		op = strchr(OPTIONS, ap[1]);
1989		if (op != NULL && *++op == ':' && ap[2] == '\0' &&
1990		    ap[1] != 'd' &&
1991#if defined(sony_news)
1992		    ap[1] != 'E' && ap[1] != 'J' &&
1993#endif
1994		    argv[1] != NULL && argv[1][0] != '-')
1995		{
1996			argv++;
1997			continue;
1998		}
1999
2000		/* If -C doesn't have an argument, use sendmail.cf. */
2001#define	__DEFPATH	"sendmail.cf"
2002		if (ap[1] == 'C' && ap[2] == '\0')
2003		{
2004			*argv = xalloc(sizeof(__DEFPATH) + 2);
2005			argv[0][0] = '-';
2006			argv[0][1] = 'C';
2007			(void)strcpy(&argv[0][2], __DEFPATH);
2008		}
2009
2010		/* If -q doesn't have an argument, run it once. */
2011		if (ap[1] == 'q' && ap[2] == '\0')
2012			*argv = "-q0";
2013
2014		/* if -d doesn't have an argument, use 0-99.1 */
2015		if (ap[1] == 'd' && ap[2] == '\0')
2016			*argv = "-d0-99.1";
2017
2018# if defined(sony_news)
2019		/* if -E doesn't have an argument, use -EC */
2020		if (ap[1] == 'E' && ap[2] == '\0')
2021			*argv = "-EC";
2022
2023		/* if -J doesn't have an argument, use -JJ */
2024		if (ap[1] == 'J' && ap[2] == '\0')
2025			*argv = "-JJ";
2026# endif
2027	}
2028}
2029/*
2030**  AUTH_WARNING -- specify authorization warning
2031**
2032**	Parameters:
2033**		e -- the current envelope.
2034**		msg -- the text of the message.
2035**		args -- arguments to the message.
2036**
2037**	Returns:
2038**		none.
2039*/
2040
2041void
2042#ifdef __STDC__
2043auth_warning(register ENVELOPE *e, const char *msg, ...)
2044#else
2045auth_warning(e, msg, va_alist)
2046	register ENVELOPE *e;
2047	const char *msg;
2048	va_dcl
2049#endif
2050{
2051	char buf[MAXLINE];
2052	VA_LOCAL_DECL
2053
2054	if (bitset(PRIV_AUTHWARNINGS, PrivacyFlags))
2055	{
2056		register char *p;
2057		static char hostbuf[48];
2058		extern struct hostent *myhostname __P((char *, int));
2059
2060		if (hostbuf[0] == '\0')
2061			(void) myhostname(hostbuf, sizeof hostbuf);
2062
2063		(void) snprintf(buf, sizeof buf, "%s: ", hostbuf);
2064		p = &buf[strlen(buf)];
2065		VA_START(msg);
2066		vsnprintf(p, SPACELEFT(buf, p), msg, ap);
2067		VA_END;
2068		addheader("X-Authentication-Warning", buf, &e->e_header);
2069		if (LogLevel > 3)
2070			sm_syslog(LOG_INFO, e->e_id,
2071				"Authentication-Warning: %.400s",
2072				buf);
2073	}
2074}
2075/*
2076**  GETEXTENV -- get from external environment
2077**
2078**	Parameters:
2079**		envar -- the name of the variable to retrieve
2080**
2081**	Returns:
2082**		The value, if any.
2083*/
2084
2085char *
2086getextenv(envar)
2087	const char *envar;
2088{
2089	char **envp;
2090	int l;
2091
2092	l = strlen(envar);
2093	for (envp = ExternalEnviron; *envp != NULL; envp++)
2094	{
2095		if (strncmp(*envp, envar, l) == 0 && (*envp)[l] == '=')
2096			return &(*envp)[l + 1];
2097	}
2098	return NULL;
2099}
2100/*
2101**  SETUSERENV -- set an environment in the propogated environment
2102**
2103**	Parameters:
2104**		envar -- the name of the environment variable.
2105**		value -- the value to which it should be set.  If
2106**			null, this is extracted from the incoming
2107**			environment.  If that is not set, the call
2108**			to setuserenv is ignored.
2109**
2110**	Returns:
2111**		none.
2112*/
2113
2114void
2115setuserenv(envar, value)
2116	const char *envar;
2117	const char *value;
2118{
2119	int i;
2120	char **evp = UserEnviron;
2121	char *p;
2122
2123	if (value == NULL)
2124	{
2125		value = getextenv(envar);
2126		if (value == NULL)
2127			return;
2128	}
2129
2130	i = strlen(envar);
2131	p = (char *) xalloc(strlen(value) + i + 2);
2132	strcpy(p, envar);
2133	p[i++] = '=';
2134	strcpy(&p[i], value);
2135
2136	while (*evp != NULL && strncmp(*evp, p, i) != 0)
2137		evp++;
2138	if (*evp != NULL)
2139	{
2140		*evp++ = p;
2141	}
2142	else if (evp < &UserEnviron[MAXUSERENVIRON])
2143	{
2144		*evp++ = p;
2145		*evp = NULL;
2146	}
2147
2148	/* make sure it is in our environment as well */
2149	if (putenv(p) < 0)
2150		syserr("setuserenv: putenv(%s) failed", p);
2151}
2152/*
2153**  DUMPSTATE -- dump state
2154**
2155**	For debugging.
2156*/
2157
2158void
2159dumpstate(when)
2160	char *when;
2161{
2162	register char *j = macvalue('j', CurEnv);
2163	int rs;
2164
2165	sm_syslog(LOG_DEBUG, CurEnv->e_id,
2166		"--- dumping state on %s: $j = %s ---",
2167		when,
2168		j == NULL ? "<NULL>" : j);
2169	if (j != NULL)
2170	{
2171		if (!wordinclass(j, 'w'))
2172			sm_syslog(LOG_DEBUG, CurEnv->e_id,
2173				"*** $j not in $=w ***");
2174	}
2175	sm_syslog(LOG_DEBUG, CurEnv->e_id, "CurChildren = %d", CurChildren);
2176	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- open file descriptors: ---");
2177	printopenfds(TRUE);
2178	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- connection cache: ---");
2179	mci_dump_all(TRUE);
2180	rs = strtorwset("debug_dumpstate", NULL, ST_FIND);
2181	if (rs > 0)
2182	{
2183		int stat;
2184		register char **pvp;
2185		char *pv[MAXATOM + 1];
2186
2187		pv[0] = NULL;
2188		stat = rewrite(pv, rs, 0, CurEnv);
2189		sm_syslog(LOG_DEBUG, CurEnv->e_id,
2190		       "--- ruleset debug_dumpstate returns stat %d, pv: ---",
2191		       stat);
2192		for (pvp = pv; *pvp != NULL; pvp++)
2193			sm_syslog(LOG_DEBUG, CurEnv->e_id, "%s", *pvp);
2194	}
2195	sm_syslog(LOG_DEBUG, CurEnv->e_id, "--- end of state dump ---");
2196}
2197
2198
2199/* ARGSUSED */
2200SIGFUNC_DECL
2201sigusr1(sig)
2202	int sig;
2203{
2204	dumpstate("user signal");
2205	return SIGFUNC_RETURN;
2206}
2207
2208
2209/* ARGSUSED */
2210SIGFUNC_DECL
2211sighup(sig)
2212	int sig;
2213{
2214	if (SaveArgv[0][0] != '/')
2215	{
2216		if (LogLevel > 3)
2217			sm_syslog(LOG_INFO, NOQID, "could not restart: need full path");
2218		finis(FALSE, EX_OSFILE);
2219	}
2220	if (LogLevel > 3)
2221		sm_syslog(LOG_INFO, NOQID, "restarting %s on signal", SaveArgv[0]);
2222	alarm(0);
2223	releasesignal(SIGHUP);
2224	closecontrolsocket(TRUE);
2225	if (drop_privileges(TRUE) != EX_OK)
2226	{
2227		if (LogLevel > 0)
2228			sm_syslog(LOG_ALERT, NOQID, "could not set[ug]id(%d, %d): %m",
2229				RunAsUid, RunAsGid);
2230		finis(FALSE, EX_OSERR);
2231	}
2232	execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
2233	if (LogLevel > 0)
2234		sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]);
2235	finis(FALSE, EX_OSFILE);
2236}
2237/*
2238**  DROP_PRIVILEGES -- reduce privileges to those of the RunAsUser option
2239**
2240**	Parameters:
2241**		to_real_uid -- if set, drop to the real uid instead
2242**			of the RunAsUser.
2243**
2244**	Returns:
2245**		EX_OSERR if the setuid failed.
2246**		EX_OK otherwise.
2247*/
2248
2249int
2250drop_privileges(to_real_uid)
2251	bool to_real_uid;
2252{
2253	int rval = EX_OK;
2254	GIDSET_T emptygidset[1];
2255
2256	if (tTd(47, 1))
2257		printf("drop_privileges(%d): Real[UG]id=%d:%d, RunAs[UG]id=%d:%d\n",
2258			(int)to_real_uid, (int)RealUid, (int)RealGid, (int)RunAsUid, (int)RunAsGid);
2259
2260	if (to_real_uid)
2261	{
2262		RunAsUserName = RealUserName;
2263		RunAsUid = RealUid;
2264		RunAsGid = RealGid;
2265	}
2266
2267	/* make sure no one can grab open descriptors for secret files */
2268	endpwent();
2269
2270	/* reset group permissions; these can be set later */
2271	emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
2272	if (setgroups(1, emptygidset) == -1 && geteuid() == 0)
2273		rval = EX_OSERR;
2274
2275	/* reset primary group and user id */
2276	if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
2277		rval = EX_OSERR;
2278	if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
2279		rval = EX_OSERR;
2280	if (tTd(47, 5))
2281	{
2282		printf("drop_privileges: e/ruid = %d/%d e/rgid = %d/%d\n",
2283			(int)geteuid(), (int)getuid(), (int)getegid(), (int)getgid());
2284		printf("drop_privileges: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
2285	}
2286	return rval;
2287}
2288/*
2289**  FILL_FD -- make sure a file descriptor has been properly allocated
2290**
2291**	Used to make sure that stdin/out/err are allocated on startup
2292**
2293**	Parameters:
2294**		fd -- the file descriptor to be filled.
2295**		where -- a string used for logging.  If NULL, this is
2296**			being called on startup, and logging should
2297**			not be done.
2298**
2299**	Returns:
2300**		none
2301*/
2302
2303void
2304fill_fd(fd, where)
2305	int fd;
2306	char *where;
2307{
2308	int i;
2309	struct stat stbuf;
2310
2311	if (fstat(fd, &stbuf) >= 0 || errno != EBADF)
2312		return;
2313
2314	if (where != NULL)
2315		syserr("fill_fd: %s: fd %d not open", where, fd);
2316	else
2317		MissingFds |= 1 << fd;
2318	i = open("/dev/null", fd == 0 ? O_RDONLY : O_WRONLY, 0666);
2319	if (i < 0)
2320	{
2321		syserr("!fill_fd: %s: cannot open /dev/null",
2322			where == NULL ? "startup" : where);
2323	}
2324	if (fd != i)
2325	{
2326		(void) dup2(i, fd);
2327		(void) close(i);
2328	}
2329}
2330/*
2331**  TESTMODELINE -- process a test mode input line
2332**
2333**	Parameters:
2334**		line -- the input line.
2335**		e -- the current environment.
2336**	Syntax:
2337**		#  a comment
2338**		.X process X as a configuration line
2339**		=X dump a configuration item (such as mailers)
2340**		$X dump a macro or class
2341**		/X try an activity
2342**		X  normal process through rule set X
2343*/
2344
2345void
2346testmodeline(line, e)
2347	char *line;
2348	ENVELOPE *e;
2349{
2350	register char *p;
2351	char *q;
2352	auto char *delimptr;
2353	int mid;
2354	int i, rs;
2355	STAB *map;
2356	char **s;
2357	struct rewrite *rw;
2358	ADDRESS a;
2359	static int tryflags = RF_COPYNONE;
2360	char exbuf[MAXLINE];
2361	extern bool invalidaddr __P((char *, char *));
2362	extern char *crackaddr __P((char *));
2363	extern void dump_class __P((STAB *, int));
2364	extern void translate_dollars __P((char *));
2365	extern void help __P((char *));
2366
2367	switch (line[0])
2368	{
2369	  case '#':
2370	  case 0:
2371		return;
2372
2373	  case '?':
2374		help("-bt");
2375		return;
2376
2377	  case '.':		/* config-style settings */
2378		switch (line[1])
2379		{
2380		  case 'D':
2381			mid = macid(&line[2], &delimptr);
2382			if (mid == '\0')
2383				return;
2384			translate_dollars(delimptr);
2385			define(mid, newstr(delimptr), e);
2386			break;
2387
2388		  case 'C':
2389			if (line[2] == '\0')	/* not to call syserr() */
2390				return;
2391
2392			mid = macid(&line[2], &delimptr);
2393			if (mid == '\0')
2394				return;
2395			translate_dollars(delimptr);
2396			expand(delimptr, exbuf, sizeof exbuf, e);
2397			p = exbuf;
2398			while (*p != '\0')
2399			{
2400				register char *wd;
2401				char delim;
2402
2403				while (*p != '\0' && isascii(*p) && isspace(*p))
2404					p++;
2405				wd = p;
2406				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2407					p++;
2408				delim = *p;
2409				*p = '\0';
2410				if (wd[0] != '\0')
2411					setclass(mid, wd);
2412				*p = delim;
2413			}
2414			break;
2415
2416		  case '\0':
2417			printf("Usage: .[DC]macro value(s)\n");
2418			break;
2419
2420		  default:
2421			printf("Unknown \".\" command %s\n", line);
2422			break;
2423		}
2424		return;
2425
2426	  case '=':		/* config-style settings */
2427		switch (line[1])
2428		{
2429		  case 'S':		/* dump rule set */
2430			rs = strtorwset(&line[2], NULL, ST_FIND);
2431			if (rs < 0)
2432			{
2433				printf("Undefined ruleset %s\n", &line[2]);
2434				return;
2435			}
2436			rw = RewriteRules[rs];
2437			if (rw == NULL)
2438				return;
2439			do
2440			{
2441				putchar('R');
2442				s = rw->r_lhs;
2443				while (*s != NULL)
2444				{
2445					xputs(*s++);
2446					putchar(' ');
2447				}
2448				putchar('\t');
2449				putchar('\t');
2450				s = rw->r_rhs;
2451				while (*s != NULL)
2452				{
2453					xputs(*s++);
2454					putchar(' ');
2455				}
2456				putchar('\n');
2457			} while ((rw = rw->r_next) != NULL);
2458			break;
2459
2460		  case 'M':
2461			for (i = 0; i < MAXMAILERS; i++)
2462			{
2463				if (Mailer[i] != NULL)
2464					printmailer(Mailer[i]);
2465			}
2466			break;
2467
2468		  case '\0':
2469			printf("Usage: =Sruleset or =M\n");
2470			break;
2471
2472		  default:
2473			printf("Unknown \"=\" command %s\n", line);
2474			break;
2475		}
2476		return;
2477
2478	  case '-':		/* set command-line-like opts */
2479		switch (line[1])
2480		{
2481		  case 'd':
2482			tTflag(&line[2]);
2483			break;
2484
2485		  case '\0':
2486			printf("Usage: -d{debug arguments}\n");
2487			break;
2488
2489		  default:
2490			printf("Unknown \"-\" command %s\n", line);
2491			break;
2492		}
2493		return;
2494
2495	  case '$':
2496		if (line[1] == '=')
2497		{
2498			mid = macid(&line[2], NULL);
2499			if (mid != '\0')
2500				stabapply(dump_class, mid);
2501			return;
2502		}
2503		mid = macid(&line[1], NULL);
2504		if (mid == '\0')
2505			return;
2506		p = macvalue(mid, e);
2507		if (p == NULL)
2508			printf("Undefined\n");
2509		else
2510		{
2511			xputs(p);
2512			printf("\n");
2513		}
2514		return;
2515
2516	  case '/':		/* miscellaneous commands */
2517		p = &line[strlen(line)];
2518		while (--p >= line && isascii(*p) && isspace(*p))
2519			*p = '\0';
2520		p = strpbrk(line, " \t");
2521		if (p != NULL)
2522		{
2523			while (isascii(*p) && isspace(*p))
2524				*p++ = '\0';
2525		}
2526		else
2527			p = "";
2528		if (line[1] == '\0')
2529		{
2530			printf("Usage: /[canon|map|mx|parse|try|tryflags]\n");
2531			return;
2532		}
2533		if (strcasecmp(&line[1], "mx") == 0)
2534		{
2535#if NAMED_BIND
2536			/* look up MX records */
2537			int nmx;
2538			auto int rcode;
2539			char *mxhosts[MAXMXHOSTS + 1];
2540
2541			if (*p == '\0')
2542			{
2543				printf("Usage: /mx address\n");
2544				return;
2545			}
2546			nmx = getmxrr(p, mxhosts, FALSE, &rcode);
2547			printf("getmxrr(%s) returns %d value(s):\n", p, nmx);
2548			for (i = 0; i < nmx; i++)
2549				printf("\t%s\n", mxhosts[i]);
2550#else
2551			printf("No MX code compiled in\n");
2552#endif
2553		}
2554		else if (strcasecmp(&line[1], "canon") == 0)
2555		{
2556			char host[MAXHOSTNAMELEN];
2557
2558			if (*p == '\0')
2559			{
2560				printf("Usage: /canon address\n");
2561				return;
2562			}
2563			else if (strlen(p) >= sizeof host)
2564			{
2565				printf("Name too long\n");
2566				return;
2567			}
2568			strcpy(host, p);
2569			(void) getcanonname(host, sizeof(host), HasWildcardMX);
2570			printf("getcanonname(%s) returns %s\n", p, host);
2571		}
2572		else if (strcasecmp(&line[1], "map") == 0)
2573		{
2574			auto int rcode = EX_OK;
2575			char *av[2];
2576
2577			if (*p == '\0')
2578			{
2579				printf("Usage: /map mapname key\n");
2580				return;
2581			}
2582			for (q = p; *q != '\0' && !(isascii(*q) && isspace(*q)); q++)
2583				continue;
2584			if (*q == '\0')
2585			{
2586				printf("No key specified\n");
2587				return;
2588			}
2589			*q++ = '\0';
2590			map = stab(p, ST_MAP, ST_FIND);
2591			if (map == NULL)
2592			{
2593				printf("Map named \"%s\" not found\n", p);
2594				return;
2595			}
2596			if (!bitset(MF_OPEN, map->s_map.map_mflags))
2597			{
2598				printf("Map named \"%s\" not open\n", p);
2599				return;
2600			}
2601			printf("map_lookup: %s (%s) ", p, q);
2602			av[0] = q;
2603			av[1] = NULL;
2604			p = (*map->s_map.map_class->map_lookup)
2605					(&map->s_map, q, av, &rcode);
2606			if (p == NULL)
2607				printf("no match (%d)\n", rcode);
2608			else
2609				printf("returns %s (%d)\n", p, rcode);
2610		}
2611		else if (strcasecmp(&line[1], "try") == 0)
2612		{
2613			MAILER *m;
2614			STAB *s;
2615			auto int rcode = EX_OK;
2616
2617			q = strpbrk(p, " \t");
2618			if (q != NULL)
2619			{
2620				while (isascii(*q) && isspace(*q))
2621					*q++ = '\0';
2622			}
2623			if (q == NULL || *q == '\0')
2624			{
2625				printf("Usage: /try mailer address\n");
2626				return;
2627			}
2628			s = stab(p, ST_MAILER, ST_FIND);
2629			if (s == NULL)
2630			{
2631				printf("Unknown mailer %s\n", p);
2632				return;
2633			}
2634			m = s->s_mailer;
2635			printf("Trying %s %s address %s for mailer %s\n",
2636				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
2637				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient",
2638				q, p);
2639			p = remotename(q, m, tryflags, &rcode, CurEnv);
2640			printf("Rcode = %d, addr = %s\n",
2641				rcode, p == NULL ? "<NULL>" : p);
2642			e->e_to = NULL;
2643		}
2644		else if (strcasecmp(&line[1], "tryflags") == 0)
2645		{
2646			if (*p == '\0')
2647			{
2648				printf("Usage: /tryflags [Hh|Ee][Ss|Rr]\n");
2649				return;
2650			}
2651			for (; *p != '\0'; p++)
2652			{
2653				switch (*p)
2654				{
2655				  case 'H':
2656				  case 'h':
2657					tryflags |= RF_HEADERADDR;
2658					break;
2659
2660				  case 'E':
2661				  case 'e':
2662					tryflags &= ~RF_HEADERADDR;
2663					break;
2664
2665				  case 'S':
2666				  case 's':
2667					tryflags |= RF_SENDERADDR;
2668					break;
2669
2670				  case 'R':
2671				  case 'r':
2672					tryflags &= ~RF_SENDERADDR;
2673					break;
2674				}
2675			}
2676		}
2677		else if (strcasecmp(&line[1], "parse") == 0)
2678		{
2679			if (*p == '\0')
2680			{
2681				printf("Usage: /parse address\n");
2682				return;
2683			}
2684			q = crackaddr(p);
2685			printf("Cracked address = ");
2686			xputs(q);
2687			printf("\nParsing %s %s address\n",
2688				bitset(RF_HEADERADDR, tryflags) ? "header" : "envelope",
2689				bitset(RF_SENDERADDR, tryflags) ? "sender" : "recipient");
2690			if (parseaddr(p, &a, tryflags, '\0', NULL, e) == NULL)
2691				printf("Cannot parse\n");
2692			else if (a.q_host != NULL && a.q_host[0] != '\0')
2693				printf("mailer %s, host %s, user %s\n",
2694					a.q_mailer->m_name, a.q_host, a.q_user);
2695			else
2696				printf("mailer %s, user %s\n",
2697					a.q_mailer->m_name, a.q_user);
2698			e->e_to = NULL;
2699		}
2700		else
2701		{
2702			printf("Unknown \"/\" command %s\n", line);
2703		}
2704		return;
2705	}
2706
2707	for (p = line; isascii(*p) && isspace(*p); p++)
2708		continue;
2709	q = p;
2710	while (*p != '\0' && !(isascii(*p) && isspace(*p)))
2711		p++;
2712	if (*p == '\0')
2713	{
2714		printf("No address!\n");
2715		return;
2716	}
2717	*p = '\0';
2718	if (invalidaddr(p + 1, NULL))
2719		return;
2720	do
2721	{
2722		register char **pvp;
2723		char pvpbuf[PSBUFSIZE];
2724
2725		pvp = prescan(++p, ',', pvpbuf, sizeof pvpbuf,
2726			      &delimptr, NULL);
2727		if (pvp == NULL)
2728			continue;
2729		p = q;
2730		while (*p != '\0')
2731		{
2732			int stat;
2733
2734			rs = strtorwset(p, NULL, ST_FIND);
2735			if (rs < 0)
2736			{
2737				printf("Undefined ruleset %s\n", p);
2738				break;
2739			}
2740			stat = rewrite(pvp, rs, 0, e);
2741			if (stat != EX_OK)
2742				printf("== Ruleset %s (%d) status %d\n",
2743					p, rs, stat);
2744			while (*p != '\0' && *p++ != ',')
2745				continue;
2746		}
2747	} while (*(p = delimptr) != '\0');
2748}
2749
2750
2751void
2752dump_class(s, id)
2753	register STAB *s;
2754	int id;
2755{
2756	if (s->s_type != ST_CLASS)
2757		return;
2758	if (bitnset(id & 0xff, s->s_class))
2759		printf("%s\n", s->s_name);
2760}
2761