srvrsmtp.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# include "sendmail.h"
14
15#ifndef lint
16#if SMTP
17static char sccsid[] = "@(#)srvrsmtp.c	8.187 (Berkeley) 10/23/1998 (with SMTP)";
18#else
19static char sccsid[] = "@(#)srvrsmtp.c	8.187 (Berkeley) 10/23/1998 (without SMTP)";
20#endif
21#endif /* not lint */
22
23# include <errno.h>
24
25# if SMTP
26
27/*
28**  SMTP -- run the SMTP protocol.
29**
30**	Parameters:
31**		nullserver -- if non-NULL, rejection message for
32**			all SMTP commands.
33**		e -- the envelope.
34**
35**	Returns:
36**		never.
37**
38**	Side Effects:
39**		Reads commands from the input channel and processes
40**			them.
41*/
42
43struct cmd
44{
45	char	*cmdname;	/* command name */
46	int	cmdcode;	/* internal code, see below */
47};
48
49/* values for cmdcode */
50# define CMDERROR	0	/* bad command */
51# define CMDMAIL	1	/* mail -- designate sender */
52# define CMDRCPT	2	/* rcpt -- designate recipient */
53# define CMDDATA	3	/* data -- send message text */
54# define CMDRSET	4	/* rset -- reset state */
55# define CMDVRFY	5	/* vrfy -- verify address */
56# define CMDEXPN	6	/* expn -- expand address */
57# define CMDNOOP	7	/* noop -- do nothing */
58# define CMDQUIT	8	/* quit -- close connection and die */
59# define CMDHELO	9	/* helo -- be polite */
60# define CMDHELP	10	/* help -- give usage info */
61# define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
62# define CMDETRN	12	/* etrn -- flush queue */
63/* non-standard commands */
64# define CMDONEX	16	/* onex -- sending one transaction only */
65# define CMDVERB	17	/* verb -- go into verbose mode */
66# define CMDXUSR	18	/* xusr -- initial (user) submission */
67/* use this to catch and log "door handle" attempts on your system */
68# define CMDLOGBOGUS	23	/* bogus command that should be logged */
69/* debugging-only commands, only enabled if SMTPDEBUG is defined */
70# define CMDDBGQSHOW	24	/* showq -- show send queue */
71# define CMDDBGDEBUG	25	/* debug -- set debug mode */
72
73static struct cmd	CmdTab[] =
74{
75	{ "mail",	CMDMAIL		},
76	{ "rcpt",	CMDRCPT		},
77	{ "data",	CMDDATA		},
78	{ "rset",	CMDRSET		},
79	{ "vrfy",	CMDVRFY		},
80	{ "expn",	CMDEXPN		},
81	{ "help",	CMDHELP		},
82	{ "noop",	CMDNOOP		},
83	{ "quit",	CMDQUIT		},
84	{ "helo",	CMDHELO		},
85	{ "ehlo",	CMDEHLO		},
86	{ "etrn",	CMDETRN		},
87	{ "verb",	CMDVERB		},
88	{ "onex",	CMDONEX		},
89	{ "xusr",	CMDXUSR		},
90    /* remaining commands are here only to trap and log attempts to use them */
91	{ "showq",	CMDDBGQSHOW	},
92	{ "debug",	CMDDBGDEBUG	},
93	{ "wiz",	CMDLOGBOGUS	},
94
95	{ NULL,		CMDERROR	}
96};
97
98bool	OneXact = FALSE;		/* one xaction only this run */
99char	*CurSmtpClient;			/* who's at the other end of channel */
100
101static char	*skipword __P((char *volatile, char *));
102
103
104#define MAXBADCOMMANDS	25	/* maximum number of bad commands */
105#define MAXNOOPCOMMANDS	20	/* max "noise" commands before slowdown */
106#define MAXHELOCOMMANDS	3	/* max HELO/EHLO commands before slowdown */
107#define MAXVRFYCOMMANDS	6	/* max VRFY/EXPN commands before slowdown */
108#define MAXETRNCOMMANDS	8	/* max ETRN commands before slowdown */
109
110void
111smtp(nullserver, e)
112	char *nullserver;
113	register ENVELOPE *volatile e;
114{
115	register char *volatile p;
116	register struct cmd *c;
117	char *cmd;
118	auto ADDRESS *vrfyqueue;
119	ADDRESS *a;
120	volatile bool gotmail;		/* mail command received */
121	volatile bool gothello;		/* helo command received */
122	bool vrfy;			/* set if this is a vrfy command */
123	char *volatile protocol;	/* sending protocol */
124	char *volatile sendinghost;	/* sending hostname */
125	char *volatile peerhostname;	/* name of SMTP peer or "localhost" */
126	auto char *delimptr;
127	char *id;
128	volatile int nrcpts = 0;	/* number of RCPT commands */
129	bool doublequeue;
130	volatile bool discard;
131	volatile int badcommands = 0;	/* count of bad commands */
132	volatile int nverifies = 0;	/* count of VRFY/EXPN commands */
133	volatile int n_etrn = 0;	/* count of ETRN commands */
134	volatile int n_noop = 0;	/* count of NOOP/VERB/ONEX etc cmds */
135	volatile int n_helo = 0;	/* count of HELO/EHLO commands */
136	bool ok;
137	volatile int lognullconnection = TRUE;
138	register char *q;
139	QUEUE_CHAR *new;
140	char inp[MAXLINE];
141	char cmdbuf[MAXLINE];
142	extern ENVELOPE BlankEnvelope;
143	extern void help __P((char *));
144	extern void settime __P((ENVELOPE *));
145	extern bool enoughdiskspace __P((long));
146	extern int runinchild __P((char *, ENVELOPE *));
147	extern void checksmtpattack __P((volatile int *, int, char *, ENVELOPE *));
148
149	if (fileno(OutChannel) != fileno(stdout))
150	{
151		/* arrange for debugging output to go to remote host */
152		(void) dup2(fileno(OutChannel), fileno(stdout));
153	}
154	settime(e);
155	peerhostname = RealHostName;
156	if (peerhostname == NULL)
157		peerhostname = "localhost";
158	CurHostName = peerhostname;
159	CurSmtpClient = macvalue('_', e);
160	if (CurSmtpClient == NULL)
161		CurSmtpClient = CurHostName;
162
163	/* check_relay may have set discard bit, save for later */
164	discard = bitset(EF_DISCARD, e->e_flags);
165
166	sm_setproctitle(TRUE, "server %s startup", CurSmtpClient);
167#if DAEMON
168	if (LogLevel > 11)
169	{
170		/* log connection information */
171		sm_syslog(LOG_INFO, NOQID,
172			"SMTP connect from %.100s (%.100s)",
173			CurSmtpClient, anynet_ntoa(&RealHostAddr));
174	}
175#endif
176
177	/* output the first line, inserting "ESMTP" as second word */
178	expand(SmtpGreeting, inp, sizeof inp, e);
179	p = strchr(inp, '\n');
180	if (p != NULL)
181		*p++ = '\0';
182	id = strchr(inp, ' ');
183	if (id == NULL)
184		id = &inp[strlen(inp)];
185	cmd = p == NULL ? "220 %.*s ESMTP%s" : "220-%.*s ESMTP%s";
186	message(cmd, id - inp, inp, id);
187
188	/* output remaining lines */
189	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
190	{
191		*p++ = '\0';
192		if (isascii(*id) && isspace(*id))
193			id++;
194		message("220-%s", id);
195	}
196	if (id != NULL)
197	{
198		if (isascii(*id) && isspace(*id))
199			id++;
200		message("220 %s", id);
201	}
202
203	protocol = NULL;
204	sendinghost = macvalue('s', e);
205	gothello = FALSE;
206	gotmail = FALSE;
207	for (;;)
208	{
209		/* arrange for backout */
210		(void) setjmp(TopFrame);
211		QuickAbort = FALSE;
212		HoldErrs = FALSE;
213		SuprErrs = FALSE;
214		LogUsrErrs = FALSE;
215		OnlyOneError = TRUE;
216		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
217
218		/* setup for the read */
219		e->e_to = NULL;
220		Errors = 0;
221		(void) fflush(stdout);
222
223		/* read the input line */
224		SmtpPhase = "server cmd read";
225		sm_setproctitle(TRUE, "server %s cmd read", CurSmtpClient);
226		p = sfgets(inp, sizeof inp, InChannel, TimeOuts.to_nextcommand,
227				SmtpPhase);
228
229		/* handle errors */
230		if (p == NULL)
231		{
232			/* end of file, just die */
233			disconnect(1, e);
234			message("421 %s Lost input channel from %s",
235				MyHostName, CurSmtpClient);
236			if (LogLevel > (gotmail ? 1 : 19))
237				sm_syslog(LOG_NOTICE, e->e_id,
238					"lost input channel from %.100s",
239					CurSmtpClient);
240			if (lognullconnection && LogLevel > 5)
241				sm_syslog(LOG_INFO, NULL,
242				"Null connection from %.100s",
243				CurSmtpClient);
244
245			/*
246			**  If have not accepted mail (DATA), do not bounce
247			**  bad addresses back to sender.
248			*/
249			if (bitset(EF_CLRQUEUE, e->e_flags))
250				e->e_sendqueue = NULL;
251
252			if (InChild)
253				ExitStat = EX_QUIT;
254			finis(TRUE, ExitStat);
255		}
256
257		/* clean up end of line */
258		fixcrlf(inp, TRUE);
259
260		/* echo command to transcript */
261		if (e->e_xfp != NULL)
262			fprintf(e->e_xfp, "<<< %s\n", inp);
263
264		if (LogLevel >= 15)
265			sm_syslog(LOG_INFO, e->e_id,
266				"<-- %s",
267				inp);
268
269		if (e->e_id == NULL)
270			sm_setproctitle(TRUE, "%s: %.80s", CurSmtpClient, inp);
271		else
272			sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp);
273
274		/* break off command */
275		for (p = inp; isascii(*p) && isspace(*p); p++)
276			continue;
277		cmd = cmdbuf;
278		while (*p != '\0' &&
279		       !(isascii(*p) && isspace(*p)) &&
280		       cmd < &cmdbuf[sizeof cmdbuf - 2])
281			*cmd++ = *p++;
282		*cmd = '\0';
283
284		/* throw away leading whitespace */
285		while (isascii(*p) && isspace(*p))
286			p++;
287
288		/* decode command */
289		for (c = CmdTab; c->cmdname != NULL; c++)
290		{
291			if (!strcasecmp(c->cmdname, cmdbuf))
292				break;
293		}
294
295		/* reset errors */
296		errno = 0;
297
298		/*
299		**  Process command.
300		**
301		**	If we are running as a null server, return 550
302		**	to everything.
303		*/
304
305		if (nullserver != NULL)
306		{
307			switch (c->cmdcode)
308			{
309			  case CMDQUIT:
310			  case CMDHELO:
311			  case CMDEHLO:
312			  case CMDNOOP:
313				/* process normally */
314				break;
315
316			  default:
317				if (++badcommands > MAXBADCOMMANDS)
318					sleep(1);
319				usrerr("550 %s", nullserver);
320				continue;
321			}
322		}
323
324		/* non-null server */
325		switch (c->cmdcode)
326		{
327		  case CMDMAIL:
328		  case CMDEXPN:
329		  case CMDVRFY:
330		  case CMDETRN:
331			lognullconnection = FALSE;
332		}
333
334		switch (c->cmdcode)
335		{
336		  case CMDHELO:		/* hello -- introduce yourself */
337		  case CMDEHLO:		/* extended hello */
338			if (c->cmdcode == CMDEHLO)
339			{
340				protocol = "ESMTP";
341				SmtpPhase = "server EHLO";
342			}
343			else
344			{
345				protocol = "SMTP";
346				SmtpPhase = "server HELO";
347			}
348
349			/* avoid denial-of-service */
350			checksmtpattack(&n_helo, MAXHELOCOMMANDS, "HELO/EHLO", e);
351
352			/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
353			if (gothello)
354			{
355				usrerr("503 %s Duplicate HELO/EHLO",
356					MyHostName);
357				break;
358			}
359
360			/* check for valid domain name (re 1123 5.2.5) */
361			if (*p == '\0' && !AllowBogusHELO)
362			{
363				usrerr("501 %s requires domain address",
364					cmdbuf);
365				break;
366			}
367
368			/* check for long domain name (hides Received: info) */
369			if (strlen(p) > MAXNAME)
370			{
371				usrerr("501 Invalid domain name");
372				break;
373			}
374
375			for (q = p; *q != '\0'; q++)
376			{
377				if (!isascii(*q))
378					break;
379				if (isalnum(*q))
380					continue;
381				if (isspace(*q))
382				{
383					*q = '\0';
384					break;
385				}
386				if (strchr("[].-_#", *q) == NULL)
387					break;
388			}
389			if (*q == '\0')
390			{
391				q = "pleased to meet you";
392				sendinghost = newstr(p);
393			}
394			else if (!AllowBogusHELO)
395			{
396				usrerr("501 Invalid domain name");
397				break;
398			}
399			else
400			{
401				q = "accepting invalid domain name";
402			}
403
404			gothello = TRUE;
405
406			/* print HELO response message */
407			if (c->cmdcode != CMDEHLO || nullserver != NULL)
408			{
409				message("250 %s Hello %s, %s",
410					MyHostName, CurSmtpClient, q);
411				break;
412			}
413
414			message("250-%s Hello %s, %s",
415				MyHostName, CurSmtpClient, q);
416
417			/* print EHLO features list */
418			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
419			{
420				message("250-EXPN");
421				if (!bitset(PRIV_NOVERB, PrivacyFlags))
422					message("250-VERB");
423			}
424#if MIME8TO7
425			message("250-8BITMIME");
426#endif
427			if (MaxMessageSize > 0)
428				message("250-SIZE %ld", MaxMessageSize);
429			else
430				message("250-SIZE");
431#if DSN
432			if (SendMIMEErrors)
433				message("250-DSN");
434#endif
435			message("250-ONEX");
436			if (!bitset(PRIV_NOETRN, PrivacyFlags))
437				message("250-ETRN");
438			message("250-XUSR");
439			message("250 HELP");
440			break;
441
442		  case CMDMAIL:		/* mail -- designate sender */
443			SmtpPhase = "server MAIL";
444
445			/* check for validity of this command */
446			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
447			{
448				usrerr("503 Polite people say HELO first");
449				break;
450			}
451			if (gotmail)
452			{
453				usrerr("503 Sender already specified");
454				break;
455			}
456			if (InChild)
457			{
458				errno = 0;
459				syserr("503 Nested MAIL command: MAIL %s", p);
460				finis(TRUE, ExitStat);
461			}
462
463			/* make sure we know who the sending host is */
464			if (sendinghost == NULL)
465				sendinghost = peerhostname;
466
467			p = skipword(p, "from");
468			if (p == NULL)
469				break;
470
471			/* fork a subprocess to process this command */
472			if (runinchild("SMTP-MAIL", e) > 0)
473				break;
474			if (Errors > 0)
475				goto undo_subproc_no_pm;
476			if (!gothello)
477			{
478				auth_warning(e,
479					"%s didn't use HELO protocol",
480					CurSmtpClient);
481			}
482#ifdef PICKY_HELO_CHECK
483			if (strcasecmp(sendinghost, peerhostname) != 0 &&
484			    (strcasecmp(peerhostname, "localhost") != 0 ||
485			     strcasecmp(sendinghost, MyHostName) != 0))
486			{
487				auth_warning(e, "Host %s claimed to be %s",
488					CurSmtpClient, sendinghost);
489			}
490#endif
491
492			if (protocol == NULL)
493				protocol = "SMTP";
494			define('r', protocol, e);
495			define('s', sendinghost, e);
496			initsys(e);
497			if (Errors > 0)
498				goto undo_subproc_no_pm;
499			nrcpts = 0;
500			e->e_flags |= EF_LOGSENDER|EF_CLRQUEUE;
501			sm_setproctitle(TRUE, "%s %s: %.80s", e->e_id, CurSmtpClient, inp);
502
503			/* child -- go do the processing */
504			if (setjmp(TopFrame) > 0)
505			{
506				/* this failed -- undo work */
507 undo_subproc_no_pm:
508				e->e_flags &= ~EF_PM_NOTIFY;
509 undo_subproc:
510				if (InChild)
511				{
512					QuickAbort = FALSE;
513					SuprErrs = TRUE;
514					e->e_flags &= ~EF_FATALERRS;
515					finis(TRUE, ExitStat);
516				}
517				break;
518			}
519			QuickAbort = TRUE;
520
521			/* must parse sender first */
522			delimptr = NULL;
523			setsender(p, e, &delimptr, ' ', FALSE);
524			if (delimptr != NULL && *delimptr != '\0')
525				*delimptr++ = '\0';
526			if (Errors > 0)
527				goto undo_subproc_no_pm;
528
529			/* do config file checking of the sender */
530			if (rscheck("check_mail", p, NULL, e) != EX_OK ||
531			    Errors > 0)
532				goto undo_subproc_no_pm;
533
534			/* check for possible spoofing */
535			if (RealUid != 0 && OpMode == MD_SMTP &&
536			    !wordinclass(RealUserName, 't') &&
537			    !bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) &&
538			    strcmp(e->e_from.q_user, RealUserName) != 0)
539			{
540				auth_warning(e, "%s owned process doing -bs",
541					RealUserName);
542			}
543
544			/* now parse ESMTP arguments */
545			e->e_msgsize = 0;
546			p = delimptr;
547			while (p != NULL && *p != '\0')
548			{
549				char *kp;
550				char *vp = NULL;
551				extern void mail_esmtp_args __P((char *, char *, ENVELOPE *));
552
553				/* locate the beginning of the keyword */
554				while (isascii(*p) && isspace(*p))
555					p++;
556				if (*p == '\0')
557					break;
558				kp = p;
559
560				/* skip to the value portion */
561				while ((isascii(*p) && isalnum(*p)) || *p == '-')
562					p++;
563				if (*p == '=')
564				{
565					*p++ = '\0';
566					vp = p;
567
568					/* skip to the end of the value */
569					while (*p != '\0' && *p != ' ' &&
570					       !(isascii(*p) && iscntrl(*p)) &&
571					       *p != '=')
572						p++;
573				}
574
575				if (*p != '\0')
576					*p++ = '\0';
577
578				if (tTd(19, 1))
579					printf("MAIL: got arg %s=\"%s\"\n", kp,
580						vp == NULL ? "<null>" : vp);
581
582				mail_esmtp_args(kp, vp, e);
583				if (Errors > 0)
584					goto undo_subproc_no_pm;
585			}
586			if (Errors > 0)
587				goto undo_subproc_no_pm;
588
589			if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
590			{
591				usrerr("552 Message size exceeds fixed maximum message size (%ld)",
592					MaxMessageSize);
593				goto undo_subproc_no_pm;
594			}
595
596			if (!enoughdiskspace(e->e_msgsize))
597			{
598				usrerr("452 Insufficient disk space; try again later");
599				goto undo_subproc_no_pm;
600			}
601			if (Errors > 0)
602				goto undo_subproc_no_pm;
603			message("250 Sender ok");
604			gotmail = TRUE;
605			break;
606
607		  case CMDRCPT:		/* rcpt -- designate recipient */
608			if (!gotmail)
609			{
610				usrerr("503 Need MAIL before RCPT");
611				break;
612			}
613			SmtpPhase = "server RCPT";
614			if (setjmp(TopFrame) > 0)
615			{
616				e->e_flags &= ~EF_FATALERRS;
617				break;
618			}
619			QuickAbort = TRUE;
620			LogUsrErrs = TRUE;
621
622			/* limit flooding of our machine */
623			if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg)
624			{
625				usrerr("452 Too many recipients");
626				break;
627			}
628
629			if (e->e_sendmode != SM_DELIVER)
630				e->e_flags |= EF_VRFYONLY;
631
632			p = skipword(p, "to");
633			if (p == NULL)
634				break;
635			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
636			if (Errors > 0)
637				break;
638			if (a == NULL)
639			{
640				usrerr("501 Missing recipient");
641				break;
642			}
643
644			if (delimptr != NULL && *delimptr != '\0')
645				*delimptr++ = '\0';
646
647			/* do config file checking of the recipient */
648			if (rscheck("check_rcpt", p, NULL, e) != EX_OK ||
649			    Errors > 0)
650				break;
651
652			/* now parse ESMTP arguments */
653			p = delimptr;
654			while (p != NULL && *p != '\0')
655			{
656				char *kp;
657				char *vp = NULL;
658				extern void rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
659
660				/* locate the beginning of the keyword */
661				while (isascii(*p) && isspace(*p))
662					p++;
663				if (*p == '\0')
664					break;
665				kp = p;
666
667				/* skip to the value portion */
668				while ((isascii(*p) && isalnum(*p)) || *p == '-')
669					p++;
670				if (*p == '=')
671				{
672					*p++ = '\0';
673					vp = p;
674
675					/* skip to the end of the value */
676					while (*p != '\0' && *p != ' ' &&
677					       !(isascii(*p) && iscntrl(*p)) &&
678					       *p != '=')
679						p++;
680				}
681
682				if (*p != '\0')
683					*p++ = '\0';
684
685				if (tTd(19, 1))
686					printf("RCPT: got arg %s=\"%s\"\n", kp,
687						vp == NULL ? "<null>" : vp);
688
689				rcpt_esmtp_args(a, kp, vp, e);
690				if (Errors > 0)
691					break;
692			}
693			if (Errors > 0)
694				break;
695
696			/* save in recipient list after ESMTP mods */
697			a = recipient(a, &e->e_sendqueue, 0, e);
698			if (Errors > 0)
699				break;
700
701			/* no errors during parsing, but might be a duplicate */
702			e->e_to = a->q_paddr;
703			if (!bitset(QBADADDR, a->q_flags))
704			{
705				message("250 Recipient ok%s",
706					bitset(QQUEUEUP, a->q_flags) ?
707						" (will queue)" : "");
708				nrcpts++;
709			}
710			else
711			{
712				/* punt -- should keep message in ADDRESS.... */
713				usrerr("550 Addressee unknown");
714			}
715			break;
716
717		  case CMDDATA:		/* data -- text of mail */
718			SmtpPhase = "server DATA";
719			if (!gotmail)
720			{
721				usrerr("503 Need MAIL command");
722				break;
723			}
724			else if (nrcpts <= 0)
725			{
726				usrerr("503 Need RCPT (recipient)");
727				break;
728			}
729
730			/* put back discard bit */
731			if (discard)
732				e->e_flags |= EF_DISCARD;
733
734			/* check to see if we need to re-expand aliases */
735			/* also reset QBADADDR on already-diagnosted addrs */
736			doublequeue = FALSE;
737			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
738			{
739				if (bitset(QVERIFIED, a->q_flags) &&
740				    !bitset(EF_DISCARD, e->e_flags))
741				{
742					/* need to re-expand aliases */
743					doublequeue = TRUE;
744				}
745				if (bitset(QBADADDR, a->q_flags))
746				{
747					/* make this "go away" */
748					a->q_flags |= QDONTSEND;
749					a->q_flags &= ~QBADADDR;
750				}
751			}
752
753			/* collect the text of the message */
754			SmtpPhase = "collect";
755			buffer_errors();
756			collect(InChannel, TRUE, NULL, e);
757			if (Errors > 0)
758			{
759				flush_errors(TRUE);
760				buffer_errors();
761				goto abortmessage;
762			}
763
764			/* make sure we actually do delivery */
765			e->e_flags &= ~EF_CLRQUEUE;
766
767			/* from now on, we have to operate silently */
768			buffer_errors();
769			e->e_errormode = EM_MAIL;
770
771			/*
772			**  Arrange to send to everyone.
773			**	If sending to multiple people, mail back
774			**		errors rather than reporting directly.
775			**	In any case, don't mail back errors for
776			**		anything that has happened up to
777			**		now (the other end will do this).
778			**	Truncate our transcript -- the mail has gotten
779			**		to us successfully, and if we have
780			**		to mail this back, it will be easier
781			**		on the reader.
782			**	Then send to everyone.
783			**	Finally give a reply code.  If an error has
784			**		already been given, don't mail a
785			**		message back.
786			**	We goose error returns by clearing error bit.
787			*/
788
789			SmtpPhase = "delivery";
790			e->e_xfp = freopen(queuename(e, 'x'), "w", e->e_xfp);
791			id = e->e_id;
792
793			if (doublequeue)
794			{
795				/* make sure it is in the queue */
796				queueup(e, FALSE);
797			}
798			else
799			{
800				/* send to all recipients */
801				sendall(e, SM_DEFAULT);
802			}
803			e->e_to = NULL;
804
805			/* issue success message */
806			message("250 %s Message accepted for delivery", id);
807
808			/* if we just queued, poke it */
809			if (doublequeue &&
810			    e->e_sendmode != SM_QUEUE &&
811			    e->e_sendmode != SM_DEFER)
812			{
813				CurrentLA = getla();
814
815				if (!shouldqueue(e->e_msgpriority, e->e_ctime))
816				{
817					unlockqueue(e);
818					(void) dowork(id, TRUE, TRUE, e);
819				}
820			}
821
822  abortmessage:
823			/* if in a child, pop back to our parent */
824			if (InChild)
825				finis(TRUE, ExitStat);
826
827			/* clean up a bit */
828			gotmail = FALSE;
829			dropenvelope(e, TRUE);
830			CurEnv = e = newenvelope(e, CurEnv);
831			e->e_flags = BlankEnvelope.e_flags;
832			break;
833
834		  case CMDRSET:		/* rset -- reset state */
835			if (tTd(94, 100))
836				message("451 Test failure");
837			else
838				message("250 Reset state");
839
840			/* arrange to ignore any current send list */
841			e->e_sendqueue = NULL;
842			e->e_flags |= EF_CLRQUEUE;
843			if (InChild)
844				finis(TRUE, ExitStat);
845
846			/* clean up a bit */
847			gotmail = FALSE;
848			SuprErrs = TRUE;
849			dropenvelope(e, TRUE);
850			CurEnv = e = newenvelope(e, CurEnv);
851			break;
852
853		  case CMDVRFY:		/* vrfy -- verify address */
854		  case CMDEXPN:		/* expn -- expand address */
855			checksmtpattack(&nverifies, MAXVRFYCOMMANDS,
856				c->cmdcode == CMDVRFY ? "VRFY" : "EXPN", e);
857			vrfy = c->cmdcode == CMDVRFY;
858			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
859						PrivacyFlags))
860			{
861				if (vrfy)
862					message("252 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
863				else
864					message("502 Sorry, we do not allow this operation");
865				if (LogLevel > 5)
866					sm_syslog(LOG_INFO, e->e_id,
867						"%.100s: %s [rejected]",
868						CurSmtpClient,
869						shortenstring(inp, MAXSHORTSTR));
870				break;
871			}
872			else if (!gothello &&
873				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
874						PrivacyFlags))
875			{
876				usrerr("503 I demand that you introduce yourself first");
877				break;
878			}
879			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
880				break;
881			if (Errors > 0)
882				goto undo_subproc;
883			if (LogLevel > 5)
884				sm_syslog(LOG_INFO, e->e_id,
885					"%.100s: %s",
886					CurSmtpClient,
887					shortenstring(inp, MAXSHORTSTR));
888			if (setjmp(TopFrame) > 0)
889				goto undo_subproc;
890			QuickAbort = TRUE;
891			vrfyqueue = NULL;
892			if (vrfy)
893				e->e_flags |= EF_VRFYONLY;
894			while (*p != '\0' && isascii(*p) && isspace(*p))
895				p++;
896			if (*p == '\0')
897			{
898				usrerr("501 Argument required");
899			}
900			else
901			{
902				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
903			}
904			if (Errors > 0)
905				goto undo_subproc;
906			if (vrfyqueue == NULL)
907			{
908				usrerr("554 Nothing to %s", vrfy ? "VRFY" : "EXPN");
909			}
910			while (vrfyqueue != NULL)
911			{
912				extern void printvrfyaddr __P((ADDRESS *, bool, bool));
913
914				a = vrfyqueue;
915				while ((a = a->q_next) != NULL &&
916				       bitset(QDONTSEND|QBADADDR, a->q_flags))
917					continue;
918				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
919					printvrfyaddr(vrfyqueue, a == NULL, vrfy);
920				vrfyqueue = vrfyqueue->q_next;
921			}
922			if (InChild)
923				finis(TRUE, ExitStat);
924			break;
925
926		  case CMDETRN:		/* etrn -- force queue flush */
927			if (bitset(PRIV_NOETRN, PrivacyFlags))
928			{
929				message("502 Sorry, we do not allow this operation");
930				if (LogLevel > 5)
931					sm_syslog(LOG_INFO, e->e_id,
932						"%.100s: %s [rejected]",
933						CurSmtpClient,
934						shortenstring(inp, MAXSHORTSTR));
935				break;
936			}
937
938			if (strlen(p) <= 0)
939			{
940				usrerr("500 Parameter required");
941				break;
942			}
943
944			/* crude way to avoid denial-of-service attacks */
945			checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e);
946
947			if (LogLevel > 5)
948				sm_syslog(LOG_INFO, e->e_id,
949					"%.100s: ETRN %s",
950					CurSmtpClient,
951					shortenstring(p, MAXSHORTSTR));
952
953			id = p;
954			if (*id == '@')
955				id++;
956			else
957				*--id = '@';
958
959			if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
960			{
961				syserr("500 ETRN out of memory");
962				break;
963			}
964			new->queue_match = id;
965			new->queue_next = NULL;
966			QueueLimitRecipient = new;
967			ok = runqueue(TRUE, TRUE);
968			free(QueueLimitRecipient);
969			QueueLimitRecipient = NULL;
970			if (ok && Errors == 0)
971				message("250 Queuing for node %s started", p);
972			break;
973
974		  case CMDHELP:		/* help -- give user info */
975			help(p);
976			break;
977
978		  case CMDNOOP:		/* noop -- do nothing */
979			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "NOOP", e);
980			message("250 OK");
981			break;
982
983		  case CMDQUIT:		/* quit -- leave mail */
984			message("221 %s closing connection", MyHostName);
985
986doquit:
987			/* arrange to ignore any current send list */
988			e->e_sendqueue = NULL;
989
990			/* avoid future 050 messages */
991			disconnect(1, e);
992
993			if (InChild)
994				ExitStat = EX_QUIT;
995			if (lognullconnection && LogLevel > 5)
996				sm_syslog(LOG_INFO, NULL,
997					"Null connection from %.100s",
998					CurSmtpClient);
999			finis(TRUE, ExitStat);
1000
1001		  case CMDVERB:		/* set verbose mode */
1002			if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
1003			    bitset(PRIV_NOVERB, PrivacyFlags))
1004			{
1005				/* this would give out the same info */
1006				message("502 Verbose unavailable");
1007				break;
1008			}
1009			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "VERB", e);
1010			Verbose = 1;
1011			e->e_sendmode = SM_DELIVER;
1012			message("250 Verbose mode");
1013			break;
1014
1015		  case CMDONEX:		/* doing one transaction only */
1016			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "ONEX", e);
1017			OneXact = TRUE;
1018			message("250 Only one transaction");
1019			break;
1020
1021		  case CMDXUSR:		/* initial (user) submission */
1022			checksmtpattack(&n_noop, MAXNOOPCOMMANDS, "XUSR", e);
1023			UserSubmission = TRUE;
1024			message("250 Initial submission");
1025			break;
1026
1027# if SMTPDEBUG
1028		  case CMDDBGQSHOW:	/* show queues */
1029			printf("Send Queue=");
1030			printaddr(e->e_sendqueue, TRUE);
1031			break;
1032
1033		  case CMDDBGDEBUG:	/* set debug mode */
1034			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
1035			tTflag(p);
1036			message("200 Debug set");
1037			break;
1038
1039# else /* not SMTPDEBUG */
1040		  case CMDDBGQSHOW:	/* show queues */
1041		  case CMDDBGDEBUG:	/* set debug mode */
1042# endif /* SMTPDEBUG */
1043		  case CMDLOGBOGUS:	/* bogus command */
1044			if (LogLevel > 0)
1045				sm_syslog(LOG_CRIT, e->e_id,
1046				    "\"%s\" command from %.100s (%.100s)",
1047				    c->cmdname, CurSmtpClient,
1048				    anynet_ntoa(&RealHostAddr));
1049			/* FALL THROUGH */
1050
1051		  case CMDERROR:	/* unknown command */
1052			if (++badcommands > MAXBADCOMMANDS)
1053			{
1054				message("421 %s Too many bad commands; closing connection",
1055					MyHostName);
1056				goto doquit;
1057			}
1058
1059			usrerr("500 Command unrecognized: \"%s\"",
1060				shortenstring(inp, MAXSHORTSTR));
1061			break;
1062
1063		  default:
1064			errno = 0;
1065			syserr("500 smtp: unknown code %d", c->cmdcode);
1066			break;
1067		}
1068	}
1069}
1070/*
1071**  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
1072**
1073**	Parameters:
1074**		pcounter -- pointer to a counter for this command.
1075**		maxcount -- maximum value for this counter before we
1076**			slow down.
1077**		cname -- command name for logging.
1078**		e -- the current envelope.
1079**
1080**	Returns:
1081**		none.
1082**
1083**	Side Effects:
1084**		Slows down if we seem to be under attack.
1085*/
1086
1087void
1088checksmtpattack(pcounter, maxcount, cname, e)
1089	volatile int *pcounter;
1090	int maxcount;
1091	char *cname;
1092	ENVELOPE *e;
1093{
1094	if (++(*pcounter) >= maxcount)
1095	{
1096		if (*pcounter == maxcount && LogLevel > 5)
1097		{
1098			sm_syslog(LOG_INFO, e->e_id,
1099				"%.100s: %.40s attack?",
1100			       CurSmtpClient, cname);
1101		}
1102		sleep(*pcounter / maxcount);
1103	}
1104}
1105/*
1106**  SKIPWORD -- skip a fixed word.
1107**
1108**	Parameters:
1109**		p -- place to start looking.
1110**		w -- word to skip.
1111**
1112**	Returns:
1113**		p following w.
1114**		NULL on error.
1115**
1116**	Side Effects:
1117**		clobbers the p data area.
1118*/
1119
1120static char *
1121skipword(p, w)
1122	register char *volatile p;
1123	char *w;
1124{
1125	register char *q;
1126	char *firstp = p;
1127
1128	/* find beginning of word */
1129	while (isascii(*p) && isspace(*p))
1130		p++;
1131	q = p;
1132
1133	/* find end of word */
1134	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
1135		p++;
1136	while (isascii(*p) && isspace(*p))
1137		*p++ = '\0';
1138	if (*p != ':')
1139	{
1140	  syntax:
1141		usrerr("501 Syntax error in parameters scanning \"%s\"",
1142			shortenstring(firstp, MAXSHORTSTR));
1143		return (NULL);
1144	}
1145	*p++ = '\0';
1146	while (isascii(*p) && isspace(*p))
1147		p++;
1148
1149	if (*p == '\0')
1150		goto syntax;
1151
1152	/* see if the input word matches desired word */
1153	if (strcasecmp(q, w))
1154		goto syntax;
1155
1156	return (p);
1157}
1158/*
1159**  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
1160**
1161**	Parameters:
1162**		kp -- the parameter key.
1163**		vp -- the value of that parameter.
1164**		e -- the envelope.
1165**
1166**	Returns:
1167**		none.
1168*/
1169
1170void
1171mail_esmtp_args(kp, vp, e)
1172	char *kp;
1173	char *vp;
1174	ENVELOPE *e;
1175{
1176	if (strcasecmp(kp, "size") == 0)
1177	{
1178		if (vp == NULL)
1179		{
1180			usrerr("501 SIZE requires a value");
1181			/* NOTREACHED */
1182		}
1183# if defined(__STDC__) && !defined(BROKEN_ANSI_LIBRARY)
1184		e->e_msgsize = strtoul(vp, (char **) NULL, 10);
1185# else
1186		e->e_msgsize = strtol(vp, (char **) NULL, 10);
1187# endif
1188	}
1189	else if (strcasecmp(kp, "body") == 0)
1190	{
1191		if (vp == NULL)
1192		{
1193			usrerr("501 BODY requires a value");
1194			/* NOTREACHED */
1195		}
1196		else if (strcasecmp(vp, "8bitmime") == 0)
1197		{
1198			SevenBitInput = FALSE;
1199		}
1200		else if (strcasecmp(vp, "7bit") == 0)
1201		{
1202			SevenBitInput = TRUE;
1203		}
1204		else
1205		{
1206			usrerr("501 Unknown BODY type %s",
1207				vp);
1208			/* NOTREACHED */
1209		}
1210		e->e_bodytype = newstr(vp);
1211	}
1212	else if (strcasecmp(kp, "envid") == 0)
1213	{
1214		if (vp == NULL)
1215		{
1216			usrerr("501 ENVID requires a value");
1217			/* NOTREACHED */
1218		}
1219		if (!xtextok(vp))
1220		{
1221			usrerr("501 Syntax error in ENVID parameter value");
1222			/* NOTREACHED */
1223		}
1224		if (e->e_envid != NULL)
1225		{
1226			usrerr("501 Duplicate ENVID parameter");
1227			/* NOTREACHED */
1228		}
1229		e->e_envid = newstr(vp);
1230	}
1231	else if (strcasecmp(kp, "ret") == 0)
1232	{
1233		if (vp == NULL)
1234		{
1235			usrerr("501 RET requires a value");
1236			/* NOTREACHED */
1237		}
1238		if (bitset(EF_RET_PARAM, e->e_flags))
1239		{
1240			usrerr("501 Duplicate RET parameter");
1241			/* NOTREACHED */
1242		}
1243		e->e_flags |= EF_RET_PARAM;
1244		if (strcasecmp(vp, "hdrs") == 0)
1245			e->e_flags |= EF_NO_BODY_RETN;
1246		else if (strcasecmp(vp, "full") != 0)
1247		{
1248			usrerr("501 Bad argument \"%s\" to RET", vp);
1249			/* NOTREACHED */
1250		}
1251	}
1252	else
1253	{
1254		usrerr("501 %s parameter unrecognized", kp);
1255		/* NOTREACHED */
1256	}
1257}
1258/*
1259**  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
1260**
1261**	Parameters:
1262**		a -- the address corresponding to the To: parameter.
1263**		kp -- the parameter key.
1264**		vp -- the value of that parameter.
1265**		e -- the envelope.
1266**
1267**	Returns:
1268**		none.
1269*/
1270
1271void
1272rcpt_esmtp_args(a, kp, vp, e)
1273	ADDRESS *a;
1274	char *kp;
1275	char *vp;
1276	ENVELOPE *e;
1277{
1278	if (strcasecmp(kp, "notify") == 0)
1279	{
1280		char *p;
1281
1282		if (vp == NULL)
1283		{
1284			usrerr("501 NOTIFY requires a value");
1285			/* NOTREACHED */
1286		}
1287		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
1288		a->q_flags |= QHASNOTIFY;
1289		if (strcasecmp(vp, "never") == 0)
1290			return;
1291		for (p = vp; p != NULL; vp = p)
1292		{
1293			p = strchr(p, ',');
1294			if (p != NULL)
1295				*p++ = '\0';
1296			if (strcasecmp(vp, "success") == 0)
1297				a->q_flags |= QPINGONSUCCESS;
1298			else if (strcasecmp(vp, "failure") == 0)
1299				a->q_flags |= QPINGONFAILURE;
1300			else if (strcasecmp(vp, "delay") == 0)
1301				a->q_flags |= QPINGONDELAY;
1302			else
1303			{
1304				usrerr("501 Bad argument \"%s\"  to NOTIFY",
1305					vp);
1306				/* NOTREACHED */
1307			}
1308		}
1309	}
1310	else if (strcasecmp(kp, "orcpt") == 0)
1311	{
1312		if (vp == NULL)
1313		{
1314			usrerr("501 ORCPT requires a value");
1315			/* NOTREACHED */
1316		}
1317		if (strchr(vp, ';') == NULL || !xtextok(vp))
1318		{
1319			usrerr("501 Syntax error in ORCPT parameter value");
1320			/* NOTREACHED */
1321		}
1322		if (a->q_orcpt != NULL)
1323		{
1324			usrerr("501 Duplicate ORCPT parameter");
1325			/* NOTREACHED */
1326		}
1327		a->q_orcpt = newstr(vp);
1328	}
1329	else
1330	{
1331		usrerr("501 %s parameter unrecognized", kp);
1332		/* NOTREACHED */
1333	}
1334}
1335/*
1336**  PRINTVRFYADDR -- print an entry in the verify queue
1337**
1338**	Parameters:
1339**		a -- the address to print
1340**		last -- set if this is the last one.
1341**		vrfy -- set if this is a VRFY command.
1342**
1343**	Returns:
1344**		none.
1345**
1346**	Side Effects:
1347**		Prints the appropriate 250 codes.
1348*/
1349
1350void
1351printvrfyaddr(a, last, vrfy)
1352	register ADDRESS *a;
1353	bool last;
1354	bool vrfy;
1355{
1356	char fmtbuf[20];
1357
1358	if (vrfy && a->q_mailer != NULL &&
1359	    !bitnset(M_VRFY250, a->q_mailer->m_flags))
1360		strcpy(fmtbuf, "252");
1361	else
1362		strcpy(fmtbuf, "250");
1363	fmtbuf[3] = last ? ' ' : '-';
1364
1365	if (a->q_fullname == NULL)
1366	{
1367		if (strchr(a->q_user, '@') == NULL)
1368			strcpy(&fmtbuf[4], "<%s@%s>");
1369		else
1370			strcpy(&fmtbuf[4], "<%s>");
1371		message(fmtbuf, a->q_user, MyHostName);
1372	}
1373	else
1374	{
1375		if (strchr(a->q_user, '@') == NULL)
1376			strcpy(&fmtbuf[4], "%s <%s@%s>");
1377		else
1378			strcpy(&fmtbuf[4], "%s <%s>");
1379		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
1380	}
1381}
1382/*
1383**  RUNINCHILD -- return twice -- once in the child, then in the parent again
1384**
1385**	Parameters:
1386**		label -- a string used in error messages
1387**
1388**	Returns:
1389**		zero in the child
1390**		one in the parent
1391**
1392**	Side Effects:
1393**		none.
1394*/
1395
1396int
1397runinchild(label, e)
1398	char *label;
1399	register ENVELOPE *e;
1400{
1401	pid_t childpid;
1402
1403	if (!OneXact)
1404	{
1405		/*
1406		**  Disable child process reaping, in case ETRN has preceeded
1407		**  MAIL command, and then fork.
1408		*/
1409
1410		(void) blocksignal(SIGCHLD);
1411
1412		childpid = dofork();
1413		if (childpid < 0)
1414		{
1415			syserr("451 %s: cannot fork", label);
1416			(void) releasesignal(SIGCHLD);
1417			return (1);
1418		}
1419		if (childpid > 0)
1420		{
1421			auto int st;
1422
1423			/* parent -- wait for child to complete */
1424			sm_setproctitle(TRUE, "server %s child wait", CurSmtpClient);
1425			st = waitfor(childpid);
1426			if (st == -1)
1427				syserr("451 %s: lost child", label);
1428			else if (!WIFEXITED(st))
1429				syserr("451 %s: died on signal %d",
1430					label, st & 0177);
1431
1432			/* if we exited on a QUIT command, complete the process */
1433			if (WEXITSTATUS(st) == EX_QUIT)
1434			{
1435				disconnect(1, e);
1436				finis(TRUE, ExitStat);
1437			}
1438
1439			/* restore the child signal */
1440			(void) releasesignal(SIGCHLD);
1441
1442			return (1);
1443		}
1444		else
1445		{
1446			/* child */
1447			InChild = TRUE;
1448			QuickAbort = FALSE;
1449			clearenvelope(e, FALSE);
1450			(void) setsignal(SIGCHLD, SIG_DFL);
1451			(void) releasesignal(SIGCHLD);
1452		}
1453	}
1454	return (0);
1455}
1456
1457# endif /* SMTP */
1458/*
1459**  HELP -- implement the HELP command.
1460**
1461**	Parameters:
1462**		topic -- the topic we want help for.
1463**
1464**	Returns:
1465**		none.
1466**
1467**	Side Effects:
1468**		outputs the help file to message output.
1469*/
1470
1471void
1472help(topic)
1473	char *topic;
1474{
1475	register FILE *hf;
1476	int len;
1477	bool noinfo;
1478	int sff = SFF_OPENASROOT|SFF_REGONLY;
1479	char buf[MAXLINE];
1480	extern char Version[];
1481
1482	if (DontLockReadFiles)
1483		sff |= SFF_NOLOCK;
1484	if (!bitset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
1485		sff |= SFF_SAFEDIRPATH;
1486
1487	if (HelpFile == NULL ||
1488	    (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
1489	{
1490		/* no help */
1491		errno = 0;
1492		message("502 Sendmail %s -- HELP not implemented", Version);
1493		return;
1494	}
1495
1496	if (topic == NULL || *topic == '\0')
1497	{
1498		topic = "smtp";
1499		message("214-This is Sendmail version %s", Version);
1500		noinfo = FALSE;
1501	}
1502	else
1503	{
1504		makelower(topic);
1505		noinfo = TRUE;
1506	}
1507
1508	len = strlen(topic);
1509
1510	while (fgets(buf, sizeof buf, hf) != NULL)
1511	{
1512		if (strncmp(buf, topic, len) == 0)
1513		{
1514			register char *p;
1515
1516			p = strchr(buf, '\t');
1517			if (p == NULL)
1518				p = buf;
1519			else
1520				p++;
1521			fixcrlf(p, TRUE);
1522			message("214-%s", p);
1523			noinfo = FALSE;
1524		}
1525	}
1526
1527	if (noinfo)
1528		message("504 HELP topic \"%.10s\" unknown", topic);
1529	else
1530		message("214 End of HELP info");
1531	(void) fclose(hf);
1532}
1533