srvrsmtp.c revision 168515
138032Speter/*
2157001Sgshapiro * Copyright (c) 1998-2006 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
1590792Sgshapiro#if MILTER
16132943Sgshapiro# include <libmilter/mfapi.h>
1790792Sgshapiro# include <libmilter/mfdef.h>
1890792Sgshapiro#endif /* MILTER */
1964562Sgshapiro
20168515SgshapiroSM_RCSID("@(#)$Id: srvrsmtp.c,v 8.960 2007/02/07 20:18:47 ca Exp $")
2164562Sgshapiro
22157001Sgshapiro#include <sm/time.h>
23132943Sgshapiro#include <sm/fdset.h>
24132943Sgshapiro
2590792Sgshapiro#if SASL || STARTTLS
2690792Sgshapiro# include "sfsasl.h"
2790792Sgshapiro#endif /* SASL || STARTTLS */
2890792Sgshapiro#if SASL
2990792Sgshapiro# define ENC64LEN(l)	(((l) + 2) * 4 / 3 + 1)
3064562Sgshapirostatic int saslmechs __P((sasl_conn_t *, char **));
3190792Sgshapiro#endif /* SASL */
3290792Sgshapiro#if STARTTLS
3390792Sgshapiro# include <sysexits.h>
3438032Speter
3590792Sgshapirostatic SSL_CTX	*srv_ctx = NULL;	/* TLS server context */
3690792Sgshapirostatic SSL	*srv_ssl = NULL;	/* per connection context */
3738032Speter
3890792Sgshapirostatic bool	tls_ok_srv = false;
3990792Sgshapiro
4090792Sgshapiro# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
4190792Sgshapiro				bitset(SRV_VRFY_CLT, features))
4290792Sgshapiro#endif /* STARTTLS */
4390792Sgshapiro
44168515Sgshapiro#if _FFR_DM_ONE
45168515Sgshapirostatic bool	NotFirstDelivery = false;
46168515Sgshapiro#endif /* _FFR_DM_ONE */
47168515Sgshapiro
4890792Sgshapiro/* server features */
4990792Sgshapiro#define SRV_NONE	0x0000	/* none... */
5090792Sgshapiro#define SRV_OFFER_TLS	0x0001	/* offer STARTTLS */
5190792Sgshapiro#define SRV_VRFY_CLT	0x0002	/* request a cert */
5290792Sgshapiro#define SRV_OFFER_AUTH	0x0004	/* offer AUTH */
5390792Sgshapiro#define SRV_OFFER_ETRN	0x0008	/* offer ETRN */
5490792Sgshapiro#define SRV_OFFER_VRFY	0x0010	/* offer VRFY (not yet used) */
5590792Sgshapiro#define SRV_OFFER_EXPN	0x0020	/* offer EXPN */
5690792Sgshapiro#define SRV_OFFER_VERB	0x0040	/* offer VERB */
5790792Sgshapiro#define SRV_OFFER_DSN	0x0080	/* offer DSN */
5890792Sgshapiro#if PIPELINING
5990792Sgshapiro# define SRV_OFFER_PIPE	0x0100	/* offer PIPELINING */
6090792Sgshapiro# if _FFR_NO_PIPE
6190792Sgshapiro#  define SRV_NO_PIPE	0x0200	/* disable PIPELINING, sleep if used */
6290792Sgshapiro# endif /* _FFR_NO_PIPE */
6390792Sgshapiro#endif /* PIPELINING */
6490792Sgshapiro#define SRV_REQ_AUTH	0x0400	/* require AUTH */
65132943Sgshapiro#define SRV_REQ_SEC	0x0800	/* require security - equiv to AuthOptions=p */
6690792Sgshapiro#define SRV_TMP_FAIL	0x1000	/* ruleset caused a temporary failure */
6790792Sgshapiro
6890792Sgshapirostatic unsigned int	srvfeatures __P((ENVELOPE *, char *, unsigned int));
6990792Sgshapiro
70132943Sgshapiro#define	STOP_ATTACK	((time_t) -1)
71132943Sgshapirostatic time_t	checksmtpattack __P((volatile unsigned int *, unsigned int,
72132943Sgshapiro				     bool, char *, ENVELOPE *));
7364562Sgshapirostatic void	printvrfyaddr __P((ADDRESS *, bool, bool));
7464562Sgshapirostatic char	*skipword __P((char *volatile, char *));
7590792Sgshapirostatic void	setup_smtpd_io __P((void));
76120256Sgshapiro
77120256Sgshapiro#if SASL
78120256Sgshapiro# if SASL >= 20000
79120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
80120256Sgshapiro				char *_remoteip, char *_localip,
81120256Sgshapiro				char *_auth_id, sasl_ssf_t *_ext_ssf));
82120256Sgshapiro
83120256Sgshapiro# define RESET_SASLCONN	\
84147078Sgshapiro	do							\
85147078Sgshapiro	{							\
86147078Sgshapiro		result = reset_saslconn(&conn, AuthRealm, remoteip, \
87147078Sgshapiro					localip, auth_id, &ext_ssf); \
88147078Sgshapiro		if (result != SASL_OK)				\
89147078Sgshapiro			sasl_ok = false;			\
90147078Sgshapiro	} while (0)
91120256Sgshapiro
92120256Sgshapiro# else /* SASL >= 20000 */
93120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
94120256Sgshapiro				struct sockaddr_in *_saddr_r,
95120256Sgshapiro				struct sockaddr_in *_saddr_l,
96120256Sgshapiro				sasl_external_properties_t *_ext_ssf));
97120256Sgshapiro# define RESET_SASLCONN	\
98147078Sgshapiro	do							\
99147078Sgshapiro	{							\
100147078Sgshapiro		result = reset_saslconn(&conn, AuthRealm, &saddr_r, \
101147078Sgshapiro					&saddr_l, &ext_ssf);	\
102147078Sgshapiro		if (result != SASL_OK)				\
103147078Sgshapiro			sasl_ok = false;			\
104147078Sgshapiro	} while (0)
105120256Sgshapiro
106120256Sgshapiro# endif /* SASL >= 20000 */
107120256Sgshapiro#endif /* SASL */
108120256Sgshapiro
10964562Sgshapiroextern ENVELOPE	BlankEnvelope;
11038032Speter
111132943Sgshapiro#define NBADRCPTS						\
112132943Sgshapiro	do							\
113132943Sgshapiro	{							\
114132943Sgshapiro		char buf[16];					\
115168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%d",	\
116132943Sgshapiro			BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \
117132943Sgshapiro				? n_badrcpts - 1 : n_badrcpts);	\
118132943Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
119132943Sgshapiro	} while (0)
120132943Sgshapiro
12190792Sgshapiro#define SKIP_SPACE(s)	while (isascii(*s) && isspace(*s))	\
12290792Sgshapiro				(s)++
12390792Sgshapiro
12438032Speter/*
125168515Sgshapiro**  PARSE_ESMTP_ARGS -- parse EMSTP arguments (for MAIL, RCPT)
126168515Sgshapiro**
127168515Sgshapiro**	Parameters:
128168515Sgshapiro**		e -- the envelope
129168515Sgshapiro**		addr_st -- address (RCPT only)
130168515Sgshapiro**		p -- read buffer
131168515Sgshapiro**		delimptr -- current position in read buffer
132168515Sgshapiro**		which -- MAIL/RCPT
133168515Sgshapiro**		args -- arguments (output)
134168515Sgshapiro**		esmtp_args -- function to process a single ESMTP argument
135168515Sgshapiro**
136168515Sgshapiro**	Returns:
137168515Sgshapiro**		none
138168515Sgshapiro*/
139168515Sgshapiro
140168515Sgshapirovoid
141168515Sgshapiroparse_esmtp_args(e, addr_st, p, delimptr, which, args, esmtp_args)
142168515Sgshapiro	ENVELOPE *e;
143168515Sgshapiro	ADDRESS *addr_st;
144168515Sgshapiro	char *p;
145168515Sgshapiro	char *delimptr;
146168515Sgshapiro	char *which;
147168515Sgshapiro	char *args[];
148168515Sgshapiro	esmtp_args_F esmtp_args;
149168515Sgshapiro{
150168515Sgshapiro	int argno;
151168515Sgshapiro
152168515Sgshapiro	argno = 0;
153168515Sgshapiro	if (args != NULL)
154168515Sgshapiro		args[argno++] = p;
155168515Sgshapiro	p = delimptr;
156168515Sgshapiro	while (p != NULL && *p != '\0')
157168515Sgshapiro	{
158168515Sgshapiro		char *kp;
159168515Sgshapiro		char *vp = NULL;
160168515Sgshapiro		char *equal = NULL;
161168515Sgshapiro
162168515Sgshapiro		/* locate the beginning of the keyword */
163168515Sgshapiro		SKIP_SPACE(p);
164168515Sgshapiro		if (*p == '\0')
165168515Sgshapiro			break;
166168515Sgshapiro		kp = p;
167168515Sgshapiro
168168515Sgshapiro		/* skip to the value portion */
169168515Sgshapiro		while ((isascii(*p) && isalnum(*p)) || *p == '-')
170168515Sgshapiro			p++;
171168515Sgshapiro		if (*p == '=')
172168515Sgshapiro		{
173168515Sgshapiro			equal = p;
174168515Sgshapiro			*p++ = '\0';
175168515Sgshapiro			vp = p;
176168515Sgshapiro
177168515Sgshapiro			/* skip to the end of the value */
178168515Sgshapiro			while (*p != '\0' && *p != ' ' &&
179168515Sgshapiro			       !(isascii(*p) && iscntrl(*p)) &&
180168515Sgshapiro			       *p != '=')
181168515Sgshapiro				p++;
182168515Sgshapiro		}
183168515Sgshapiro
184168515Sgshapiro		if (*p != '\0')
185168515Sgshapiro			*p++ = '\0';
186168515Sgshapiro
187168515Sgshapiro		if (tTd(19, 1))
188168515Sgshapiro			sm_dprintf("%s: got arg %s=\"%s\"\n", which, kp,
189168515Sgshapiro				vp == NULL ? "<null>" : vp);
190168515Sgshapiro
191168515Sgshapiro		esmtp_args(addr_st, kp, vp, e);
192168515Sgshapiro		if (equal != NULL)
193168515Sgshapiro			*equal = '=';
194168515Sgshapiro		if (args != NULL)
195168515Sgshapiro			args[argno] = kp;
196168515Sgshapiro		argno++;
197168515Sgshapiro		if (argno >= MAXSMTPARGS - 1)
198168515Sgshapiro			usrerr("501 5.5.4 Too many parameters");
199168515Sgshapiro		if (Errors > 0)
200168515Sgshapiro			break;
201168515Sgshapiro	}
202168515Sgshapiro	if (args != NULL)
203168515Sgshapiro		args[argno] = NULL;
204168515Sgshapiro}
205168515Sgshapiro
206168515Sgshapiro/*
20738032Speter**  SMTP -- run the SMTP protocol.
20838032Speter**
20938032Speter**	Parameters:
21038032Speter**		nullserver -- if non-NULL, rejection message for
21190792Sgshapiro**			(almost) all SMTP commands.
21290792Sgshapiro**		d_flags -- daemon flags
21338032Speter**		e -- the envelope.
21438032Speter**
21538032Speter**	Returns:
21638032Speter**		never.
21738032Speter**
21838032Speter**	Side Effects:
21990792Sgshapiro**		Reads commands from the input channel and processes them.
22038032Speter*/
22138032Speter
22290792Sgshapiro/*
22390792Sgshapiro**  Notice: The smtp server doesn't have a session context like the client
22490792Sgshapiro**	side has (mci). Therefore some data (session oriented) is allocated
22590792Sgshapiro**	or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
22690792Sgshapiro**	This should be fixed in a successor version.
22790792Sgshapiro*/
22890792Sgshapiro
22938032Speterstruct cmd
23038032Speter{
23164562Sgshapiro	char	*cmd_name;	/* command name */
23264562Sgshapiro	int	cmd_code;	/* internal code, see below */
23338032Speter};
23438032Speter
23564562Sgshapiro/* values for cmd_code */
23690792Sgshapiro#define CMDERROR	0	/* bad command */
23790792Sgshapiro#define CMDMAIL	1	/* mail -- designate sender */
23890792Sgshapiro#define CMDRCPT	2	/* rcpt -- designate recipient */
23990792Sgshapiro#define CMDDATA	3	/* data -- send message text */
24090792Sgshapiro#define CMDRSET	4	/* rset -- reset state */
24190792Sgshapiro#define CMDVRFY	5	/* vrfy -- verify address */
24290792Sgshapiro#define CMDEXPN	6	/* expn -- expand address */
24390792Sgshapiro#define CMDNOOP	7	/* noop -- do nothing */
24490792Sgshapiro#define CMDQUIT	8	/* quit -- close connection and die */
24590792Sgshapiro#define CMDHELO	9	/* helo -- be polite */
24690792Sgshapiro#define CMDHELP	10	/* help -- give usage info */
24790792Sgshapiro#define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
24890792Sgshapiro#define CMDETRN	12	/* etrn -- flush queue */
24990792Sgshapiro#if SASL
25090792Sgshapiro# define CMDAUTH	13	/* auth -- SASL authenticate */
25190792Sgshapiro#endif /* SASL */
25290792Sgshapiro#if STARTTLS
25390792Sgshapiro# define CMDSTLS	14	/* STARTTLS -- start TLS session */
25490792Sgshapiro#endif /* STARTTLS */
25538032Speter/* non-standard commands */
25690792Sgshapiro#define CMDVERB	17	/* verb -- go into verbose mode */
25764562Sgshapiro/* unimplemented commands from RFC 821 */
25890792Sgshapiro#define CMDUNIMPL	19	/* unimplemented rfc821 commands */
25938032Speter/* use this to catch and log "door handle" attempts on your system */
26090792Sgshapiro#define CMDLOGBOGUS	23	/* bogus command that should be logged */
26138032Speter/* debugging-only commands, only enabled if SMTPDEBUG is defined */
26290792Sgshapiro#define CMDDBGQSHOW	24	/* showq -- show send queue */
26390792Sgshapiro#define CMDDBGDEBUG	25	/* debug -- set debug mode */
26438032Speter
26566494Sgshapiro/*
26690792Sgshapiro**  Note: If you change this list, remember to update 'helpfile'
26766494Sgshapiro*/
26866494Sgshapiro
26938032Speterstatic struct cmd	CmdTab[] =
27038032Speter{
27138032Speter	{ "mail",	CMDMAIL		},
27238032Speter	{ "rcpt",	CMDRCPT		},
27338032Speter	{ "data",	CMDDATA		},
27438032Speter	{ "rset",	CMDRSET		},
27538032Speter	{ "vrfy",	CMDVRFY		},
27638032Speter	{ "expn",	CMDEXPN		},
27738032Speter	{ "help",	CMDHELP		},
27838032Speter	{ "noop",	CMDNOOP		},
27938032Speter	{ "quit",	CMDQUIT		},
28038032Speter	{ "helo",	CMDHELO		},
28138032Speter	{ "ehlo",	CMDEHLO		},
28238032Speter	{ "etrn",	CMDETRN		},
28338032Speter	{ "verb",	CMDVERB		},
28464562Sgshapiro	{ "send",	CMDUNIMPL	},
28564562Sgshapiro	{ "saml",	CMDUNIMPL	},
28664562Sgshapiro	{ "soml",	CMDUNIMPL	},
28764562Sgshapiro	{ "turn",	CMDUNIMPL	},
28890792Sgshapiro#if SASL
28964562Sgshapiro	{ "auth",	CMDAUTH,	},
29090792Sgshapiro#endif /* SASL */
29190792Sgshapiro#if STARTTLS
29264562Sgshapiro	{ "starttls",	CMDSTLS,	},
29390792Sgshapiro#endif /* STARTTLS */
29438032Speter    /* remaining commands are here only to trap and log attempts to use them */
29538032Speter	{ "showq",	CMDDBGQSHOW	},
29638032Speter	{ "debug",	CMDDBGDEBUG	},
29738032Speter	{ "wiz",	CMDLOGBOGUS	},
29838032Speter
29938032Speter	{ NULL,		CMDERROR	}
30038032Speter};
30138032Speter
30264562Sgshapirostatic char	*CurSmtpClient;		/* who's at the other end of channel */
30338032Speter
30490792Sgshapiro#ifndef MAXBADCOMMANDS
30590792Sgshapiro# define MAXBADCOMMANDS 25	/* maximum number of bad commands */
30694334Sgshapiro#endif /* ! MAXBADCOMMANDS */
30790792Sgshapiro#ifndef MAXHELOCOMMANDS
30890792Sgshapiro# define MAXHELOCOMMANDS 3	/* max HELO/EHLO commands before slowdown */
30994334Sgshapiro#endif /* ! MAXHELOCOMMANDS */
31090792Sgshapiro#ifndef MAXVRFYCOMMANDS
31190792Sgshapiro# define MAXVRFYCOMMANDS 6	/* max VRFY/EXPN commands before slowdown */
31294334Sgshapiro#endif /* ! MAXVRFYCOMMANDS */
31390792Sgshapiro#ifndef MAXETRNCOMMANDS
31490792Sgshapiro# define MAXETRNCOMMANDS 8	/* max ETRN commands before slowdown */
31594334Sgshapiro#endif /* ! MAXETRNCOMMANDS */
31690792Sgshapiro#ifndef MAXTIMEOUT
31790792Sgshapiro# define MAXTIMEOUT (4 * 60)	/* max timeout for bad commands */
31894334Sgshapiro#endif /* ! MAXTIMEOUT */
31938032Speter
320132943Sgshapiro/*
321132943Sgshapiro**  Maximum shift value to compute timeout for bad commands.
322132943Sgshapiro**  This introduces an upper limit of 2^MAXSHIFT for the timeout.
323132943Sgshapiro*/
324132943Sgshapiro
325132943Sgshapiro#ifndef MAXSHIFT
326132943Sgshapiro# define MAXSHIFT 8
327132943Sgshapiro#endif /* ! MAXSHIFT */
328132943Sgshapiro#if MAXSHIFT > 31
329132943Sgshapiro ERROR _MAXSHIFT > 31 is invalid
330132943Sgshapiro#endif /* MAXSHIFT */
331132943Sgshapiro
332132943Sgshapiro
333132943Sgshapiro#if MAXBADCOMMANDS > 0
334132943Sgshapiro# define STOP_IF_ATTACK(r)	do		\
335132943Sgshapiro	{					\
336132943Sgshapiro		if ((r) == STOP_ATTACK)		\
337132943Sgshapiro			goto stopattack;	\
338132943Sgshapiro	} while (0)
339132943Sgshapiro
340132943Sgshapiro#else /* MAXBADCOMMANDS > 0 */
341132943Sgshapiro# define STOP_IF_ATTACK(r)	r
342132943Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
343132943Sgshapiro
344132943Sgshapiro
34590792Sgshapiro#if SM_HEAP_CHECK
34690792Sgshapirostatic SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
34790792Sgshapiro	"@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
34890792Sgshapiro#endif /* SM_HEAP_CHECK */
34938032Speter
35090792Sgshapirotypedef struct
35190792Sgshapiro{
35290792Sgshapiro	bool	sm_gotmail;	/* mail command received */
35390792Sgshapiro	unsigned int sm_nrcpts;	/* number of successful RCPT commands */
35490792Sgshapiro	bool	sm_discard;
35590792Sgshapiro#if MILTER
35690792Sgshapiro	bool	sm_milterize;
35790792Sgshapiro	bool	sm_milterlist;	/* any filters in the list? */
35890792Sgshapiro#endif /* MILTER */
35990792Sgshapiro	char	*sm_quarmsg;	/* carry quarantining across messages */
36090792Sgshapiro} SMTP_T;
36190792Sgshapiro
362132943Sgshapirostatic bool	smtp_data __P((SMTP_T *, ENVELOPE *));
36390792Sgshapiro
364132943Sgshapiro#define MSG_TEMPFAIL "451 4.3.2 Please try again later"
36590792Sgshapiro
36690792Sgshapiro#if MILTER
36790792Sgshapiro# define MILTER_ABORT(e)	milter_abort((e))
368110560Sgshapiro
36990792Sgshapiro# define MILTER_REPLY(str)						\
37090792Sgshapiro	{								\
37190792Sgshapiro		int savelogusrerrs = LogUsrErrs;			\
37290792Sgshapiro									\
373168515Sgshapiro		milter_cmd_fail = true;					\
37490792Sgshapiro		switch (state)						\
37590792Sgshapiro		{							\
376157001Sgshapiro		  case SMFIR_SHUTDOWN:					\
377157001Sgshapiro			if (MilterLogLevel > 3)				\
378157001Sgshapiro			{						\
379157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
380157001Sgshapiro					  "Milter: %s=%s, reject=421, errormode=4",	\
381157001Sgshapiro					  str, addr);			\
382157001Sgshapiro				LogUsrErrs = false;			\
383157001Sgshapiro			}						\
384157001Sgshapiro			{						\
385157001Sgshapiro				bool tsave = QuickAbort;		\
386157001Sgshapiro									\
387157001Sgshapiro				QuickAbort = false;			\
388157001Sgshapiro				usrerr("421 4.3.0 closing connection");	\
389157001Sgshapiro				QuickAbort = tsave;			\
390157001Sgshapiro				e->e_sendqueue = NULL;			\
391157001Sgshapiro				goto doquit;				\
392157001Sgshapiro			}						\
393157001Sgshapiro			break;						\
39490792Sgshapiro		  case SMFIR_REPLYCODE:					\
39590792Sgshapiro			if (MilterLogLevel > 3)				\
39690792Sgshapiro			{						\
39790792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
39890792Sgshapiro					  "Milter: %s=%s, reject=%s",	\
39990792Sgshapiro					  str, addr, response);		\
40090792Sgshapiro				LogUsrErrs = false;			\
40190792Sgshapiro			}						\
402157001Sgshapiro			if (strncmp(response, "421 ", 4) == 0		\
403157001Sgshapiro			    || strncmp(response, "421-", 4) == 0)	\
404132943Sgshapiro			{						\
405132943Sgshapiro				bool tsave = QuickAbort;		\
406132943Sgshapiro									\
407132943Sgshapiro				QuickAbort = false;			\
408132943Sgshapiro				usrerr(response);			\
409132943Sgshapiro				QuickAbort = tsave;			\
410132943Sgshapiro				e->e_sendqueue = NULL;			\
411132943Sgshapiro				goto doquit;				\
412132943Sgshapiro			}						\
413132943Sgshapiro			else						\
414132943Sgshapiro				usrerr(response);			\
41590792Sgshapiro			break;						\
41690792Sgshapiro									\
41790792Sgshapiro		  case SMFIR_REJECT:					\
41890792Sgshapiro			if (MilterLogLevel > 3)				\
41990792Sgshapiro			{						\
42090792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
42190792Sgshapiro					  "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
42290792Sgshapiro					  str, addr);			\
42390792Sgshapiro				LogUsrErrs = false;			\
42490792Sgshapiro			}						\
42590792Sgshapiro			usrerr("550 5.7.1 Command rejected");		\
42690792Sgshapiro			break;						\
42790792Sgshapiro									\
42890792Sgshapiro		  case SMFIR_DISCARD:					\
42990792Sgshapiro			if (MilterLogLevel > 3)				\
43090792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
43190792Sgshapiro					  "Milter: %s=%s, discard",	\
43290792Sgshapiro					  str, addr);			\
43390792Sgshapiro			e->e_flags |= EF_DISCARD;			\
434168515Sgshapiro			milter_cmd_fail = false;			\
43590792Sgshapiro			break;						\
43690792Sgshapiro									\
43790792Sgshapiro		  case SMFIR_TEMPFAIL:					\
43890792Sgshapiro			if (MilterLogLevel > 3)				\
43990792Sgshapiro			{						\
44090792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
44190792Sgshapiro					  "Milter: %s=%s, reject=%s",	\
44290792Sgshapiro					  str, addr, MSG_TEMPFAIL);	\
44390792Sgshapiro				LogUsrErrs = false;			\
44490792Sgshapiro			}						\
44590792Sgshapiro			usrerr(MSG_TEMPFAIL);				\
44690792Sgshapiro			break;						\
447168515Sgshapiro		  default:						\
448168515Sgshapiro			milter_cmd_fail = false;			\
449168515Sgshapiro			break;						\
45090792Sgshapiro		}							\
45190792Sgshapiro		LogUsrErrs = savelogusrerrs;				\
45290792Sgshapiro		if (response != NULL)					\
45390792Sgshapiro			sm_free(response); /* XXX */			\
45490792Sgshapiro	}
45590792Sgshapiro
45690792Sgshapiro#else /* MILTER */
45790792Sgshapiro# define MILTER_ABORT(e)
45890792Sgshapiro#endif /* MILTER */
45990792Sgshapiro
46090792Sgshapiro/* clear all SMTP state (for HELO/EHLO/RSET) */
46190792Sgshapiro#define CLEAR_STATE(cmd)					\
462132943Sgshapirodo								\
46390792Sgshapiro{								\
46490792Sgshapiro	/* abort milter filters */				\
46590792Sgshapiro	MILTER_ABORT(e);					\
46690792Sgshapiro								\
46790792Sgshapiro	if (smtp.sm_nrcpts > 0)					\
46890792Sgshapiro	{							\
46990792Sgshapiro		logundelrcpts(e, cmd, 10, false);		\
47090792Sgshapiro		smtp.sm_nrcpts = 0;				\
47190792Sgshapiro		macdefine(&e->e_macro, A_PERM,			\
47290792Sgshapiro			  macid("{nrcpts}"), "0");		\
47390792Sgshapiro	}							\
47490792Sgshapiro								\
47590792Sgshapiro	e->e_sendqueue = NULL;					\
47690792Sgshapiro	e->e_flags |= EF_CLRQUEUE;				\
47790792Sgshapiro								\
47890792Sgshapiro	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))	\
47990792Sgshapiro		logsender(e, NULL);				\
48090792Sgshapiro	e->e_flags &= ~EF_LOGSENDER;				\
48190792Sgshapiro								\
48290792Sgshapiro	/* clean up a bit */					\
48390792Sgshapiro	smtp.sm_gotmail = false;				\
48490792Sgshapiro	SuprErrs = true;					\
48590792Sgshapiro	dropenvelope(e, true, false);				\
48690792Sgshapiro	sm_rpool_free(e->e_rpool);				\
48790792Sgshapiro	e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL));	\
48890792Sgshapiro	CurEnv = e;						\
489168515Sgshapiro	e->e_features = features;				\
490132943Sgshapiro								\
491132943Sgshapiro	/* put back discard bit */				\
492132943Sgshapiro	if (smtp.sm_discard)					\
493132943Sgshapiro		e->e_flags |= EF_DISCARD;			\
494132943Sgshapiro								\
495132943Sgshapiro	/* restore connection quarantining */			\
496132943Sgshapiro	if (smtp.sm_quarmsg == NULL)				\
497132943Sgshapiro	{							\
498132943Sgshapiro		e->e_quarmsg = NULL;				\
499132943Sgshapiro		macdefine(&e->e_macro, A_PERM,			\
500132943Sgshapiro			macid("{quarantine}"), "");		\
501132943Sgshapiro	}							\
502132943Sgshapiro	else							\
503132943Sgshapiro	{							\
504132943Sgshapiro		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,	\
505132943Sgshapiro						smtp.sm_quarmsg);	\
506132943Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"),	\
507132943Sgshapiro			  e->e_quarmsg);			\
508132943Sgshapiro	}							\
509132943Sgshapiro} while (0)
51090792Sgshapiro
51190792Sgshapiro/* sleep to flatten out connection load */
51290792Sgshapiro#define MIN_DELAY_LOG	15	/* wait before logging this again */
51390792Sgshapiro
51490792Sgshapiro/* is it worth setting the process title for 1s? */
51590792Sgshapiro#define DELAY_CONN(cmd)						\
51690792Sgshapiro	if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA)	\
51790792Sgshapiro	{							\
51890792Sgshapiro		time_t dnow;					\
51990792Sgshapiro								\
52090792Sgshapiro		sm_setproctitle(true, e,			\
52190792Sgshapiro				"%s: %s: delaying %s: load average: %d", \
52290792Sgshapiro				qid_printname(e), CurSmtpClient,	\
52390792Sgshapiro				cmd, DelayLA);	\
52490792Sgshapiro		if (LogLevel > 8 && (dnow = curtime()) > log_delay)	\
52590792Sgshapiro		{						\
52690792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,		\
52790792Sgshapiro				  "delaying=%s, load average=%d >= %d",	\
52890792Sgshapiro				  cmd, CurrentLA, DelayLA);		\
52990792Sgshapiro			log_delay = dnow + MIN_DELAY_LOG;	\
53090792Sgshapiro		}						\
53190792Sgshapiro		(void) sleep(1);				\
53290792Sgshapiro		sm_setproctitle(true, e, "%s %s: %.80s",	\
53390792Sgshapiro				qid_printname(e), CurSmtpClient, inp);	\
53490792Sgshapiro	}
53590792Sgshapiro
536168515Sgshapirostatic bool SevenBitInput_Saved;	/* saved version of SevenBitInput */
53790792Sgshapiro
53838032Spetervoid
53964562Sgshapirosmtp(nullserver, d_flags, e)
54064562Sgshapiro	char *volatile nullserver;
54164562Sgshapiro	BITMAP256 d_flags;
54238032Speter	register ENVELOPE *volatile e;
54338032Speter{
54438032Speter	register char *volatile p;
54564562Sgshapiro	register struct cmd *volatile c = NULL;
54638032Speter	char *cmd;
54738032Speter	auto ADDRESS *vrfyqueue;
54838032Speter	ADDRESS *a;
54938032Speter	volatile bool gothello;		/* helo command received */
55038032Speter	bool vrfy;			/* set if this is a vrfy command */
55138032Speter	char *volatile protocol;	/* sending protocol */
55238032Speter	char *volatile sendinghost;	/* sending hostname */
55338032Speter	char *volatile peerhostname;	/* name of SMTP peer or "localhost" */
55438032Speter	auto char *delimptr;
55538032Speter	char *id;
55690792Sgshapiro	volatile unsigned int n_badcmds = 0;	/* count of bad commands */
55790792Sgshapiro	volatile unsigned int n_badrcpts = 0;	/* number of rejected RCPT */
55890792Sgshapiro	volatile unsigned int n_verifies = 0;	/* count of VRFY/EXPN */
55990792Sgshapiro	volatile unsigned int n_etrn = 0;	/* count of ETRN */
56090792Sgshapiro	volatile unsigned int n_noop = 0;	/* count of NOOP/VERB/etc */
56190792Sgshapiro	volatile unsigned int n_helo = 0;	/* count of HELO/EHLO */
56238032Speter	bool ok;
56390792Sgshapiro	volatile bool first;
56490792Sgshapiro	volatile bool tempfail = false;
56564562Sgshapiro	volatile time_t wt;		/* timeout after too many commands */
56664562Sgshapiro	volatile time_t previous;	/* time after checksmtpattack() */
56790792Sgshapiro	volatile bool lognullconnection = true;
56838032Speter	register char *q;
56990792Sgshapiro	SMTP_T smtp;
57064562Sgshapiro	char *addr;
57164562Sgshapiro	char *greetcode = "220";
57290792Sgshapiro	char *hostname;			/* my hostname ($j) */
57338032Speter	QUEUE_CHAR *new;
57464562Sgshapiro	char *args[MAXSMTPARGS];
575168515Sgshapiro	char inp[MAXINPLINE];
576168515Sgshapiro#if MAXINPLINE < MAXLINE
577168515Sgshapiro ERROR _MAXINPLINE must NOT be less than _MAXLINE: MAXINPLINE < MAXLINE
578168515Sgshapiro#endif /* MAXINPLINE < MAXLINE */
57938032Speter	char cmdbuf[MAXLINE];
58090792Sgshapiro#if SASL
58164562Sgshapiro	sasl_conn_t *conn;
58264562Sgshapiro	volatile bool sasl_ok;
58390792Sgshapiro	volatile unsigned int n_auth = 0;	/* count of AUTH commands */
58464562Sgshapiro	bool ismore;
58564562Sgshapiro	int result;
58664562Sgshapiro	volatile int authenticating;
58764562Sgshapiro	char *user;
58898121Sgshapiro	char *in, *out2;
58998121Sgshapiro# if SASL >= 20000
590168515Sgshapiro	char *auth_id = NULL;
59198121Sgshapiro	const char *out;
592102528Sgshapiro	sasl_ssf_t ext_ssf;
593120256Sgshapiro	char localip[60], remoteip[60];
59498121Sgshapiro# else /* SASL >= 20000 */
59598121Sgshapiro	char *out;
59664562Sgshapiro	const char *errstr;
59798121Sgshapiro	sasl_external_properties_t ext_ssf;
598120256Sgshapiro	struct sockaddr_in saddr_l;
599120256Sgshapiro	struct sockaddr_in saddr_r;
60098121Sgshapiro# endif /* SASL >= 20000 */
60198121Sgshapiro	sasl_security_properties_t ssp;
60298121Sgshapiro	sasl_ssf_t *ssf;
60390792Sgshapiro	unsigned int inlen, out2len;
60464562Sgshapiro	unsigned int outlen;
60564562Sgshapiro	char *volatile auth_type;
60664562Sgshapiro	char *mechlist;
60790792Sgshapiro	volatile unsigned int n_mechs;
60890792Sgshapiro	unsigned int len;
609168515Sgshapiro#else /* SASL */
61090792Sgshapiro#endif /* SASL */
611132943Sgshapiro	int r;
61290792Sgshapiro#if STARTTLS
61366494Sgshapiro	int rfd, wfd;
61490792Sgshapiro	volatile bool tls_active = false;
615132943Sgshapiro	volatile bool smtps = bitnset(D_SMTPS, d_flags);
61664562Sgshapiro	bool saveQuickAbort;
61764562Sgshapiro	bool saveSuprErrs;
61890792Sgshapiro	time_t tlsstart;
61990792Sgshapiro#endif /* STARTTLS */
62090792Sgshapiro	volatile unsigned int features;
62190792Sgshapiro#if PIPELINING
62290792Sgshapiro# if _FFR_NO_PIPE
62390792Sgshapiro	int np_log = 0;
62490792Sgshapiro# endif /* _FFR_NO_PIPE */
62590792Sgshapiro#endif /* PIPELINING */
62690792Sgshapiro	volatile time_t log_delay = (time_t) 0;
627168515Sgshapiro#if MILTER
628168515Sgshapiro	volatile bool milter_cmd_done, milter_cmd_safe;
629168515Sgshapiro	volatile bool milter_rcpt_added, milter_cmd_fail;
630168515Sgshapiro	ADDRESS addr_st;
631168515Sgshapiro# define p_addr_st	&addr_st
632168515Sgshapiro#else /* MILTER */
633168515Sgshapiro# define p_addr_st	NULL
634168515Sgshapiro#endif /* MILTER */
635168515Sgshapiro	size_t inplen;
63638032Speter
637168515Sgshapiro	SevenBitInput_Saved = SevenBitInput;
63890792Sgshapiro	smtp.sm_nrcpts = 0;
63990792Sgshapiro#if MILTER
64090792Sgshapiro	smtp.sm_milterize = (nullserver == NULL);
64190792Sgshapiro	smtp.sm_milterlist = false;
642168515Sgshapiro	addr = NULL;
64390792Sgshapiro#endif /* MILTER */
64490792Sgshapiro
64590792Sgshapiro	/* setup I/O fd correctly for the SMTP server */
64690792Sgshapiro	setup_smtpd_io();
64790792Sgshapiro
64890792Sgshapiro#if SM_HEAP_CHECK
64990792Sgshapiro	if (sm_debug_active(&DebugLeakSmtp, 1))
65038032Speter	{
65190792Sgshapiro		sm_heap_newgroup();
65290792Sgshapiro		sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
65338032Speter	}
65490792Sgshapiro#endif /* SM_HEAP_CHECK */
65564562Sgshapiro
65690792Sgshapiro	/* XXX the rpool should be set when e is initialized in main() */
65790792Sgshapiro	e->e_rpool = sm_rpool_new_x(NULL);
65890792Sgshapiro	e->e_macro.mac_rpool = e->e_rpool;
65990792Sgshapiro
66038032Speter	settime(e);
66190792Sgshapiro	sm_getla();
66238032Speter	peerhostname = RealHostName;
66338032Speter	if (peerhostname == NULL)
66438032Speter		peerhostname = "localhost";
66538032Speter	CurHostName = peerhostname;
66638032Speter	CurSmtpClient = macvalue('_', e);
66738032Speter	if (CurSmtpClient == NULL)
66838032Speter		CurSmtpClient = CurHostName;
66938032Speter
67038032Speter	/* check_relay may have set discard bit, save for later */
67190792Sgshapiro	smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
67238032Speter
67390792Sgshapiro#if PIPELINING
67490792Sgshapiro	/* auto-flush output when reading input */
67590792Sgshapiro	(void) sm_io_autoflush(InChannel, OutChannel);
67690792Sgshapiro#endif /* PIPELINING */
67764562Sgshapiro
67890792Sgshapiro	sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
67990792Sgshapiro
68090792Sgshapiro	/* Set default features for server. */
68190792Sgshapiro	features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
68290792Sgshapiro		     bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
68390792Sgshapiro		| (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
68490792Sgshapiro		| (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
68590792Sgshapiro			: (SRV_OFFER_EXPN
68690792Sgshapiro			  | (bitset(PRIV_NOVERB, PrivacyFlags)
68790792Sgshapiro			     ? SRV_NONE : SRV_OFFER_VERB)))
688159609Sgshapiro		| ((bitset(PRIV_NORECEIPTS, PrivacyFlags) || !SendMIMEErrors)
689159609Sgshapiro			 ? SRV_NONE : SRV_OFFER_DSN)
69090792Sgshapiro#if SASL
69190792Sgshapiro		| (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
692132943Sgshapiro		| (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC
693132943Sgshapiro							  : SRV_NONE)
69490792Sgshapiro#endif /* SASL */
69590792Sgshapiro#if PIPELINING
69690792Sgshapiro		| SRV_OFFER_PIPE
69790792Sgshapiro#endif /* PIPELINING */
69890792Sgshapiro#if STARTTLS
69990792Sgshapiro		| (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
70090792Sgshapiro		| (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
70190792Sgshapiro						       : SRV_VRFY_CLT)
70290792Sgshapiro#endif /* STARTTLS */
70390792Sgshapiro		;
70490792Sgshapiro	if (nullserver == NULL)
70590792Sgshapiro	{
70690792Sgshapiro		features = srvfeatures(e, CurSmtpClient, features);
70790792Sgshapiro		if (bitset(SRV_TMP_FAIL, features))
70890792Sgshapiro		{
70990792Sgshapiro			if (LogLevel > 4)
71090792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
71190792Sgshapiro					  "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
71290792Sgshapiro					  CurSmtpClient);
71390792Sgshapiro			nullserver = "450 4.3.0 Please try again later.";
71490792Sgshapiro		}
715132943Sgshapiro		else
716132943Sgshapiro		{
71790792Sgshapiro#if PIPELINING
71890792Sgshapiro# if _FFR_NO_PIPE
719132943Sgshapiro			if (bitset(SRV_NO_PIPE, features))
720132943Sgshapiro			{
721132943Sgshapiro				/* for consistency */
722132943Sgshapiro				features &= ~SRV_OFFER_PIPE;
723132943Sgshapiro			}
72490792Sgshapiro# endif /* _FFR_NO_PIPE */
72590792Sgshapiro#endif /* PIPELINING */
726132943Sgshapiro#if SASL
727132943Sgshapiro			if (bitset(SRV_REQ_SEC, features))
728132943Sgshapiro				SASLOpts |= SASL_SEC_NOPLAINTEXT;
729132943Sgshapiro			else
730132943Sgshapiro				SASLOpts &= ~SASL_SEC_NOPLAINTEXT;
731132943Sgshapiro#endif /* SASL */
732132943Sgshapiro		}
73390792Sgshapiro	}
734132943Sgshapiro	else if (strncmp(nullserver, "421 ", 4) == 0)
735132943Sgshapiro	{
736132943Sgshapiro		message(nullserver);
737132943Sgshapiro		goto doquit;
738132943Sgshapiro	}
73990792Sgshapiro
740168515Sgshapiro	e->e_features = features;
74190792Sgshapiro	hostname = macvalue('j', e);
74290792Sgshapiro#if SASL
743132943Sgshapiro	if (AuthRealm == NULL)
744132943Sgshapiro		AuthRealm = hostname;
74590792Sgshapiro	sasl_ok = bitset(SRV_OFFER_AUTH, features);
74664562Sgshapiro	n_mechs = 0;
74790792Sgshapiro	authenticating = SASL_NOT_AUTH;
74864562Sgshapiro
74964562Sgshapiro	/* SASL server new connection */
75090792Sgshapiro	if (sasl_ok)
75138032Speter	{
75298121Sgshapiro# if SASL >= 20000
753132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL,
75498121Sgshapiro					 NULL, 0, &conn);
75598121Sgshapiro# elif SASL > 10505
75690792Sgshapiro		/* use empty realm: only works in SASL > 1.5.5 */
757132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn);
75898121Sgshapiro# else /* SASL >= 20000 */
75990792Sgshapiro		/* use no realm -> realm is set to hostname by SASL lib */
760132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0,
76190792Sgshapiro					 &conn);
76298121Sgshapiro# endif /* SASL >= 20000 */
76390792Sgshapiro		sasl_ok = result == SASL_OK;
76490792Sgshapiro		if (!sasl_ok)
76590792Sgshapiro		{
76690792Sgshapiro			if (LogLevel > 9)
76790792Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
76890792Sgshapiro					  "AUTH error: sasl_server_new failed=%d",
76990792Sgshapiro					  result);
77090792Sgshapiro		}
77190792Sgshapiro	}
77290792Sgshapiro	if (sasl_ok)
77390792Sgshapiro	{
77464562Sgshapiro		/*
77564562Sgshapiro		**  SASL set properties for sasl
77664562Sgshapiro		**  set local/remote IP
77798121Sgshapiro		**  XXX Cyrus SASL v1 only supports IPv4
77864562Sgshapiro		**
77964562Sgshapiro		**  XXX where exactly are these used/required?
78064562Sgshapiro		**  Kerberos_v4
78164562Sgshapiro		*/
78264562Sgshapiro
78398121Sgshapiro# if SASL >= 20000
784147078Sgshapiro		localip[0] = remoteip[0] = '\0';
78598121Sgshapiro#  if NETINET || NETINET6
78690792Sgshapiro		in = macvalue(macid("{daemon_family}"), e);
78798121Sgshapiro		if (in != NULL && (
78898121Sgshapiro#   if NETINET6
78998121Sgshapiro		    strcmp(in, "inet6") == 0 ||
79098121Sgshapiro#   endif /* NETINET6 */
79198121Sgshapiro		    strcmp(in, "inet") == 0))
79298121Sgshapiro		{
79398121Sgshapiro			SOCKADDR_LEN_T addrsize;
79498121Sgshapiro			SOCKADDR saddr_l;
79598121Sgshapiro			SOCKADDR saddr_r;
79698121Sgshapiro
79798121Sgshapiro			addrsize = sizeof(saddr_r);
79898121Sgshapiro			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
79998121Sgshapiro						      NULL),
80098121Sgshapiro					(struct sockaddr *) &saddr_r,
80198121Sgshapiro					&addrsize) == 0)
80298121Sgshapiro			{
80398121Sgshapiro				if (iptostring(&saddr_r, addrsize,
804168515Sgshapiro					       remoteip, sizeof(remoteip)))
80598121Sgshapiro				{
80698121Sgshapiro					sasl_setprop(conn, SASL_IPREMOTEPORT,
80798121Sgshapiro						     remoteip);
80898121Sgshapiro				}
80998121Sgshapiro				addrsize = sizeof(saddr_l);
81098121Sgshapiro				if (getsockname(sm_io_getinfo(InChannel,
81198121Sgshapiro							      SM_IO_WHAT_FD,
81298121Sgshapiro							      NULL),
81398121Sgshapiro						(struct sockaddr *) &saddr_l,
81498121Sgshapiro						&addrsize) == 0)
81598121Sgshapiro				{
81698121Sgshapiro					if (iptostring(&saddr_l, addrsize,
81798121Sgshapiro						       localip,
818168515Sgshapiro						       sizeof(localip)))
81998121Sgshapiro					{
82098121Sgshapiro						sasl_setprop(conn,
82198121Sgshapiro							     SASL_IPLOCALPORT,
82298121Sgshapiro							     localip);
82398121Sgshapiro					}
82498121Sgshapiro				}
82598121Sgshapiro			}
82698121Sgshapiro		}
82798121Sgshapiro#  endif /* NETINET || NETINET6 */
82898121Sgshapiro# else /* SASL >= 20000 */
82998121Sgshapiro#  if NETINET
83098121Sgshapiro		in = macvalue(macid("{daemon_family}"), e);
83164562Sgshapiro		if (in != NULL && strcmp(in, "inet") == 0)
83264562Sgshapiro		{
83364562Sgshapiro			SOCKADDR_LEN_T addrsize;
83464562Sgshapiro
83564562Sgshapiro			addrsize = sizeof(struct sockaddr_in);
83690792Sgshapiro			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
83790792Sgshapiro						      NULL),
83864562Sgshapiro					(struct sockaddr *)&saddr_r,
83964562Sgshapiro					&addrsize) == 0)
84064562Sgshapiro			{
84164562Sgshapiro				sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
84264562Sgshapiro				addrsize = sizeof(struct sockaddr_in);
84390792Sgshapiro				if (getsockname(sm_io_getinfo(InChannel,
84490792Sgshapiro							      SM_IO_WHAT_FD,
84590792Sgshapiro							      NULL),
84664562Sgshapiro						(struct sockaddr *)&saddr_l,
84764562Sgshapiro						&addrsize) == 0)
84864562Sgshapiro					sasl_setprop(conn, SASL_IP_LOCAL,
84964562Sgshapiro						     &saddr_l);
85064562Sgshapiro			}
85164562Sgshapiro		}
85298121Sgshapiro#  endif /* NETINET */
85398121Sgshapiro# endif /* SASL >= 20000 */
85464562Sgshapiro
85564562Sgshapiro		auth_type = NULL;
85664562Sgshapiro		mechlist = NULL;
85764562Sgshapiro		user = NULL;
85890792Sgshapiro# if 0
85990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
86090792Sgshapiro			macid("{auth_author}"), NULL);
86190792Sgshapiro# endif /* 0 */
86264562Sgshapiro
86364562Sgshapiro		/* set properties */
864168515Sgshapiro		(void) memset(&ssp, '\0', sizeof(ssp));
86590792Sgshapiro
86664562Sgshapiro		/* XXX should these be options settable via .cf ? */
86764562Sgshapiro		/* ssp.min_ssf = 0; is default due to memset() */
86864562Sgshapiro		{
86990792Sgshapiro			ssp.max_ssf = MaxSLBits;
87064562Sgshapiro			ssp.maxbufsize = MAXOUTLEN;
87164562Sgshapiro		}
87264562Sgshapiro		ssp.security_flags = SASLOpts & SASL_SEC_MASK;
87364562Sgshapiro		sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
87464562Sgshapiro
87564562Sgshapiro		if (sasl_ok)
87664562Sgshapiro		{
87764562Sgshapiro			/*
87864562Sgshapiro			**  external security strength factor;
87990792Sgshapiro			**	currently we have none so zero
88064562Sgshapiro			*/
88190792Sgshapiro
88298121Sgshapiro# if SASL >= 20000
88398121Sgshapiro			ext_ssf = 0;
88498121Sgshapiro			auth_id = NULL;
88598121Sgshapiro			sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL,
88698121Sgshapiro						 &ext_ssf) == SASL_OK) &&
88798121Sgshapiro				   (sasl_setprop(conn, SASL_AUTH_EXTERNAL,
888102528Sgshapiro						 auth_id) == SASL_OK));
88998121Sgshapiro# else /* SASL >= 20000 */
89064562Sgshapiro			ext_ssf.ssf = 0;
89164562Sgshapiro			ext_ssf.auth_id = NULL;
89264562Sgshapiro			sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
89364562Sgshapiro					       &ext_ssf) == SASL_OK;
89498121Sgshapiro# endif /* SASL >= 20000 */
89564562Sgshapiro		}
89664562Sgshapiro		if (sasl_ok)
89764562Sgshapiro			n_mechs = saslmechs(conn, &mechlist);
89838032Speter	}
89990792Sgshapiro#endif /* SASL */
90038032Speter
901120256Sgshapiro#if STARTTLS
902120256Sgshapiro#endif /* STARTTLS */
903120256Sgshapiro
90490792Sgshapiro#if MILTER
90590792Sgshapiro	if (smtp.sm_milterize)
90664562Sgshapiro	{
90764562Sgshapiro		char state;
90864562Sgshapiro
90964562Sgshapiro		/* initialize mail filter connection */
91090792Sgshapiro		smtp.sm_milterlist = milter_init(e, &state);
91164562Sgshapiro		switch (state)
91264562Sgshapiro		{
91364562Sgshapiro		  case SMFIR_REJECT:
91490792Sgshapiro			if (MilterLogLevel > 3)
91590792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
91694334Sgshapiro					  "Milter: initialization failed, rejecting commands");
91764562Sgshapiro			greetcode = "554";
91864562Sgshapiro			nullserver = "Command rejected";
91990792Sgshapiro			smtp.sm_milterize = false;
92064562Sgshapiro			break;
92164562Sgshapiro
92264562Sgshapiro		  case SMFIR_TEMPFAIL:
92390792Sgshapiro			if (MilterLogLevel > 3)
92490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
92594334Sgshapiro					  "Milter: initialization failed, temp failing commands");
92690792Sgshapiro			tempfail = true;
92790792Sgshapiro			smtp.sm_milterize = false;
92864562Sgshapiro			break;
929157001Sgshapiro
930157001Sgshapiro		  case SMFIR_SHUTDOWN:
931157001Sgshapiro			if (MilterLogLevel > 3)
932157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
933157001Sgshapiro					  "Milter: initialization failed, closing connection");
934157001Sgshapiro			tempfail = true;
935157001Sgshapiro			smtp.sm_milterize = false;
936157001Sgshapiro			message("421 4.7.0 %s closing connection",
937157001Sgshapiro					MyHostName);
938157001Sgshapiro
939157001Sgshapiro			/* arrange to ignore send list */
940157001Sgshapiro			e->e_sendqueue = NULL;
941157001Sgshapiro			goto doquit;
94264562Sgshapiro		}
94364562Sgshapiro	}
94464562Sgshapiro
94590792Sgshapiro	if (smtp.sm_milterlist && smtp.sm_milterize &&
94690792Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
94764562Sgshapiro	{
94864562Sgshapiro		char state;
94990792Sgshapiro		char *response;
95064562Sgshapiro
951161389Sgshapiro		q = macvalue(macid("{client_name}"), e);
952168515Sgshapiro		SM_ASSERT(q != NULL || OpMode == MD_SMTP);
953168515Sgshapiro		if (q == NULL)
954168515Sgshapiro			q = "localhost";
955161389Sgshapiro		response = milter_connect(q, RealHostAddr, e, &state);
95664562Sgshapiro		switch (state)
95764562Sgshapiro		{
95864562Sgshapiro		  case SMFIR_REPLYCODE:	/* REPLYCODE shouldn't happen */
95964562Sgshapiro		  case SMFIR_REJECT:
96090792Sgshapiro			if (MilterLogLevel > 3)
96190792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
96290792Sgshapiro					  "Milter: connect: host=%s, addr=%s, rejecting commands",
96390792Sgshapiro					  peerhostname,
96490792Sgshapiro					  anynet_ntoa(&RealHostAddr));
96564562Sgshapiro			greetcode = "554";
96664562Sgshapiro			nullserver = "Command rejected";
96790792Sgshapiro			smtp.sm_milterize = false;
96864562Sgshapiro			break;
96964562Sgshapiro
97064562Sgshapiro		  case SMFIR_TEMPFAIL:
97190792Sgshapiro			if (MilterLogLevel > 3)
97290792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
97390792Sgshapiro					  "Milter: connect: host=%s, addr=%s, temp failing commands",
97490792Sgshapiro					  peerhostname,
97590792Sgshapiro					  anynet_ntoa(&RealHostAddr));
97690792Sgshapiro			tempfail = true;
97790792Sgshapiro			smtp.sm_milterize = false;
97864562Sgshapiro			break;
979110560Sgshapiro
980110560Sgshapiro		  case SMFIR_SHUTDOWN:
981110560Sgshapiro			if (MilterLogLevel > 3)
982110560Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
983110560Sgshapiro					  "Milter: connect: host=%s, addr=%s, shutdown",
984110560Sgshapiro					  peerhostname,
985110560Sgshapiro					  anynet_ntoa(&RealHostAddr));
986110560Sgshapiro			tempfail = true;
987110560Sgshapiro			smtp.sm_milterize = false;
988110560Sgshapiro			message("421 4.7.0 %s closing connection",
989110560Sgshapiro					MyHostName);
990110560Sgshapiro
991110560Sgshapiro			/* arrange to ignore send list */
992110560Sgshapiro			e->e_sendqueue = NULL;
993110560Sgshapiro			goto doquit;
99464562Sgshapiro		}
99590792Sgshapiro		if (response != NULL)
99690792Sgshapiro			sm_free(response); /* XXX */
99764562Sgshapiro	}
99890792Sgshapiro#endif /* MILTER */
99964562Sgshapiro
1000132943Sgshapiro	/*
1001132943Sgshapiro	**  Broken proxies and SMTP slammers
1002132943Sgshapiro	**  push data without waiting, catch them
1003132943Sgshapiro	*/
1004132943Sgshapiro
1005132943Sgshapiro	if (
100690792Sgshapiro#if STARTTLS
1007132943Sgshapiro	    !smtps &&
1008132943Sgshapiro#endif /* STARTTLS */
1009168515Sgshapiro	    *greetcode == '2' && nullserver == NULL)
1010132943Sgshapiro	{
1011132943Sgshapiro		time_t msecs = 0;
1012132943Sgshapiro		char **pvp;
1013132943Sgshapiro		char pvpbuf[PSBUFSIZE];
1014132943Sgshapiro
1015132943Sgshapiro		/* Ask the rulesets how long to pause */
1016132943Sgshapiro		pvp = NULL;
1017132943Sgshapiro		r = rscap("greet_pause", peerhostname,
1018132943Sgshapiro			  anynet_ntoa(&RealHostAddr), e,
1019132943Sgshapiro			  &pvp, pvpbuf, sizeof(pvpbuf));
1020132943Sgshapiro		if (r == EX_OK && pvp != NULL && pvp[0] != NULL &&
1021132943Sgshapiro		    (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
1022132943Sgshapiro		{
1023132943Sgshapiro			msecs = strtol(pvp[1], NULL, 10);
1024132943Sgshapiro		}
1025132943Sgshapiro
1026132943Sgshapiro		if (msecs > 0)
1027132943Sgshapiro		{
1028132943Sgshapiro			int fd;
1029132943Sgshapiro			fd_set readfds;
1030132943Sgshapiro			struct timeval timeout;
1031157001Sgshapiro			struct timeval bp, ep, tp; /* {begin,end,total}pause */
1032168515Sgshapiro			int eoftest;
1033132943Sgshapiro
1034132943Sgshapiro			/* pause for a moment */
1035132943Sgshapiro			timeout.tv_sec = msecs / 1000;
1036132943Sgshapiro			timeout.tv_usec = (msecs % 1000) * 1000;
1037132943Sgshapiro
1038132943Sgshapiro			/* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
1039132943Sgshapiro			if (timeout.tv_sec >= 300)
1040132943Sgshapiro			{
1041132943Sgshapiro				timeout.tv_sec = 300;
1042132943Sgshapiro				timeout.tv_usec = 0;
1043132943Sgshapiro			}
1044132943Sgshapiro
1045132943Sgshapiro			/* check if data is on the socket during the pause */
1046132943Sgshapiro			fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
1047132943Sgshapiro			FD_ZERO(&readfds);
1048132943Sgshapiro			SM_FD_SET(fd, &readfds);
1049157001Sgshapiro			gettimeofday(&bp, NULL);
1050132943Sgshapiro			if (select(fd + 1, FDSET_CAST &readfds,
1051132943Sgshapiro			    NULL, NULL, &timeout) > 0 &&
1052168515Sgshapiro			    FD_ISSET(fd, &readfds) &&
1053168515Sgshapiro			    (eoftest = sm_io_getc(InChannel, SM_TIME_DEFAULT))
1054168515Sgshapiro			    != SM_IO_EOF)
1055132943Sgshapiro			{
1056168515Sgshapiro				sm_io_ungetc(InChannel, SM_TIME_DEFAULT,
1057168515Sgshapiro					     eoftest);
1058157001Sgshapiro				gettimeofday(&ep, NULL);
1059157001Sgshapiro				timersub(&ep, &bp, &tp);
1060132943Sgshapiro				greetcode = "554";
1061132943Sgshapiro				nullserver = "Command rejected";
1062132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
1063168515Sgshapiro					  "rejecting commands from %s [%s] due to pre-greeting traffic after %d seconds",
1064132943Sgshapiro					  peerhostname,
1065168515Sgshapiro					  anynet_ntoa(&RealHostAddr),
1066168515Sgshapiro					  (int) tp.tv_sec +
1067157001Sgshapiro						(tp.tv_usec >= 500000 ? 1 : 0)
1068157001Sgshapiro					 );
1069132943Sgshapiro			}
1070132943Sgshapiro		}
1071132943Sgshapiro	}
1072132943Sgshapiro
1073132943Sgshapiro#if STARTTLS
107490792Sgshapiro	/* If this an smtps connection, start TLS now */
107590792Sgshapiro	if (smtps)
1076120256Sgshapiro	{
1077120256Sgshapiro		Errors = 0;
107890792Sgshapiro		goto starttls;
1079120256Sgshapiro	}
108090792Sgshapiro
108190792Sgshapiro  greeting:
108290792Sgshapiro
108390792Sgshapiro#endif /* STARTTLS */
108490792Sgshapiro
108538032Speter	/* output the first line, inserting "ESMTP" as second word */
108690792Sgshapiro	if (*greetcode == '5')
1087168515Sgshapiro		(void) sm_snprintf(inp, sizeof(inp),
1088168515Sgshapiro				"%s not accepting messages", hostname);
108990792Sgshapiro	else
1090168515Sgshapiro		expand(SmtpGreeting, inp, sizeof(inp), e);
109190792Sgshapiro
109238032Speter	p = strchr(inp, '\n');
109338032Speter	if (p != NULL)
109438032Speter		*p++ = '\0';
109538032Speter	id = strchr(inp, ' ');
109638032Speter	if (id == NULL)
109738032Speter		id = &inp[strlen(inp)];
109864562Sgshapiro	if (p == NULL)
1099168515Sgshapiro		(void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
110064562Sgshapiro			 "%s %%.*s ESMTP%%s", greetcode);
110164562Sgshapiro	else
1102168515Sgshapiro		(void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
110364562Sgshapiro			 "%s-%%.*s ESMTP%%s", greetcode);
110466494Sgshapiro	message(cmdbuf, (int) (id - inp), inp, id);
110538032Speter
110638032Speter	/* output remaining lines */
110738032Speter	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
110838032Speter	{
110938032Speter		*p++ = '\0';
111038032Speter		if (isascii(*id) && isspace(*id))
111138032Speter			id++;
1112168515Sgshapiro		(void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, "-%s");
111364562Sgshapiro		message(cmdbuf, id);
111438032Speter	}
111538032Speter	if (id != NULL)
111638032Speter	{
111738032Speter		if (isascii(*id) && isspace(*id))
111838032Speter			id++;
1119168515Sgshapiro		(void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, " %s");
112064562Sgshapiro		message(cmdbuf, id);
112138032Speter	}
112238032Speter
112338032Speter	protocol = NULL;
112438032Speter	sendinghost = macvalue('s', e);
112590792Sgshapiro
112690792Sgshapiro	/* If quarantining by a connect/ehlo action, save between messages */
112790792Sgshapiro	if (e->e_quarmsg == NULL)
112890792Sgshapiro		smtp.sm_quarmsg = NULL;
112990792Sgshapiro	else
113090792Sgshapiro		smtp.sm_quarmsg = newstr(e->e_quarmsg);
113190792Sgshapiro
113290792Sgshapiro	/* sendinghost's storage must outlive the current envelope */
113390792Sgshapiro	if (sendinghost != NULL)
113490792Sgshapiro		sendinghost = sm_strdup_x(sendinghost);
113590792Sgshapiro	first = true;
113690792Sgshapiro	gothello = false;
113790792Sgshapiro	smtp.sm_gotmail = false;
113838032Speter	for (;;)
113938032Speter	{
114090792Sgshapiro	    SM_TRY
114190792Sgshapiro	    {
114290792Sgshapiro		QuickAbort = false;
114390792Sgshapiro		HoldErrs = false;
114490792Sgshapiro		SuprErrs = false;
114590792Sgshapiro		LogUsrErrs = false;
114690792Sgshapiro		OnlyOneError = true;
114738032Speter		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
1148168515Sgshapiro#if MILTER
1149168515Sgshapiro		milter_cmd_fail = false;
1150168515Sgshapiro#endif /* MILTER */
115138032Speter
115238032Speter		/* setup for the read */
115338032Speter		e->e_to = NULL;
115438032Speter		Errors = 0;
115564562Sgshapiro		FileName = NULL;
115690792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
115738032Speter
115838032Speter		/* read the input line */
115938032Speter		SmtpPhase = "server cmd read";
116090792Sgshapiro		sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
116138032Speter
116238032Speter		/* handle errors */
116390792Sgshapiro		if (sm_io_error(OutChannel) ||
1164168515Sgshapiro		    (p = sfgets(inp, sizeof(inp), InChannel,
116564562Sgshapiro				TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
116638032Speter		{
116764562Sgshapiro			char *d;
116864562Sgshapiro
116990792Sgshapiro			d = macvalue(macid("{daemon_name}"), e);
117064562Sgshapiro			if (d == NULL)
117164562Sgshapiro				d = "stdin";
117238032Speter			/* end of file, just die */
117338032Speter			disconnect(1, e);
117464562Sgshapiro
117590792Sgshapiro#if MILTER
117664562Sgshapiro			/* close out milter filters */
117764562Sgshapiro			milter_quit(e);
117890792Sgshapiro#endif /* MILTER */
117964562Sgshapiro
118064562Sgshapiro			message("421 4.4.1 %s Lost input channel from %s",
118138032Speter				MyHostName, CurSmtpClient);
118290792Sgshapiro			if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
118338032Speter				sm_syslog(LOG_NOTICE, e->e_id,
1184110560Sgshapiro					  "lost input channel from %s to %s after %s",
118564562Sgshapiro					  CurSmtpClient, d,
118664562Sgshapiro					  (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
118738032Speter			/*
118842575Speter			**  If have not accepted mail (DATA), do not bounce
118942575Speter			**  bad addresses back to sender.
119038032Speter			*/
119164562Sgshapiro
119238032Speter			if (bitset(EF_CLRQUEUE, e->e_flags))
119338032Speter				e->e_sendqueue = NULL;
119464562Sgshapiro			goto doquit;
119538032Speter		}
119638032Speter
1197168515Sgshapiro		/* also used by "proxy" check below */
1198168515Sgshapiro		inplen = strlen(inp);
1199168515Sgshapiro#if SASL
1200168515Sgshapiro		/*
1201168515Sgshapiro		**  SMTP AUTH requires accepting any length,
1202168515Sgshapiro		**  at least for challenge/response. However, not imposing
1203168515Sgshapiro		**  a limit is a bad idea (denial of service).
1204168515Sgshapiro		*/
1205168515Sgshapiro
1206168515Sgshapiro		if (authenticating != SASL_PROC_AUTH
1207168515Sgshapiro		    && sm_strncasecmp(inp, "AUTH ", 5) != 0
1208168515Sgshapiro		    && inplen > MAXLINE)
1209168515Sgshapiro		{
1210168515Sgshapiro			message("421 4.7.0 %s Command too long, possible attack %s",
1211168515Sgshapiro				MyHostName, CurSmtpClient);
1212168515Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
1213168515Sgshapiro				  "%s: SMTP violation, input too long: %lu",
1214168515Sgshapiro				  CurSmtpClient, (unsigned long) inplen);
1215168515Sgshapiro			goto doquit;
1216168515Sgshapiro		}
1217168515Sgshapiro#endif /* SASL */
1218168515Sgshapiro
121990792Sgshapiro		if (first)
122090792Sgshapiro		{
1221168515Sgshapiro			size_t cmdlen;
1222110560Sgshapiro			int idx;
1223110560Sgshapiro			char *http_cmd;
1224110560Sgshapiro			static char *http_cmds[] = { "GET", "POST",
1225110560Sgshapiro						     "CONNECT", "USER", NULL };
1226110560Sgshapiro
1227110560Sgshapiro			for (idx = 0; (http_cmd = http_cmds[idx]) != NULL;
1228110560Sgshapiro			     idx++)
1229110560Sgshapiro			{
1230110560Sgshapiro				cmdlen = strlen(http_cmd);
1231110560Sgshapiro				if (cmdlen < inplen &&
1232110560Sgshapiro				    sm_strncasecmp(inp, http_cmd, cmdlen) == 0 &&
1233110560Sgshapiro				    isascii(inp[cmdlen]) && isspace(inp[cmdlen]))
1234110560Sgshapiro				{
1235110560Sgshapiro					/* Open proxy, drop it */
1236110560Sgshapiro					message("421 4.7.0 %s Rejecting open proxy %s",
1237110560Sgshapiro						MyHostName, CurSmtpClient);
1238110560Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1239110560Sgshapiro						  "%s: probable open proxy: command=%.40s",
1240110560Sgshapiro						  CurSmtpClient, inp);
1241110560Sgshapiro					goto doquit;
1242110560Sgshapiro				}
1243110560Sgshapiro			}
124490792Sgshapiro			first = false;
124590792Sgshapiro		}
124690792Sgshapiro
124738032Speter		/* clean up end of line */
124890792Sgshapiro		fixcrlf(inp, true);
124938032Speter
125090792Sgshapiro#if PIPELINING
125190792Sgshapiro# if _FFR_NO_PIPE
125290792Sgshapiro		/*
125390792Sgshapiro		**  if there is more input and pipelining is disabled:
125490792Sgshapiro		**	delay ... (and maybe discard the input?)
125590792Sgshapiro		**  XXX this doesn't really work, at least in tests using
125690792Sgshapiro		**  telnet SM_IO_IS_READABLE only returns 1 if there were
125790792Sgshapiro		**  more than 2 input lines available.
125890792Sgshapiro		*/
125990792Sgshapiro
126090792Sgshapiro		if (bitset(SRV_NO_PIPE, features) &&
1261110560Sgshapiro		    sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
126290792Sgshapiro		{
126390792Sgshapiro			if (++np_log < 3)
126490792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
126590792Sgshapiro					  "unauthorized PIPELINING, sleeping");
126690792Sgshapiro			sleep(1);
126790792Sgshapiro		}
126890792Sgshapiro
126990792Sgshapiro# endif /* _FFR_NO_PIPE */
127090792Sgshapiro#endif /* PIPELINING */
127190792Sgshapiro
127290792Sgshapiro#if SASL
127364562Sgshapiro		if (authenticating == SASL_PROC_AUTH)
127464562Sgshapiro		{
127590792Sgshapiro# if 0
127664562Sgshapiro			if (*inp == '\0')
127764562Sgshapiro			{
127864562Sgshapiro				authenticating = SASL_NOT_AUTH;
127964562Sgshapiro				message("501 5.5.2 missing input");
1280120256Sgshapiro				RESET_SASLCONN;
128164562Sgshapiro				continue;
128264562Sgshapiro			}
128390792Sgshapiro# endif /* 0 */
128464562Sgshapiro			if (*inp == '*' && *(inp + 1) == '\0')
128564562Sgshapiro			{
128664562Sgshapiro				authenticating = SASL_NOT_AUTH;
128764562Sgshapiro
128864562Sgshapiro				/* rfc 2254 4. */
128964562Sgshapiro				message("501 5.0.0 AUTH aborted");
1290120256Sgshapiro				RESET_SASLCONN;
129164562Sgshapiro				continue;
129264562Sgshapiro			}
129364562Sgshapiro
129464562Sgshapiro			/* could this be shorter? XXX */
129598121Sgshapiro# if SASL >= 20000
129698121Sgshapiro			in = xalloc(strlen(inp) + 1);
129798121Sgshapiro			result = sasl_decode64(inp, strlen(inp), in,
129898121Sgshapiro					       strlen(inp), &inlen);
129998121Sgshapiro# else /* SASL >= 20000 */
130064562Sgshapiro			out = xalloc(strlen(inp));
130164562Sgshapiro			result = sasl_decode64(inp, strlen(inp), out, &outlen);
130298121Sgshapiro# endif /* SASL >= 20000 */
130364562Sgshapiro			if (result != SASL_OK)
130464562Sgshapiro			{
130564562Sgshapiro				authenticating = SASL_NOT_AUTH;
130664562Sgshapiro
130764562Sgshapiro				/* rfc 2254 4. */
130864562Sgshapiro				message("501 5.5.4 cannot decode AUTH parameter %s",
130964562Sgshapiro					inp);
131098121Sgshapiro# if SASL >= 20000
131198121Sgshapiro				sm_free(in);
131298121Sgshapiro# endif /* SASL >= 20000 */
1313120256Sgshapiro				RESET_SASLCONN;
131464562Sgshapiro				continue;
131564562Sgshapiro			}
131664562Sgshapiro
131798121Sgshapiro# if SASL >= 20000
131898121Sgshapiro			result = sasl_server_step(conn,	in, inlen,
131998121Sgshapiro						  &out, &outlen);
132098121Sgshapiro			sm_free(in);
132198121Sgshapiro# else /* SASL >= 20000 */
132264562Sgshapiro			result = sasl_server_step(conn,	out, outlen,
132364562Sgshapiro						  &out, &outlen, &errstr);
132498121Sgshapiro# endif /* SASL >= 20000 */
132564562Sgshapiro
132664562Sgshapiro			/* get an OK if we're done */
132764562Sgshapiro			if (result == SASL_OK)
132864562Sgshapiro			{
132964562Sgshapiro  authenticated:
133064562Sgshapiro				message("235 2.0.0 OK Authenticated");
133164562Sgshapiro				authenticating = SASL_IS_AUTH;
133290792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
133390792Sgshapiro					macid("{auth_type}"), auth_type);
133464562Sgshapiro
133598121Sgshapiro# if SASL >= 20000
133698121Sgshapiro				user = macvalue(macid("{auth_authen}"), e);
133798121Sgshapiro
133898121Sgshapiro				/* get security strength (features) */
133998121Sgshapiro				result = sasl_getprop(conn, SASL_SSF,
134098121Sgshapiro						      (const void **) &ssf);
134198121Sgshapiro# else /* SASL >= 20000 */
134264562Sgshapiro				result = sasl_getprop(conn, SASL_USERNAME,
134364562Sgshapiro						      (void **)&user);
134464562Sgshapiro				if (result != SASL_OK)
134564562Sgshapiro				{
134664562Sgshapiro					user = "";
134790792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
134890792Sgshapiro						  A_PERM,
134990792Sgshapiro						  macid("{auth_authen}"), NULL);
135064562Sgshapiro				}
135164562Sgshapiro				else
135264562Sgshapiro				{
135390792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
135490792Sgshapiro						  A_TEMP,
1355132943Sgshapiro						  macid("{auth_authen}"),
1356132943Sgshapiro						  xtextify(user, "<>\")"));
135764562Sgshapiro				}
135864562Sgshapiro
135990792Sgshapiro# if 0
136064562Sgshapiro				/* get realm? */
136164562Sgshapiro				sasl_getprop(conn, SASL_REALM, (void **) &data);
136290792Sgshapiro# endif /* 0 */
136364562Sgshapiro
136464562Sgshapiro				/* get security strength (features) */
136564562Sgshapiro				result = sasl_getprop(conn, SASL_SSF,
136664562Sgshapiro						      (void **) &ssf);
136798121Sgshapiro# endif /* SASL >= 20000 */
136864562Sgshapiro				if (result != SASL_OK)
136964562Sgshapiro				{
137090792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
137190792Sgshapiro						  A_PERM,
137290792Sgshapiro						  macid("{auth_ssf}"), "0");
137364562Sgshapiro					ssf = NULL;
137464562Sgshapiro				}
137564562Sgshapiro				else
137664562Sgshapiro				{
137764562Sgshapiro					char pbuf[8];
137864562Sgshapiro
1379168515Sgshapiro					(void) sm_snprintf(pbuf, sizeof(pbuf),
138090792Sgshapiro							   "%u", *ssf);
138190792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
138290792Sgshapiro						  A_TEMP,
138390792Sgshapiro						  macid("{auth_ssf}"), pbuf);
138464562Sgshapiro					if (tTd(95, 8))
138590792Sgshapiro						sm_dprintf("AUTH auth_ssf: %u\n",
138690792Sgshapiro							   *ssf);
138764562Sgshapiro				}
138890792Sgshapiro
138964562Sgshapiro				/*
139090792Sgshapiro				**  Only switch to encrypted connection
139164562Sgshapiro				**  if a security layer has been negotiated
139264562Sgshapiro				*/
139390792Sgshapiro
139464562Sgshapiro				if (ssf != NULL && *ssf > 0)
139564562Sgshapiro				{
1396159609Sgshapiro					int tmo;
1397159609Sgshapiro
139864562Sgshapiro					/*
139990792Sgshapiro					**  Convert I/O layer to use SASL.
140090792Sgshapiro					**  If the call fails, the connection
140190792Sgshapiro					**  is aborted.
140264562Sgshapiro					*/
140390792Sgshapiro
1404159609Sgshapiro					tmo = TimeOuts.to_datablock * 1000;
140590792Sgshapiro					if (sfdcsasl(&InChannel, &OutChannel,
1406159609Sgshapiro						     conn, tmo) == 0)
140764562Sgshapiro					{
140864562Sgshapiro						/* restart dialogue */
140964562Sgshapiro						n_helo = 0;
141094334Sgshapiro# if PIPELINING
141190792Sgshapiro						(void) sm_io_autoflush(InChannel,
141290792Sgshapiro								       OutChannel);
141394334Sgshapiro# endif /* PIPELINING */
141464562Sgshapiro					}
141564562Sgshapiro					else
141664562Sgshapiro						syserr("503 5.3.3 SASL TLS failed");
141764562Sgshapiro				}
141890792Sgshapiro
141990792Sgshapiro				/* NULL pointer ok since it's our function */
142090792Sgshapiro				if (LogLevel > 8)
142164562Sgshapiro					sm_syslog(LOG_INFO, NOQID,
1422110560Sgshapiro						  "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d",
142390792Sgshapiro						  CurSmtpClient,
142490792Sgshapiro						  shortenstring(user, 128),
142590792Sgshapiro						  auth_type, *ssf);
142664562Sgshapiro			}
142764562Sgshapiro			else if (result == SASL_CONTINUE)
142864562Sgshapiro			{
142964562Sgshapiro				len = ENC64LEN(outlen);
143064562Sgshapiro				out2 = xalloc(len);
143164562Sgshapiro				result = sasl_encode64(out, outlen, out2, len,
143290792Sgshapiro						       &out2len);
143364562Sgshapiro				if (result != SASL_OK)
143464562Sgshapiro				{
143564562Sgshapiro					/* correct code? XXX */
143664562Sgshapiro					/* 454 Temp. authentication failure */
143764562Sgshapiro					message("454 4.5.4 Internal error: unable to encode64");
143864562Sgshapiro					if (LogLevel > 5)
143964562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
144090792Sgshapiro							  "AUTH encode64 error [%d for \"%s\"]",
144164562Sgshapiro							  result, out);
144264562Sgshapiro					/* start over? */
144364562Sgshapiro					authenticating = SASL_NOT_AUTH;
144464562Sgshapiro				}
144564562Sgshapiro				else
144664562Sgshapiro				{
144764562Sgshapiro					message("334 %s", out2);
144864562Sgshapiro					if (tTd(95, 2))
144990792Sgshapiro						sm_dprintf("AUTH continue: msg='%s' len=%u\n",
145090792Sgshapiro							   out2, out2len);
145164562Sgshapiro				}
145298121Sgshapiro# if SASL >= 20000
145398121Sgshapiro				sm_free(out2);
145498121Sgshapiro# endif /* SASL >= 20000 */
145564562Sgshapiro			}
145664562Sgshapiro			else
145764562Sgshapiro			{
145864562Sgshapiro				/* not SASL_OK or SASL_CONT */
145998121Sgshapiro				message("535 5.7.0 authentication failed");
146064562Sgshapiro				if (LogLevel > 9)
146164562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
146290792Sgshapiro						  "AUTH failure (%s): %s (%d) %s",
146364562Sgshapiro						  auth_type,
146464562Sgshapiro						  sasl_errstring(result, NULL,
146564562Sgshapiro								 NULL),
146690792Sgshapiro						  result,
146798121Sgshapiro# if SASL >= 20000
146898121Sgshapiro						  sasl_errdetail(conn));
146998121Sgshapiro# else /* SASL >= 20000 */
147090792Sgshapiro						  errstr == NULL ? "" : errstr);
147198121Sgshapiro# endif /* SASL >= 20000 */
1472120256Sgshapiro				RESET_SASLCONN;
147364562Sgshapiro				authenticating = SASL_NOT_AUTH;
147464562Sgshapiro			}
147564562Sgshapiro		}
147664562Sgshapiro		else
147764562Sgshapiro		{
147864562Sgshapiro			/* don't want to do any of this if authenticating */
147990792Sgshapiro#endif /* SASL */
148064562Sgshapiro
148138032Speter		/* echo command to transcript */
148238032Speter		if (e->e_xfp != NULL)
148390792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
148490792Sgshapiro					     "<<< %s\n", inp);
148538032Speter
148690792Sgshapiro		if (LogLevel > 14)
148790792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
148838032Speter
148938032Speter		/* break off command */
149038032Speter		for (p = inp; isascii(*p) && isspace(*p); p++)
149138032Speter			continue;
149238032Speter		cmd = cmdbuf;
149338032Speter		while (*p != '\0' &&
149438032Speter		       !(isascii(*p) && isspace(*p)) &&
1495168515Sgshapiro		       cmd < &cmdbuf[sizeof(cmdbuf) - 2])
149638032Speter			*cmd++ = *p++;
149738032Speter		*cmd = '\0';
149838032Speter
149938032Speter		/* throw away leading whitespace */
150090792Sgshapiro		SKIP_SPACE(p);
150138032Speter
150238032Speter		/* decode command */
150364562Sgshapiro		for (c = CmdTab; c->cmd_name != NULL; c++)
150438032Speter		{
150590792Sgshapiro			if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
150638032Speter				break;
150738032Speter		}
150838032Speter
150938032Speter		/* reset errors */
151038032Speter		errno = 0;
151138032Speter
151290792Sgshapiro		/* check whether a "non-null" command has been used */
151390792Sgshapiro		switch (c->cmd_code)
151490792Sgshapiro		{
151590792Sgshapiro#if SASL
151690792Sgshapiro		  case CMDAUTH:
151790792Sgshapiro			/* avoid information leak; take first two words? */
151890792Sgshapiro			q = "AUTH";
151990792Sgshapiro			break;
152090792Sgshapiro#endif /* SASL */
152190792Sgshapiro
152290792Sgshapiro		  case CMDMAIL:
152390792Sgshapiro		  case CMDEXPN:
152490792Sgshapiro		  case CMDVRFY:
152590792Sgshapiro		  case CMDETRN:
152690792Sgshapiro			lognullconnection = false;
152790792Sgshapiro			/* FALLTHROUGH */
152890792Sgshapiro		  default:
152990792Sgshapiro			q = inp;
153090792Sgshapiro			break;
153190792Sgshapiro		}
153290792Sgshapiro
153390792Sgshapiro		if (e->e_id == NULL)
153490792Sgshapiro			sm_setproctitle(true, e, "%s: %.80s",
153590792Sgshapiro					CurSmtpClient, q);
153690792Sgshapiro		else
153790792Sgshapiro			sm_setproctitle(true, e, "%s %s: %.80s",
153890792Sgshapiro					qid_printname(e),
153990792Sgshapiro					CurSmtpClient, q);
154090792Sgshapiro
154138032Speter		/*
154238032Speter		**  Process command.
154338032Speter		**
154438032Speter		**	If we are running as a null server, return 550
154590792Sgshapiro		**	to almost everything.
154638032Speter		*/
154738032Speter
154864562Sgshapiro		if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
154938032Speter		{
155064562Sgshapiro			switch (c->cmd_code)
155138032Speter			{
155238032Speter			  case CMDQUIT:
155338032Speter			  case CMDHELO:
155438032Speter			  case CMDEHLO:
155538032Speter			  case CMDNOOP:
155664562Sgshapiro			  case CMDRSET:
1557125820Sgshapiro			  case CMDERROR:
155838032Speter				/* process normally */
155938032Speter				break;
156038032Speter
156164562Sgshapiro			  case CMDETRN:
156264562Sgshapiro				if (bitnset(D_ETRNONLY, d_flags) &&
156364562Sgshapiro				    nullserver == NULL)
156464562Sgshapiro					break;
156590792Sgshapiro				DELAY_CONN("ETRN");
156680785Sgshapiro				/* FALLTHROUGH */
156764562Sgshapiro
156838032Speter			  default:
156990792Sgshapiro#if MAXBADCOMMANDS > 0
157090792Sgshapiro				/* theoretically this could overflow */
157190792Sgshapiro				if (nullserver != NULL &&
157290792Sgshapiro				    ++n_badcmds > MAXBADCOMMANDS)
157364562Sgshapiro				{
157490792Sgshapiro					message("421 4.7.0 %s Too many bad commands; closing connection",
157590792Sgshapiro						MyHostName);
157690792Sgshapiro
157790792Sgshapiro					/* arrange to ignore send list */
157890792Sgshapiro					e->e_sendqueue = NULL;
157990792Sgshapiro					goto doquit;
158064562Sgshapiro				}
158190792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
158264562Sgshapiro				if (nullserver != NULL)
158364562Sgshapiro				{
158464562Sgshapiro					if (ISSMTPREPLY(nullserver))
158564562Sgshapiro						usrerr(nullserver);
158664562Sgshapiro					else
158790792Sgshapiro						usrerr("550 5.0.0 %s",
158890792Sgshapiro						       nullserver);
158964562Sgshapiro				}
159064562Sgshapiro				else
159164562Sgshapiro					usrerr("452 4.4.5 Insufficient disk space; try again later");
159238032Speter				continue;
159338032Speter			}
159438032Speter		}
159538032Speter
159664562Sgshapiro		switch (c->cmd_code)
159738032Speter		{
159890792Sgshapiro#if SASL
159964562Sgshapiro		  case CMDAUTH: /* sasl */
160090792Sgshapiro			DELAY_CONN("AUTH");
160190792Sgshapiro			if (!sasl_ok || n_mechs <= 0)
160264562Sgshapiro			{
160364562Sgshapiro				message("503 5.3.3 AUTH not available");
160464562Sgshapiro				break;
160564562Sgshapiro			}
160664562Sgshapiro			if (authenticating == SASL_IS_AUTH)
160764562Sgshapiro			{
160864562Sgshapiro				message("503 5.5.0 Already Authenticated");
160964562Sgshapiro				break;
161064562Sgshapiro			}
161190792Sgshapiro			if (smtp.sm_gotmail)
161264562Sgshapiro			{
161364562Sgshapiro				message("503 5.5.0 AUTH not permitted during a mail transaction");
161464562Sgshapiro				break;
161564562Sgshapiro			}
161664562Sgshapiro			if (tempfail)
161764562Sgshapiro			{
161864562Sgshapiro				if (LogLevel > 9)
161964562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1620110560Sgshapiro						  "SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)",
162164562Sgshapiro						  p, CurSmtpClient);
1622132943Sgshapiro				usrerr("454 4.3.0 Please try again later");
162364562Sgshapiro				break;
162464562Sgshapiro			}
162564562Sgshapiro
162690792Sgshapiro			ismore = false;
162764562Sgshapiro
162864562Sgshapiro			/* crude way to avoid crack attempts */
1629132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1,
1630132943Sgshapiro							true, "AUTH", e));
163164562Sgshapiro
163290792Sgshapiro			/* make sure mechanism (p) is a valid string */
163364562Sgshapiro			for (q = p; *q != '\0' && isascii(*q); q++)
163464562Sgshapiro			{
163564562Sgshapiro				if (isspace(*q))
163664562Sgshapiro				{
163764562Sgshapiro					*q = '\0';
163864562Sgshapiro					while (*++q != '\0' &&
163964562Sgshapiro					       isascii(*q) && isspace(*q))
164064562Sgshapiro						continue;
164164562Sgshapiro					*(q - 1) = '\0';
164264562Sgshapiro					ismore = (*q != '\0');
164364562Sgshapiro					break;
164464562Sgshapiro				}
164564562Sgshapiro			}
164664562Sgshapiro
164798121Sgshapiro			if (*p == '\0')
164898121Sgshapiro			{
164998121Sgshapiro				message("501 5.5.2 AUTH mechanism must be specified");
165098121Sgshapiro				break;
165198121Sgshapiro			}
165298121Sgshapiro
165364562Sgshapiro			/* check whether mechanism is available */
165464562Sgshapiro			if (iteminlist(p, mechlist, " ") == NULL)
165564562Sgshapiro			{
165698121Sgshapiro				message("504 5.3.3 AUTH mechanism %.32s not available",
165764562Sgshapiro					p);
165864562Sgshapiro				break;
165964562Sgshapiro			}
166064562Sgshapiro
166164562Sgshapiro			if (ismore)
166264562Sgshapiro			{
166364562Sgshapiro				/* could this be shorter? XXX */
166498121Sgshapiro# if SASL >= 20000
166598121Sgshapiro				in = xalloc(strlen(q) + 1);
1666102528Sgshapiro				result = sasl_decode64(q, strlen(q), in,
166798121Sgshapiro						       strlen(q), &inlen);
166898121Sgshapiro# else /* SASL >= 20000 */
166990792Sgshapiro				in = sm_rpool_malloc(e->e_rpool, strlen(q));
167064562Sgshapiro				result = sasl_decode64(q, strlen(q), in,
167190792Sgshapiro						       &inlen);
167298121Sgshapiro# endif /* SASL >= 20000 */
167364562Sgshapiro				if (result != SASL_OK)
167464562Sgshapiro				{
167564562Sgshapiro					message("501 5.5.4 cannot BASE64 decode '%s'",
167664562Sgshapiro						q);
167764562Sgshapiro					if (LogLevel > 5)
167864562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
167990792Sgshapiro							  "AUTH decode64 error [%d for \"%s\"]",
168064562Sgshapiro							  result, q);
168164562Sgshapiro					/* start over? */
168264562Sgshapiro					authenticating = SASL_NOT_AUTH;
168398121Sgshapiro# if SASL >= 20000
168498121Sgshapiro					sm_free(in);
168598121Sgshapiro# endif /* SASL >= 20000 */
168664562Sgshapiro					in = NULL;
168764562Sgshapiro					inlen = 0;
168864562Sgshapiro					break;
168964562Sgshapiro				}
169064562Sgshapiro			}
169164562Sgshapiro			else
169264562Sgshapiro			{
169364562Sgshapiro				in = NULL;
169464562Sgshapiro				inlen = 0;
169564562Sgshapiro			}
169664562Sgshapiro
169764562Sgshapiro			/* see if that auth type exists */
169898121Sgshapiro# if SASL >= 20000
1699102528Sgshapiro			result = sasl_server_start(conn, p, in, inlen,
170098121Sgshapiro						   &out, &outlen);
170198121Sgshapiro			if (in != NULL)
170298121Sgshapiro				sm_free(in);
170398121Sgshapiro# else /* SASL >= 20000 */
170464562Sgshapiro			result = sasl_server_start(conn, p, in, inlen,
170564562Sgshapiro						   &out, &outlen, &errstr);
170698121Sgshapiro# endif /* SASL >= 20000 */
170764562Sgshapiro
170864562Sgshapiro			if (result != SASL_OK && result != SASL_CONTINUE)
170964562Sgshapiro			{
171098121Sgshapiro				message("535 5.7.0 authentication failed");
171164562Sgshapiro				if (LogLevel > 9)
171264562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
171390792Sgshapiro						  "AUTH failure (%s): %s (%d) %s",
171464562Sgshapiro						  p,
171564562Sgshapiro						  sasl_errstring(result, NULL,
171664562Sgshapiro								 NULL),
171790792Sgshapiro						  result,
171898121Sgshapiro# if SASL >= 20000
171998121Sgshapiro						  sasl_errdetail(conn));
172098121Sgshapiro# else /* SASL >= 20000 */
172190792Sgshapiro						  errstr);
172298121Sgshapiro# endif /* SASL >= 20000 */
1723120256Sgshapiro				RESET_SASLCONN;
172464562Sgshapiro				break;
172564562Sgshapiro			}
172664562Sgshapiro			auth_type = newstr(p);
172764562Sgshapiro
172864562Sgshapiro			if (result == SASL_OK)
172964562Sgshapiro			{
173064562Sgshapiro				/* ugly, but same code */
173164562Sgshapiro				goto authenticated;
173264562Sgshapiro				/* authenticated by the initial response */
173364562Sgshapiro			}
173464562Sgshapiro
173564562Sgshapiro			/* len is at least 2 */
173664562Sgshapiro			len = ENC64LEN(outlen);
173764562Sgshapiro			out2 = xalloc(len);
173864562Sgshapiro			result = sasl_encode64(out, outlen, out2, len,
173990792Sgshapiro					       &out2len);
174064562Sgshapiro
174164562Sgshapiro			if (result != SASL_OK)
174264562Sgshapiro			{
174364562Sgshapiro				message("454 4.5.4 Temporary authentication failure");
174464562Sgshapiro				if (LogLevel > 5)
174564562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
174690792Sgshapiro						  "AUTH encode64 error [%d for \"%s\"]",
174764562Sgshapiro						  result, out);
174864562Sgshapiro
174964562Sgshapiro				/* start over? */
175064562Sgshapiro				authenticating = SASL_NOT_AUTH;
1751120256Sgshapiro				RESET_SASLCONN;
175264562Sgshapiro			}
175364562Sgshapiro			else
175464562Sgshapiro			{
175564562Sgshapiro				message("334 %s", out2);
175664562Sgshapiro				authenticating = SASL_PROC_AUTH;
175764562Sgshapiro			}
175898121Sgshapiro# if SASL >= 20000
175998121Sgshapiro			sm_free(out2);
176098121Sgshapiro# endif /* SASL >= 20000 */
176164562Sgshapiro			break;
176290792Sgshapiro#endif /* SASL */
176364562Sgshapiro
176490792Sgshapiro#if STARTTLS
176564562Sgshapiro		  case CMDSTLS: /* starttls */
176690792Sgshapiro			DELAY_CONN("STARTTLS");
176764562Sgshapiro			if (*p != '\0')
176864562Sgshapiro			{
176964562Sgshapiro				message("501 5.5.2 Syntax error (no parameters allowed)");
177064562Sgshapiro				break;
177164562Sgshapiro			}
177290792Sgshapiro			if (!bitset(SRV_OFFER_TLS, features))
177364562Sgshapiro			{
177464562Sgshapiro				message("503 5.5.0 TLS not available");
177564562Sgshapiro				break;
177664562Sgshapiro			}
177777349Sgshapiro			if (!tls_ok_srv)
177864562Sgshapiro			{
177964562Sgshapiro				message("454 4.3.3 TLS not available after start");
178064562Sgshapiro				break;
178164562Sgshapiro			}
178290792Sgshapiro			if (smtp.sm_gotmail)
178364562Sgshapiro			{
178464562Sgshapiro				message("503 5.5.0 TLS not permitted during a mail transaction");
178564562Sgshapiro				break;
178664562Sgshapiro			}
178764562Sgshapiro			if (tempfail)
178864562Sgshapiro			{
178964562Sgshapiro				if (LogLevel > 9)
179064562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1791110560Sgshapiro						  "SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)",
179264562Sgshapiro						  p, CurSmtpClient);
1793132943Sgshapiro				usrerr("454 4.7.0 Please try again later");
179464562Sgshapiro				break;
179564562Sgshapiro			}
179690792Sgshapiro  starttls:
179764562Sgshapiro# if TLS_NO_RSA
179864562Sgshapiro			/*
179964562Sgshapiro			**  XXX do we need a temp key ?
180064562Sgshapiro			*/
180164562Sgshapiro# else /* TLS_NO_RSA */
180264562Sgshapiro# endif /* TLS_NO_RSA */
180390792Sgshapiro
180490792Sgshapiro# if TLS_VRFY_PER_CTX
180590792Sgshapiro			/*
180690792Sgshapiro			**  Note: this sets the verification globally
180790792Sgshapiro			**  (per SSL_CTX)
180890792Sgshapiro			**  it's ok since it applies only to one transaction
180990792Sgshapiro			*/
181090792Sgshapiro
181190792Sgshapiro			TLS_VERIFY_CLIENT();
181290792Sgshapiro# endif /* TLS_VRFY_PER_CTX */
181390792Sgshapiro
181464562Sgshapiro			if (srv_ssl != NULL)
181564562Sgshapiro				SSL_clear(srv_ssl);
181664562Sgshapiro			else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
181764562Sgshapiro			{
181864562Sgshapiro				message("454 4.3.3 TLS not available: error generating SSL handle");
1819120256Sgshapiro				if (LogLevel > 8)
1820120256Sgshapiro					tlslogerr("server");
182190792Sgshapiro				goto tls_done;
182264562Sgshapiro			}
182390792Sgshapiro
182490792Sgshapiro# if !TLS_VRFY_PER_CTX
182590792Sgshapiro			/*
182690792Sgshapiro			**  this could be used if it were possible to set
182790792Sgshapiro			**  verification per SSL (connection)
182890792Sgshapiro			**  not just per SSL_CTX (global)
182990792Sgshapiro			*/
183090792Sgshapiro
183190792Sgshapiro			TLS_VERIFY_CLIENT();
183290792Sgshapiro# endif /* !TLS_VRFY_PER_CTX */
183390792Sgshapiro
183490792Sgshapiro			rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
183590792Sgshapiro			wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
183690792Sgshapiro
183766494Sgshapiro			if (rfd < 0 || wfd < 0 ||
183866494Sgshapiro			    SSL_set_rfd(srv_ssl, rfd) <= 0 ||
183966494Sgshapiro			    SSL_set_wfd(srv_ssl, wfd) <= 0)
184064562Sgshapiro			{
184164562Sgshapiro				message("454 4.3.3 TLS not available: error set fd");
184264562Sgshapiro				SSL_free(srv_ssl);
184364562Sgshapiro				srv_ssl = NULL;
184490792Sgshapiro				goto tls_done;
184564562Sgshapiro			}
184690792Sgshapiro			if (!smtps)
184790792Sgshapiro				message("220 2.0.0 Ready to start TLS");
184890792Sgshapiro# if PIPELINING
184990792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
185090792Sgshapiro# endif /* PIPELINING */
185190792Sgshapiro
185264562Sgshapiro			SSL_set_accept_state(srv_ssl);
185364562Sgshapiro
185464562Sgshapiro#  define SSL_ACC(s)	SSL_accept(s)
185590792Sgshapiro
185690792Sgshapiro			tlsstart = curtime();
185790792Sgshapiro  ssl_retry:
185864562Sgshapiro			if ((r = SSL_ACC(srv_ssl)) <= 0)
185964562Sgshapiro			{
1860157001Sgshapiro				int i, ssl_err;
186164562Sgshapiro
1862157001Sgshapiro				ssl_err = SSL_get_error(srv_ssl, r);
1863157001Sgshapiro				i = tls_retry(srv_ssl, rfd, wfd, tlsstart,
1864157001Sgshapiro						TimeOuts.to_starttls, ssl_err,
1865157001Sgshapiro						"server");
1866157001Sgshapiro				if (i > 0)
1867157001Sgshapiro					goto ssl_retry;
186890792Sgshapiro
186964562Sgshapiro				if (LogLevel > 5)
187064562Sgshapiro				{
187190792Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
1872157001Sgshapiro						  "STARTTLS=server, error: accept failed=%d, SSL_error=%d, errno=%d, retry=%d",
1873157001Sgshapiro						  r, ssl_err, errno, i);
187490792Sgshapiro					if (LogLevel > 8)
187590792Sgshapiro						tlslogerr("server");
187664562Sgshapiro				}
187790792Sgshapiro				tls_ok_srv = false;
187864562Sgshapiro				SSL_free(srv_ssl);
187964562Sgshapiro				srv_ssl = NULL;
188064562Sgshapiro
188164562Sgshapiro				/*
188264562Sgshapiro				**  according to the next draft of
188364562Sgshapiro				**  RFC 2487 the connection should be dropped
188464562Sgshapiro				*/
188564562Sgshapiro
188664562Sgshapiro				/* arrange to ignore any current send list */
188764562Sgshapiro				e->e_sendqueue = NULL;
188864562Sgshapiro				goto doquit;
188964562Sgshapiro			}
189064562Sgshapiro
189164562Sgshapiro			/* ignore return code for now, it's in {verify} */
189290792Sgshapiro			(void) tls_get_info(srv_ssl, true,
189390792Sgshapiro					    CurSmtpClient,
189490792Sgshapiro					    &BlankEnvelope.e_macro,
189590792Sgshapiro					    bitset(SRV_VRFY_CLT, features));
189664562Sgshapiro
189764562Sgshapiro			/*
189864562Sgshapiro			**  call Stls_client to find out whether
189964562Sgshapiro			**  to accept the connection from the client
190064562Sgshapiro			*/
190164562Sgshapiro
190264562Sgshapiro			saveQuickAbort = QuickAbort;
190364562Sgshapiro			saveSuprErrs = SuprErrs;
190490792Sgshapiro			SuprErrs = true;
190590792Sgshapiro			QuickAbort = false;
190664562Sgshapiro			if (rscheck("tls_client",
190790792Sgshapiro				     macvalue(macid("{verify}"), e),
1908102528Sgshapiro				     "STARTTLS", e,
1909102528Sgshapiro				     RSF_RMCOMM|RSF_COUNT,
1910168515Sgshapiro				     5, NULL, NOQID, NULL) != EX_OK ||
191190792Sgshapiro			    Errors > 0)
191264562Sgshapiro			{
191364562Sgshapiro				extern char MsgBuf[];
191464562Sgshapiro
191564562Sgshapiro				if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
191664562Sgshapiro					nullserver = newstr(MsgBuf);
191764562Sgshapiro				else
191864562Sgshapiro					nullserver = "503 5.7.0 Authentication required.";
191964562Sgshapiro			}
192064562Sgshapiro			QuickAbort = saveQuickAbort;
192164562Sgshapiro			SuprErrs = saveSuprErrs;
192264562Sgshapiro
192390792Sgshapiro			tls_ok_srv = false;	/* don't offer STARTTLS again */
192464562Sgshapiro			n_helo = 0;
192590792Sgshapiro# if SASL
192664562Sgshapiro			if (sasl_ok)
192764562Sgshapiro			{
1928132943Sgshapiro				int cipher_bits;
1929132943Sgshapiro				bool verified;
1930132943Sgshapiro				char *s, *v, *c;
193164562Sgshapiro
193290792Sgshapiro				s = macvalue(macid("{cipher_bits}"), e);
1933132943Sgshapiro				v = macvalue(macid("{verify}"), e);
1934132943Sgshapiro				c = macvalue(macid("{cert_subject}"), e);
1935132943Sgshapiro				verified = (v != NULL && strcmp(v, "OK") == 0);
1936132943Sgshapiro				if (s != NULL && (cipher_bits = atoi(s)) > 0)
1937132943Sgshapiro				{
193898121Sgshapiro#  if SASL >= 20000
1939132943Sgshapiro					ext_ssf = cipher_bits;
1940132943Sgshapiro					auth_id = verified ? c : NULL;
1941132943Sgshapiro					sasl_ok = ((sasl_setprop(conn,
1942132943Sgshapiro							SASL_SSF_EXTERNAL,
1943132943Sgshapiro							&ext_ssf) == SASL_OK) &&
1944132943Sgshapiro						   (sasl_setprop(conn,
1945132943Sgshapiro							SASL_AUTH_EXTERNAL,
1946132943Sgshapiro							auth_id) == SASL_OK));
194798121Sgshapiro#  else /* SASL >= 20000 */
1948132943Sgshapiro					ext_ssf.ssf = cipher_bits;
1949132943Sgshapiro					ext_ssf.auth_id = verified ? c : NULL;
1950132943Sgshapiro					sasl_ok = sasl_setprop(conn,
1951132943Sgshapiro							SASL_SSF_EXTERNAL,
1952132943Sgshapiro							&ext_ssf) == SASL_OK;
195398121Sgshapiro#  endif /* SASL >= 20000 */
195464562Sgshapiro					mechlist = NULL;
195564562Sgshapiro					if (sasl_ok)
195664562Sgshapiro						n_mechs = saslmechs(conn,
195764562Sgshapiro								    &mechlist);
195864562Sgshapiro				}
195964562Sgshapiro			}
196090792Sgshapiro# endif /* SASL */
196164562Sgshapiro
196264562Sgshapiro			/* switch to secure connection */
196390792Sgshapiro			if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
196490792Sgshapiro			{
196590792Sgshapiro				tls_active = true;
196690792Sgshapiro# if PIPELINING
196790792Sgshapiro				(void) sm_io_autoflush(InChannel, OutChannel);
196890792Sgshapiro# endif /* PIPELINING */
196990792Sgshapiro			}
197064562Sgshapiro			else
197164562Sgshapiro			{
197264562Sgshapiro				/*
197364562Sgshapiro				**  XXX this is an internal error
197464562Sgshapiro				**  how to deal with it?
197564562Sgshapiro				**  we can't generate an error message
197664562Sgshapiro				**  since the other side switched to an
197764562Sgshapiro				**  encrypted layer, but we could not...
197864562Sgshapiro				**  just "hang up"?
197964562Sgshapiro				*/
198090792Sgshapiro
198164562Sgshapiro				nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
198290792Sgshapiro				syserr("STARTTLS: can't switch to encrypted layer");
198364562Sgshapiro			}
198490792Sgshapiro		  tls_done:
198590792Sgshapiro			if (smtps)
198690792Sgshapiro			{
198790792Sgshapiro				if (tls_active)
198890792Sgshapiro					goto greeting;
198990792Sgshapiro				else
199090792Sgshapiro					goto doquit;
199190792Sgshapiro			}
199264562Sgshapiro			break;
199390792Sgshapiro#endif /* STARTTLS */
199464562Sgshapiro
199538032Speter		  case CMDHELO:		/* hello -- introduce yourself */
199638032Speter		  case CMDEHLO:		/* extended hello */
199790792Sgshapiro			DELAY_CONN("EHLO");
199864562Sgshapiro			if (c->cmd_code == CMDEHLO)
199938032Speter			{
200038032Speter				protocol = "ESMTP";
200138032Speter				SmtpPhase = "server EHLO";
200238032Speter			}
200338032Speter			else
200438032Speter			{
200538032Speter				protocol = "SMTP";
200638032Speter				SmtpPhase = "server HELO";
200738032Speter			}
200838032Speter
200938032Speter			/* avoid denial-of-service */
2010132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
2011132943Sgshapiro							true, "HELO/EHLO", e));
201238032Speter
201390792Sgshapiro#if 0
201490792Sgshapiro			/* RFC2821 4.1.4 allows duplicate HELO/EHLO */
201538032Speter			/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
201638032Speter			if (gothello)
201738032Speter			{
201838032Speter				usrerr("503 %s Duplicate HELO/EHLO",
201973188Sgshapiro				       MyHostName);
202038032Speter				break;
202138032Speter			}
202290792Sgshapiro#endif /* 0 */
202338032Speter
202438032Speter			/* check for valid domain name (re 1123 5.2.5) */
202538032Speter			if (*p == '\0' && !AllowBogusHELO)
202638032Speter			{
202738032Speter				usrerr("501 %s requires domain address",
202838032Speter					cmdbuf);
202938032Speter				break;
203038032Speter			}
203138032Speter
203238032Speter			/* check for long domain name (hides Received: info) */
203338032Speter			if (strlen(p) > MAXNAME)
203438032Speter			{
203538032Speter				usrerr("501 Invalid domain name");
203664562Sgshapiro				if (LogLevel > 9)
203764562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
2038110560Sgshapiro						  "invalid domain name (too long) from %s",
203964562Sgshapiro						  CurSmtpClient);
204038032Speter				break;
204138032Speter			}
204238032Speter
204398121Sgshapiro			ok = true;
204438032Speter			for (q = p; *q != '\0'; q++)
204538032Speter			{
204638032Speter				if (!isascii(*q))
204738032Speter					break;
204838032Speter				if (isalnum(*q))
204938032Speter					continue;
205038032Speter				if (isspace(*q))
205138032Speter				{
205238032Speter					*q = '\0';
205398121Sgshapiro
205498121Sgshapiro					/* only complain if strict check */
205598121Sgshapiro					ok = AllowBogusHELO;
2056141858Sgshapiro
2057141858Sgshapiro					/* allow trailing whitespace */
2058141858Sgshapiro					while (!ok && *++q != '\0' &&
2059141858Sgshapiro					       isspace(*q))
2060141858Sgshapiro						;
2061141858Sgshapiro					if (*q == '\0')
2062141858Sgshapiro						ok = true;
206338032Speter					break;
206438032Speter				}
2065120256Sgshapiro				if (strchr("[].-_#:", *q) == NULL)
206638032Speter					break;
206738032Speter			}
206864562Sgshapiro
206998121Sgshapiro			if (*q == '\0' && ok)
207038032Speter			{
207138032Speter				q = "pleased to meet you";
207290792Sgshapiro				sendinghost = sm_strdup_x(p);
207338032Speter			}
207438032Speter			else if (!AllowBogusHELO)
207538032Speter			{
207638032Speter				usrerr("501 Invalid domain name");
207764562Sgshapiro				if (LogLevel > 9)
207864562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
2079110560Sgshapiro						  "invalid domain name (%s) from %.100s",
208064562Sgshapiro						  p, CurSmtpClient);
208138032Speter				break;
208238032Speter			}
208338032Speter			else
208438032Speter			{
208538032Speter				q = "accepting invalid domain name";
208638032Speter			}
208738032Speter
2088157001Sgshapiro			if (gothello || smtp.sm_gotmail)
208990792Sgshapiro				CLEAR_STATE(cmdbuf);
209090792Sgshapiro
209190792Sgshapiro#if MILTER
209290792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
209390792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
209464562Sgshapiro			{
209564562Sgshapiro				char state;
209664562Sgshapiro				char *response;
209764562Sgshapiro
209864562Sgshapiro				response = milter_helo(p, e, &state);
209964562Sgshapiro				switch (state)
210064562Sgshapiro				{
210164562Sgshapiro				  case SMFIR_REJECT:
210290792Sgshapiro					if (MilterLogLevel > 3)
210390792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
210490792Sgshapiro							  "Milter: helo=%s, reject=Command rejected",
210590792Sgshapiro							  p);
210664562Sgshapiro					nullserver = "Command rejected";
210790792Sgshapiro					smtp.sm_milterize = false;
210864562Sgshapiro					break;
210964562Sgshapiro
211064562Sgshapiro				  case SMFIR_TEMPFAIL:
211190792Sgshapiro					if (MilterLogLevel > 3)
211290792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
211390792Sgshapiro							  "Milter: helo=%s, reject=%s",
211490792Sgshapiro							  p, MSG_TEMPFAIL);
211590792Sgshapiro					tempfail = true;
211690792Sgshapiro					smtp.sm_milterize = false;
211764562Sgshapiro					break;
2118157001Sgshapiro
2119168515Sgshapiro				  case SMFIR_REPLYCODE:
2120157001Sgshapiro					if (MilterLogLevel > 3)
2121157001Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
2122168515Sgshapiro							  "Milter: helo=%s, reject=%s",
2123168515Sgshapiro							  p, response);
2124168515Sgshapiro					if (strncmp(response, "421 ", 4) != 0
2125168515Sgshapiro					    && strncmp(response, "421-", 4) != 0)
2126168515Sgshapiro					{
2127168515Sgshapiro						nullserver = newstr(response);
2128168515Sgshapiro						smtp.sm_milterize = false;
2129168515Sgshapiro						break;
2130168515Sgshapiro					}
2131168515Sgshapiro					/* FALLTHROUGH */
2132168515Sgshapiro
2133168515Sgshapiro				  case SMFIR_SHUTDOWN:
2134168515Sgshapiro					if (MilterLogLevel > 3 &&
2135168515Sgshapiro					    response == NULL)
2136168515Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
2137159609Sgshapiro							  "Milter: helo=%s, reject=421 4.7.0 %s closing connection",
2138157001Sgshapiro							  p, MyHostName);
2139157001Sgshapiro					tempfail = true;
2140157001Sgshapiro					smtp.sm_milterize = false;
2141168515Sgshapiro					if (response != NULL)
2142168515Sgshapiro						usrerr(response);
2143168515Sgshapiro					else
2144168515Sgshapiro						message("421 4.7.0 %s closing connection",
2145168515Sgshapiro							MyHostName);
2146157001Sgshapiro					/* arrange to ignore send list */
2147157001Sgshapiro					e->e_sendqueue = NULL;
2148168515Sgshapiro					lognullconnection = false;
2149157001Sgshapiro					goto doquit;
215064562Sgshapiro				}
215190792Sgshapiro				if (response != NULL)
215290792Sgshapiro					sm_free(response);
215390792Sgshapiro
215490792Sgshapiro				/*
215590792Sgshapiro				**  If quarantining by a connect/ehlo action,
215690792Sgshapiro				**  save between messages
215790792Sgshapiro				*/
215890792Sgshapiro
215990792Sgshapiro				if (smtp.sm_quarmsg == NULL &&
216090792Sgshapiro				    e->e_quarmsg != NULL)
216190792Sgshapiro					smtp.sm_quarmsg = newstr(e->e_quarmsg);
216264562Sgshapiro			}
216390792Sgshapiro#endif /* MILTER */
216490792Sgshapiro			gothello = true;
216564562Sgshapiro
216638032Speter			/* print HELO response message */
216764562Sgshapiro			if (c->cmd_code != CMDEHLO)
216838032Speter			{
216938032Speter				message("250 %s Hello %s, %s",
217038032Speter					MyHostName, CurSmtpClient, q);
217138032Speter				break;
217238032Speter			}
217338032Speter
217438032Speter			message("250-%s Hello %s, %s",
217538032Speter				MyHostName, CurSmtpClient, q);
217638032Speter
217764562Sgshapiro			/* offer ENHSC even for nullserver */
217864562Sgshapiro			if (nullserver != NULL)
217964562Sgshapiro			{
218064562Sgshapiro				message("250 ENHANCEDSTATUSCODES");
218164562Sgshapiro				break;
218264562Sgshapiro			}
218364562Sgshapiro
218466494Sgshapiro			/*
218566494Sgshapiro			**  print EHLO features list
218666494Sgshapiro			**
218766494Sgshapiro			**  Note: If you change this list,
218890792Sgshapiro			**	  remember to update 'helpfile'
218966494Sgshapiro			*/
219066494Sgshapiro
219164562Sgshapiro			message("250-ENHANCEDSTATUSCODES");
219290792Sgshapiro#if PIPELINING
219390792Sgshapiro			if (bitset(SRV_OFFER_PIPE, features))
219490792Sgshapiro				message("250-PIPELINING");
219590792Sgshapiro#endif /* PIPELINING */
219690792Sgshapiro			if (bitset(SRV_OFFER_EXPN, features))
219738032Speter			{
219838032Speter				message("250-EXPN");
219990792Sgshapiro				if (bitset(SRV_OFFER_VERB, features))
220038032Speter					message("250-VERB");
220138032Speter			}
220290792Sgshapiro#if MIME8TO7
220338032Speter			message("250-8BITMIME");
220490792Sgshapiro#endif /* MIME8TO7 */
220538032Speter			if (MaxMessageSize > 0)
220638032Speter				message("250-SIZE %ld", MaxMessageSize);
220738032Speter			else
220838032Speter				message("250-SIZE");
220990792Sgshapiro#if DSN
221090792Sgshapiro			if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
221138032Speter				message("250-DSN");
221290792Sgshapiro#endif /* DSN */
221390792Sgshapiro			if (bitset(SRV_OFFER_ETRN, features))
221438032Speter				message("250-ETRN");
221590792Sgshapiro#if SASL
221664562Sgshapiro			if (sasl_ok && mechlist != NULL && *mechlist != '\0')
221764562Sgshapiro				message("250-AUTH %s", mechlist);
221890792Sgshapiro#endif /* SASL */
221990792Sgshapiro#if STARTTLS
222098841Sgshapiro			if (tls_ok_srv &&
222198841Sgshapiro			    bitset(SRV_OFFER_TLS, features))
222264562Sgshapiro				message("250-STARTTLS");
222390792Sgshapiro#endif /* STARTTLS */
222490792Sgshapiro			if (DeliverByMin > 0)
222590792Sgshapiro				message("250-DELIVERBY %ld",
222690792Sgshapiro					(long) DeliverByMin);
222790792Sgshapiro			else if (DeliverByMin == 0)
222890792Sgshapiro				message("250-DELIVERBY");
222990792Sgshapiro
223090792Sgshapiro			/* < 0: no deliver-by */
223190792Sgshapiro
223238032Speter			message("250 HELP");
223338032Speter			break;
223438032Speter
223538032Speter		  case CMDMAIL:		/* mail -- designate sender */
223638032Speter			SmtpPhase = "server MAIL";
223790792Sgshapiro			DELAY_CONN("MAIL");
223838032Speter
223938032Speter			/* check for validity of this command */
224038032Speter			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
224138032Speter			{
224264562Sgshapiro				usrerr("503 5.0.0 Polite people say HELO first");
224338032Speter				break;
224438032Speter			}
224590792Sgshapiro			if (smtp.sm_gotmail)
224638032Speter			{
224764562Sgshapiro				usrerr("503 5.5.0 Sender already specified");
224838032Speter				break;
224938032Speter			}
225090792Sgshapiro#if SASL
225190792Sgshapiro			if (bitset(SRV_REQ_AUTH, features) &&
225264562Sgshapiro			    authenticating != SASL_IS_AUTH)
225364562Sgshapiro			{
225464562Sgshapiro				usrerr("530 5.7.0 Authentication required");
225564562Sgshapiro				break;
225664562Sgshapiro			}
225790792Sgshapiro#endif /* SASL */
225838032Speter
225964562Sgshapiro			p = skipword(p, "from");
226064562Sgshapiro			if (p == NULL)
226164562Sgshapiro				break;
226264562Sgshapiro			if (tempfail)
226364562Sgshapiro			{
226464562Sgshapiro				if (LogLevel > 9)
226564562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2266110560Sgshapiro						  "SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)",
226764562Sgshapiro						  p, CurSmtpClient);
226890792Sgshapiro				usrerr(MSG_TEMPFAIL);
226964562Sgshapiro				break;
227064562Sgshapiro			}
227164562Sgshapiro
227238032Speter			/* make sure we know who the sending host is */
227338032Speter			if (sendinghost == NULL)
227438032Speter				sendinghost = peerhostname;
227538032Speter
227638032Speter
227790792Sgshapiro#if SM_HEAP_CHECK
227890792Sgshapiro			if (sm_debug_active(&DebugLeakSmtp, 1))
227990792Sgshapiro			{
228090792Sgshapiro				sm_heap_newgroup();
228190792Sgshapiro				sm_dprintf("smtp() heap group #%d\n",
228290792Sgshapiro					sm_heap_group());
228390792Sgshapiro			}
228490792Sgshapiro#endif /* SM_HEAP_CHECK */
228564562Sgshapiro
228638032Speter			if (Errors > 0)
228790792Sgshapiro				goto undo_no_pm;
228838032Speter			if (!gothello)
228938032Speter			{
229090792Sgshapiro				auth_warning(e, "%s didn't use HELO protocol",
229190792Sgshapiro					     CurSmtpClient);
229238032Speter			}
229390792Sgshapiro#ifdef PICKY_HELO_CHECK
229490792Sgshapiro			if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
229590792Sgshapiro			    (sm_strcasecmp(peerhostname, "localhost") != 0 ||
229690792Sgshapiro			     sm_strcasecmp(sendinghost, MyHostName) != 0))
229738032Speter			{
229838032Speter				auth_warning(e, "Host %s claimed to be %s",
229990792Sgshapiro					     CurSmtpClient, sendinghost);
230038032Speter			}
230190792Sgshapiro#endif /* PICKY_HELO_CHECK */
230238032Speter
230338032Speter			if (protocol == NULL)
230438032Speter				protocol = "SMTP";
230590792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'r', protocol);
230690792Sgshapiro			macdefine(&e->e_macro, A_PERM, 's', sendinghost);
230764562Sgshapiro
230838032Speter			if (Errors > 0)
230990792Sgshapiro				goto undo_no_pm;
231090792Sgshapiro			smtp.sm_nrcpts = 0;
231190792Sgshapiro			n_badrcpts = 0;
231290792Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
231390792Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
2314132943Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"),
2315132943Sgshapiro				"0");
231664562Sgshapiro			e->e_flags |= EF_CLRQUEUE;
231790792Sgshapiro			sm_setproctitle(true, e, "%s %s: %.80s",
231864562Sgshapiro					qid_printname(e),
231964562Sgshapiro					CurSmtpClient, inp);
232038032Speter
232190792Sgshapiro			/* do the processing */
232290792Sgshapiro		    SM_TRY
232390792Sgshapiro		    {
232494334Sgshapiro			extern char *FullName;
232594334Sgshapiro
232690792Sgshapiro			QuickAbort = true;
232794334Sgshapiro			SM_FREE_CLR(FullName);
232864562Sgshapiro
232938032Speter			/* must parse sender first */
233038032Speter			delimptr = NULL;
233190792Sgshapiro			setsender(p, e, &delimptr, ' ', false);
233238032Speter			if (delimptr != NULL && *delimptr != '\0')
233338032Speter				*delimptr++ = '\0';
233438032Speter			if (Errors > 0)
233590792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
233638032Speter
233764562Sgshapiro			/* Successfully set e_from, allow logging */
233864562Sgshapiro			e->e_flags |= EF_LOGSENDER;
233938032Speter
234064562Sgshapiro			/* put resulting triple from parseaddr() into macros */
234164562Sgshapiro			if (e->e_from.q_mailer != NULL)
234290792Sgshapiro				 macdefine(&e->e_macro, A_PERM,
234390792Sgshapiro					macid("{mail_mailer}"),
234490792Sgshapiro					e->e_from.q_mailer->m_name);
234564562Sgshapiro			else
234690792Sgshapiro				 macdefine(&e->e_macro, A_PERM,
234790792Sgshapiro					macid("{mail_mailer}"), NULL);
234864562Sgshapiro			if (e->e_from.q_host != NULL)
234990792Sgshapiro				macdefine(&e->e_macro, A_PERM,
235090792Sgshapiro					macid("{mail_host}"),
235190792Sgshapiro					e->e_from.q_host);
235264562Sgshapiro			else
235390792Sgshapiro				macdefine(&e->e_macro, A_PERM,
235490792Sgshapiro					macid("{mail_host}"), "localhost");
235564562Sgshapiro			if (e->e_from.q_user != NULL)
235690792Sgshapiro				macdefine(&e->e_macro, A_PERM,
235790792Sgshapiro					macid("{mail_addr}"),
235890792Sgshapiro					e->e_from.q_user);
235964562Sgshapiro			else
236090792Sgshapiro				macdefine(&e->e_macro, A_PERM,
236190792Sgshapiro					macid("{mail_addr}"), NULL);
236264562Sgshapiro			if (Errors > 0)
236390792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
236464562Sgshapiro
236538032Speter			/* check for possible spoofing */
236638032Speter			if (RealUid != 0 && OpMode == MD_SMTP &&
236738032Speter			    !wordinclass(RealUserName, 't') &&
236864562Sgshapiro			    (!bitnset(M_LOCALMAILER,
236964562Sgshapiro				      e->e_from.q_mailer->m_flags) ||
237064562Sgshapiro			     strcmp(e->e_from.q_user, RealUserName) != 0))
237138032Speter			{
237238032Speter				auth_warning(e, "%s owned process doing -bs",
237338032Speter					RealUserName);
237438032Speter			}
237538032Speter
2376120256Sgshapiro			/* reset to default value */
2377168515Sgshapiro			SevenBitInput = SevenBitInput_Saved;
2378120256Sgshapiro
237938032Speter			/* now parse ESMTP arguments */
238038032Speter			e->e_msgsize = 0;
238164562Sgshapiro			addr = p;
2382168515Sgshapiro			parse_esmtp_args(e, NULL, p, delimptr, "MAIL", args,
2383168515Sgshapiro					mail_esmtp_args);
238438032Speter			if (Errors > 0)
238590792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
238638032Speter
238790792Sgshapiro#if SASL
238890792Sgshapiro# if _FFR_AUTH_PASSING
238990792Sgshapiro			/* set the default AUTH= if the sender didn't */
239090792Sgshapiro			if (e->e_auth_param == NULL)
239190792Sgshapiro			{
239290792Sgshapiro				/* XXX only do this for an MSA? */
239390792Sgshapiro				e->e_auth_param = macvalue(macid("{auth_authen}"),
239490792Sgshapiro							   e);
239590792Sgshapiro				if (e->e_auth_param == NULL)
239690792Sgshapiro					e->e_auth_param = "<>";
239790792Sgshapiro
239890792Sgshapiro				/*
239990792Sgshapiro				**  XXX should we invoke Strust_auth now?
240090792Sgshapiro				**  authorizing as the client that just
240190792Sgshapiro				**  authenticated, so we'll trust implicitly
240290792Sgshapiro				*/
240390792Sgshapiro			}
240490792Sgshapiro# endif /* _FFR_AUTH_PASSING */
240590792Sgshapiro#endif /* SASL */
240690792Sgshapiro
240764562Sgshapiro			/* do config file checking of the sender */
240890792Sgshapiro			macdefine(&e->e_macro, A_PERM,
240990792Sgshapiro				macid("{addr_type}"), "e s");
241090792Sgshapiro#if _FFR_MAIL_MACRO
241190792Sgshapiro			/* make the "real" sender address available */
241290792Sgshapiro			macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
241390792Sgshapiro				  e->e_from.q_paddr);
241490792Sgshapiro#endif /* _FFR_MAIL_MACRO */
241564562Sgshapiro			if (rscheck("check_mail", addr,
2416102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
2417168515Sgshapiro				    NULL, e->e_id, NULL) != EX_OK ||
241864562Sgshapiro			    Errors > 0)
241990792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
242090792Sgshapiro			macdefine(&e->e_macro, A_PERM,
242190792Sgshapiro				  macid("{addr_type}"), NULL);
242264562Sgshapiro
242366494Sgshapiro			if (MaxMessageSize > 0 &&
242477349Sgshapiro			    (e->e_msgsize > MaxMessageSize ||
242577349Sgshapiro			     e->e_msgsize < 0))
242638032Speter			{
242764562Sgshapiro				usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
242838032Speter					MaxMessageSize);
242990792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
243038032Speter			}
243164562Sgshapiro
243290792Sgshapiro			/*
243390792Sgshapiro			**  XXX always check whether there is at least one fs
243490792Sgshapiro			**  with enough space?
243590792Sgshapiro			**  However, this may not help much: the queue group
243690792Sgshapiro			**  selection may later on select a FS that hasn't
243790792Sgshapiro			**  enough space.
243890792Sgshapiro			*/
243990792Sgshapiro
244090792Sgshapiro			if ((NumFileSys == 1 || NumQueue == 1) &&
244190792Sgshapiro			    !enoughdiskspace(e->e_msgsize, e)
244290792Sgshapiro#if _FFR_ANY_FREE_FS
244390792Sgshapiro			    && !filesys_free(e->e_msgsize)
244490792Sgshapiro#endif /* _FFR_ANY_FREE_FS */
244590792Sgshapiro			   )
244638032Speter			{
244790792Sgshapiro				/*
244890792Sgshapiro				**  We perform this test again when the
244990792Sgshapiro				**  queue directory is selected, in collect.
245090792Sgshapiro				*/
245190792Sgshapiro
245264562Sgshapiro				usrerr("452 4.4.5 Insufficient disk space; try again later");
245390792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
245438032Speter			}
245538032Speter			if (Errors > 0)
245690792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
245764562Sgshapiro
245890792Sgshapiro			LogUsrErrs = true;
245990792Sgshapiro#if MILTER
246090792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
246190792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
246264562Sgshapiro			{
246364562Sgshapiro				char state;
246464562Sgshapiro				char *response;
246564562Sgshapiro
246664562Sgshapiro				response = milter_envfrom(args, e, &state);
246790792Sgshapiro				MILTER_REPLY("from");
246864562Sgshapiro			}
246990792Sgshapiro#endif /* MILTER */
247064562Sgshapiro			if (Errors > 0)
247190792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
247264562Sgshapiro
247364562Sgshapiro			message("250 2.1.0 Sender ok");
247490792Sgshapiro			smtp.sm_gotmail = true;
247590792Sgshapiro		    }
247690792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
247790792Sgshapiro		    {
247890792Sgshapiro			/*
247990792Sgshapiro			**  An error occurred while processing a MAIL command.
248090792Sgshapiro			**  Jump to the common error handling code.
248190792Sgshapiro			*/
248290792Sgshapiro
248390792Sgshapiro			sm_exc_free(exc);
248490792Sgshapiro			goto undo_no_pm;
248590792Sgshapiro		    }
248690792Sgshapiro		    SM_END_TRY
248738032Speter			break;
248838032Speter
248990792Sgshapiro		  undo_no_pm:
249090792Sgshapiro			e->e_flags &= ~EF_PM_NOTIFY;
249190792Sgshapiro		  undo:
249290792Sgshapiro			break;
249390792Sgshapiro
249438032Speter		  case CMDRCPT:		/* rcpt -- designate recipient */
249590792Sgshapiro			DELAY_CONN("RCPT");
2496168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2497168515Sgshapiro				macid("{rcpt_mailer}"), NULL);
2498168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2499168515Sgshapiro				macid("{rcpt_host}"), NULL);
2500168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2501168515Sgshapiro				macid("{rcpt_addr}"), NULL);
2502168515Sgshapiro#if MILTER
2503168515Sgshapiro			(void) memset(&addr_st, '\0', sizeof(addr_st));
2504168515Sgshapiro			a = NULL;
2505168515Sgshapiro			milter_rcpt_added = false;
2506168515Sgshapiro#endif
2507125820Sgshapiro			if (BadRcptThrottle > 0 &&
2508125820Sgshapiro			    n_badrcpts >= BadRcptThrottle)
2509125820Sgshapiro			{
2510125820Sgshapiro				if (LogLevel > 5 &&
2511125820Sgshapiro				    n_badrcpts == BadRcptThrottle)
2512125820Sgshapiro				{
2513125820Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2514125820Sgshapiro						  "%s: Possible SMTP RCPT flood, throttling.",
2515125820Sgshapiro						  CurSmtpClient);
2516125820Sgshapiro
2517125820Sgshapiro					/* To avoid duplicated message */
2518125820Sgshapiro					n_badrcpts++;
2519125820Sgshapiro				}
2520132943Sgshapiro				NBADRCPTS;
2521125820Sgshapiro
2522125820Sgshapiro				/*
2523125820Sgshapiro				**  Don't use exponential backoff for now.
2524125820Sgshapiro				**  Some servers will open more connections
2525125820Sgshapiro				**  and actually overload the receiver even
2526125820Sgshapiro				**  more.
2527125820Sgshapiro				*/
2528125820Sgshapiro
2529125820Sgshapiro				(void) sleep(1);
2530125820Sgshapiro			}
253190792Sgshapiro			if (!smtp.sm_gotmail)
253238032Speter			{
253364562Sgshapiro				usrerr("503 5.0.0 Need MAIL before RCPT");
253438032Speter				break;
253538032Speter			}
253638032Speter			SmtpPhase = "server RCPT";
253790792Sgshapiro		    SM_TRY
253890792Sgshapiro		    {
253990792Sgshapiro			QuickAbort = true;
254090792Sgshapiro			LogUsrErrs = true;
254138032Speter
254238032Speter			/* limit flooding of our machine */
254390792Sgshapiro			if (MaxRcptPerMsg > 0 &&
254490792Sgshapiro			    smtp.sm_nrcpts >= MaxRcptPerMsg)
254538032Speter			{
254690792Sgshapiro				/* sleep(1); / * slow down? */
254764562Sgshapiro				usrerr("452 4.5.3 Too many recipients");
254890792Sgshapiro				goto rcpt_done;
254938032Speter			}
255038032Speter
2551157001Sgshapiro			if (e->e_sendmode != SM_DELIVER
2552157001Sgshapiro#if _FFR_DM_ONE
2553157001Sgshapiro			    && (NotFirstDelivery || SM_DM_ONE != e->e_sendmode)
2554157001Sgshapiro#endif /* _FFR_DM_ONE */
2555157001Sgshapiro			   )
255638032Speter				e->e_flags |= EF_VRFYONLY;
255738032Speter
255890792Sgshapiro#if MILTER
255964562Sgshapiro			/*
2560168515Sgshapiro			**  Do not expand recipients at RCPT time (in the call
256164562Sgshapiro			**  to recipient()).  If they are expanded, it
256264562Sgshapiro			**  is impossible for removefromlist() to figure
256364562Sgshapiro			**  out the expanded members of the original
256464562Sgshapiro			**  recipient and mark them as QS_DONTSEND.
256564562Sgshapiro			*/
256664562Sgshapiro
2567168515Sgshapiro			e->e_flags |= EF_VRFYONLY;
2568168515Sgshapiro			milter_cmd_done = false;
2569168515Sgshapiro			milter_cmd_safe = false;
257090792Sgshapiro#endif /* MILTER */
257164562Sgshapiro
257238032Speter			p = skipword(p, "to");
257338032Speter			if (p == NULL)
257490792Sgshapiro				goto rcpt_done;
257590792Sgshapiro			macdefine(&e->e_macro, A_PERM,
257690792Sgshapiro				macid("{addr_type}"), "e r");
257790792Sgshapiro			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
257890792Sgshapiro				      e, true);
257990792Sgshapiro			macdefine(&e->e_macro, A_PERM,
258090792Sgshapiro				macid("{addr_type}"), NULL);
258142575Speter			if (Errors > 0)
258290792Sgshapiro				goto rcpt_done;
258342575Speter			if (a == NULL)
258442575Speter			{
258564562Sgshapiro				usrerr("501 5.0.0 Missing recipient");
258690792Sgshapiro				goto rcpt_done;
258742575Speter			}
258842575Speter
258938032Speter			if (delimptr != NULL && *delimptr != '\0')
259038032Speter				*delimptr++ = '\0';
259138032Speter
259264562Sgshapiro			/* put resulting triple from parseaddr() into macros */
259364562Sgshapiro			if (a->q_mailer != NULL)
259490792Sgshapiro				macdefine(&e->e_macro, A_PERM,
259590792Sgshapiro					macid("{rcpt_mailer}"),
259690792Sgshapiro					a->q_mailer->m_name);
259764562Sgshapiro			else
259890792Sgshapiro				macdefine(&e->e_macro, A_PERM,
259990792Sgshapiro					macid("{rcpt_mailer}"), NULL);
260064562Sgshapiro			if (a->q_host != NULL)
260190792Sgshapiro				macdefine(&e->e_macro, A_PERM,
260290792Sgshapiro					macid("{rcpt_host}"), a->q_host);
260364562Sgshapiro			else
260490792Sgshapiro				macdefine(&e->e_macro, A_PERM,
260590792Sgshapiro					macid("{rcpt_host}"), "localhost");
260664562Sgshapiro			if (a->q_user != NULL)
260790792Sgshapiro				macdefine(&e->e_macro, A_PERM,
260890792Sgshapiro					macid("{rcpt_addr}"), a->q_user);
260964562Sgshapiro			else
261090792Sgshapiro				macdefine(&e->e_macro, A_PERM,
261190792Sgshapiro					macid("{rcpt_addr}"), NULL);
261264562Sgshapiro			if (Errors > 0)
261390792Sgshapiro				goto rcpt_done;
261438032Speter
261538032Speter			/* now parse ESMTP arguments */
261664562Sgshapiro			addr = p;
2617168515Sgshapiro			parse_esmtp_args(e, a, p, delimptr, "RCPT", args,
2618168515Sgshapiro					rcpt_esmtp_args);
261938032Speter			if (Errors > 0)
262090792Sgshapiro				goto rcpt_done;
262138032Speter
2622168515Sgshapiro#if MILTER
2623168515Sgshapiro			/*
2624168515Sgshapiro			**  rscheck() can trigger an "exception"
2625168515Sgshapiro			**  in which case the execution continues at
2626168515Sgshapiro			**  SM_EXCEPT(exc, "[!F]*")
2627168515Sgshapiro			**  This means milter_cmd_safe is not set
2628168515Sgshapiro			**  and hence milter is not invoked.
2629168515Sgshapiro			**  Would it be "safe" to change that, i.e., use
2630168515Sgshapiro			**  milter_cmd_safe = true;
2631168515Sgshapiro			**  here so a milter is informed (if requested)
2632168515Sgshapiro			**  about RCPTs that are rejected by check_rcpt?
2633168515Sgshapiro			*/
2634168515Sgshapiro# if _FFR_MILTER_CHECK_REJECTIONS_TOO
2635168515Sgshapiro			milter_cmd_safe = true;
2636168515Sgshapiro# endif
2637168515Sgshapiro#endif
2638168515Sgshapiro
263964562Sgshapiro			/* do config file checking of the recipient */
264090792Sgshapiro			macdefine(&e->e_macro, A_PERM,
264190792Sgshapiro				macid("{addr_type}"), "e r");
264264562Sgshapiro			if (rscheck("check_rcpt", addr,
2643102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
2644168515Sgshapiro				    NULL, e->e_id, p_addr_st) != EX_OK ||
264564562Sgshapiro			    Errors > 0)
264690792Sgshapiro				goto rcpt_done;
264790792Sgshapiro			macdefine(&e->e_macro, A_PERM,
264890792Sgshapiro				macid("{addr_type}"), NULL);
264964562Sgshapiro
2650102528Sgshapiro			/* If discarding, don't bother to verify user */
2651102528Sgshapiro			if (bitset(EF_DISCARD, e->e_flags))
2652102528Sgshapiro				a->q_state = QS_VERIFIED;
2653168515Sgshapiro#if MILTER
2654168515Sgshapiro			milter_cmd_safe = true;
2655168515Sgshapiro#endif
2656102528Sgshapiro
2657168515Sgshapiro			/* save in recipient list after ESMTP mods */
2658168515Sgshapiro			a = recipient(a, &e->e_sendqueue, 0, e);
2659168515Sgshapiro			/* may trigger exception... */
2660168515Sgshapiro
266190792Sgshapiro#if MILTER
2662168515Sgshapiro			milter_rcpt_added = true;
2663168515Sgshapiro#endif
2664168515Sgshapiro
2665168515Sgshapiro			if(!(Errors > 0) && QS_IS_BADADDR(a->q_state))
2666168515Sgshapiro			{
2667168515Sgshapiro				/* punt -- should keep message in ADDRESS.... */
2668168515Sgshapiro				usrerr("550 5.1.1 Addressee unknown");
2669168515Sgshapiro			}
2670168515Sgshapiro
2671168515Sgshapiro#if MILTER
2672168515Sgshapiro		rcpt_done:
267390792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
267490792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
267564562Sgshapiro			{
267664562Sgshapiro				char state;
267764562Sgshapiro				char *response;
267864562Sgshapiro
2679168515Sgshapiro				/* how to get the error codes? */
2680168515Sgshapiro				if (Errors > 0)
2681168515Sgshapiro				{
2682168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
2683168515Sgshapiro						macid("{rcpt_mailer}"),
2684168515Sgshapiro						"error");
2685168515Sgshapiro					if (a != NULL &&
2686168515Sgshapiro					    a->q_status != NULL &&
2687168515Sgshapiro					    a->q_rstatus != NULL)
2688168515Sgshapiro					{
2689168515Sgshapiro						macdefine(&e->e_macro, A_PERM,
2690168515Sgshapiro							macid("{rcpt_host}"),
2691168515Sgshapiro							a->q_status);
2692168515Sgshapiro						macdefine(&e->e_macro, A_PERM,
2693168515Sgshapiro							macid("{rcpt_addr}"),
2694168515Sgshapiro							a->q_rstatus);
2695168515Sgshapiro					}
2696168515Sgshapiro					else
2697168515Sgshapiro					{
2698168515Sgshapiro						if (addr_st.q_host != NULL)
2699168515Sgshapiro							macdefine(&e->e_macro,
2700168515Sgshapiro								A_PERM,
2701168515Sgshapiro								macid("{rcpt_host}"),
2702168515Sgshapiro								addr_st.q_host);
2703168515Sgshapiro						if (addr_st.q_user != NULL)
2704168515Sgshapiro							macdefine(&e->e_macro,
2705168515Sgshapiro								A_PERM,
2706168515Sgshapiro								macid("{rcpt_addr}"),
2707168515Sgshapiro								addr_st.q_user);
2708168515Sgshapiro					}
2709168515Sgshapiro				}
2710168515Sgshapiro
2711168515Sgshapiro				response = milter_envrcpt(args, e, &state,
2712168515Sgshapiro							Errors > 0);
2713168515Sgshapiro				milter_cmd_done = true;
271490792Sgshapiro				MILTER_REPLY("to");
271564562Sgshapiro			}
271690792Sgshapiro#endif /* MILTER */
271764562Sgshapiro
271838032Speter			/* no errors during parsing, but might be a duplicate */
271938032Speter			e->e_to = a->q_paddr;
2720168515Sgshapiro			if (!(Errors > 0) && !QS_IS_BADADDR(a->q_state))
272138032Speter			{
272290792Sgshapiro				if (smtp.sm_nrcpts == 0)
272364562Sgshapiro					initsys(e);
272464562Sgshapiro				message("250 2.1.5 Recipient ok%s",
272564562Sgshapiro					QS_IS_QUEUEUP(a->q_state) ?
272638032Speter						" (will queue)" : "");
272790792Sgshapiro				smtp.sm_nrcpts++;
272838032Speter			}
2729168515Sgshapiro
2730168515Sgshapiro			/* Is this needed? */
2731168515Sgshapiro#if !MILTER
2732168515Sgshapiro		rcpt_done:
2733168515Sgshapiro#endif /* !MILTER */
2734168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2735168515Sgshapiro				macid("{rcpt_mailer}"), NULL);
2736168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2737168515Sgshapiro				macid("{rcpt_host}"), NULL);
2738168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2739168515Sgshapiro				macid("{rcpt_addr}"), NULL);
2740168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2741168515Sgshapiro				macid("{dsn_notify}"), NULL);
2742168515Sgshapiro
274390792Sgshapiro			if (Errors > 0)
2744132943Sgshapiro			{
274590792Sgshapiro				++n_badrcpts;
2746132943Sgshapiro				NBADRCPTS;
2747132943Sgshapiro			}
274890792Sgshapiro		    }
274990792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
275090792Sgshapiro		    {
275190792Sgshapiro			/* An exception occurred while processing RCPT */
275290792Sgshapiro			e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
275390792Sgshapiro			++n_badrcpts;
2754132943Sgshapiro			NBADRCPTS;
2755168515Sgshapiro#if MILTER
2756168515Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
2757168515Sgshapiro			    !bitset(EF_DISCARD, e->e_flags) &&
2758168515Sgshapiro			    !milter_cmd_done && milter_cmd_safe)
2759168515Sgshapiro			{
2760168515Sgshapiro				char state;
2761168515Sgshapiro				char *response;
2762168515Sgshapiro
2763168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
2764168515Sgshapiro					macid("{rcpt_mailer}"), "error");
2765168515Sgshapiro
2766168515Sgshapiro				/* how to get the error codes? */
2767168515Sgshapiro				if (addr_st.q_host != NULL)
2768168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
2769168515Sgshapiro						macid("{rcpt_host}"),
2770168515Sgshapiro						addr_st.q_host);
2771168515Sgshapiro				else if (a != NULL && a->q_status != NULL)
2772168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
2773168515Sgshapiro						macid("{rcpt_host}"),
2774168515Sgshapiro						a->q_status);
2775168515Sgshapiro
2776168515Sgshapiro				if (addr_st.q_user != NULL)
2777168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
2778168515Sgshapiro						macid("{rcpt_addr}"),
2779168515Sgshapiro						addr_st.q_user);
2780168515Sgshapiro				else if (a != NULL && a->q_rstatus != NULL)
2781168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
2782168515Sgshapiro						macid("{rcpt_addr}"),
2783168515Sgshapiro						a->q_rstatus);
2784168515Sgshapiro
2785168515Sgshapiro				response = milter_envrcpt(args, e, &state,
2786168515Sgshapiro							true);
2787168515Sgshapiro				milter_cmd_done = true;
2788168515Sgshapiro				MILTER_REPLY("to");
2789168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
2790168515Sgshapiro					macid("{rcpt_mailer}"), NULL);
2791168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
2792168515Sgshapiro					macid("{rcpt_host}"), NULL);
2793168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
2794168515Sgshapiro					macid("{rcpt_addr}"), NULL);
2795168515Sgshapiro			}
2796168515Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
2797168515Sgshapiro			    milter_rcpt_added && milter_cmd_done &&
2798168515Sgshapiro			    milter_cmd_fail)
2799168515Sgshapiro			{
2800168515Sgshapiro				(void) removefromlist(addr, &e->e_sendqueue, e);
2801168515Sgshapiro				milter_cmd_fail = false;
2802168515Sgshapiro			}
2803168515Sgshapiro#endif /* MILTER */
280490792Sgshapiro		    }
280590792Sgshapiro		    SM_END_TRY
280638032Speter			break;
280738032Speter
280838032Speter		  case CMDDATA:		/* data -- text of mail */
280990792Sgshapiro			DELAY_CONN("DATA");
2810132943Sgshapiro			if (!smtp_data(&smtp, e))
2811132943Sgshapiro				goto doquit;
281238032Speter			break;
281338032Speter
281438032Speter		  case CMDRSET:		/* rset -- reset state */
281538032Speter			if (tTd(94, 100))
281664562Sgshapiro				message("451 4.0.0 Test failure");
281738032Speter			else
281864562Sgshapiro				message("250 2.0.0 Reset state");
281990792Sgshapiro			CLEAR_STATE(cmdbuf);
282038032Speter			break;
282138032Speter
282238032Speter		  case CMDVRFY:		/* vrfy -- verify address */
282338032Speter		  case CMDEXPN:		/* expn -- expand address */
282490792Sgshapiro			vrfy = c->cmd_code == CMDVRFY;
282590792Sgshapiro			DELAY_CONN(vrfy ? "VRFY" : "EXPN");
282664562Sgshapiro			if (tempfail)
282764562Sgshapiro			{
282864562Sgshapiro				if (LogLevel > 9)
282964562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2830110560Sgshapiro						  "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)",
283190792Sgshapiro						  vrfy ? "VRFY" : "EXPN",
283264562Sgshapiro						  p, CurSmtpClient);
283390792Sgshapiro
283490792Sgshapiro				/* RFC 821 doesn't allow 4xy reply code */
283564562Sgshapiro				usrerr("550 5.7.1 Please try again later");
283664562Sgshapiro				break;
283764562Sgshapiro			}
283890792Sgshapiro			wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
283990792Sgshapiro					     false, vrfy ? "VRFY" : "EXPN", e);
2840132943Sgshapiro			STOP_IF_ATTACK(wt);
284164562Sgshapiro			previous = curtime();
2842110560Sgshapiro			if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) ||
2843110560Sgshapiro			    (!vrfy && !bitset(SRV_OFFER_EXPN, features)))
284438032Speter			{
284538032Speter				if (vrfy)
284664562Sgshapiro					message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
284738032Speter				else
284864562Sgshapiro					message("502 5.7.0 Sorry, we do not allow this operation");
284938032Speter				if (LogLevel > 5)
285038032Speter					sm_syslog(LOG_INFO, e->e_id,
2851110560Sgshapiro						  "%s: %s [rejected]",
285264562Sgshapiro						  CurSmtpClient,
285364562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
285438032Speter				break;
285538032Speter			}
285638032Speter			else if (!gothello &&
285738032Speter				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
285838032Speter						PrivacyFlags))
285938032Speter			{
286064562Sgshapiro				usrerr("503 5.0.0 I demand that you introduce yourself first");
286138032Speter				break;
286238032Speter			}
286390792Sgshapiro			if (Errors > 0)
286438032Speter				break;
286538032Speter			if (LogLevel > 5)
2866110560Sgshapiro				sm_syslog(LOG_INFO, e->e_id, "%s: %s",
286764562Sgshapiro					  CurSmtpClient,
286864562Sgshapiro					  shortenstring(inp, MAXSHORTSTR));
286990792Sgshapiro		    SM_TRY
287090792Sgshapiro		    {
287190792Sgshapiro			QuickAbort = true;
287238032Speter			vrfyqueue = NULL;
287338032Speter			if (vrfy)
287438032Speter				e->e_flags |= EF_VRFYONLY;
287538032Speter			while (*p != '\0' && isascii(*p) && isspace(*p))
287638032Speter				p++;
287738032Speter			if (*p == '\0')
287838032Speter			{
287964562Sgshapiro				usrerr("501 5.5.2 Argument required");
288038032Speter			}
288138032Speter			else
288238032Speter			{
288364562Sgshapiro				/* do config file checking of the address */
288464562Sgshapiro				if (rscheck(vrfy ? "check_vrfy" : "check_expn",
2885102528Sgshapiro					    p, NULL, e, RSF_RMCOMM,
2886168515Sgshapiro					    3, NULL, NOQID, NULL) != EX_OK ||
288790792Sgshapiro				    Errors > 0)
288890792Sgshapiro					sm_exc_raisenew_x(&EtypeQuickAbort, 1);
288938032Speter				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
289038032Speter			}
289164562Sgshapiro			if (wt > 0)
289271345Sgshapiro			{
289371345Sgshapiro				time_t t;
289471345Sgshapiro
289571345Sgshapiro				t = wt - (curtime() - previous);
289671345Sgshapiro				if (t > 0)
289771345Sgshapiro					(void) sleep(t);
289871345Sgshapiro			}
289938032Speter			if (Errors > 0)
290090792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
290138032Speter			if (vrfyqueue == NULL)
290238032Speter			{
290364562Sgshapiro				usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
290438032Speter			}
290538032Speter			while (vrfyqueue != NULL)
290638032Speter			{
290764562Sgshapiro				if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
290864562Sgshapiro				{
290964562Sgshapiro					vrfyqueue = vrfyqueue->q_next;
291064562Sgshapiro					continue;
291164562Sgshapiro				}
291238032Speter
291364562Sgshapiro				/* see if there is more in the vrfy list */
291438032Speter				a = vrfyqueue;
291538032Speter				while ((a = a->q_next) != NULL &&
291666494Sgshapiro				       (!QS_IS_UNDELIVERED(a->q_state)))
291738032Speter					continue;
291864562Sgshapiro				printvrfyaddr(vrfyqueue, a == NULL, vrfy);
291964562Sgshapiro				vrfyqueue = a;
292038032Speter			}
292190792Sgshapiro		    }
292290792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
292390792Sgshapiro		    {
292490792Sgshapiro			/*
292590792Sgshapiro			**  An exception occurred while processing VRFY/EXPN
292690792Sgshapiro			*/
292790792Sgshapiro
292890792Sgshapiro			sm_exc_free(exc);
292990792Sgshapiro			goto undo;
293090792Sgshapiro		    }
293190792Sgshapiro		    SM_END_TRY
293238032Speter			break;
293338032Speter
293438032Speter		  case CMDETRN:		/* etrn -- force queue flush */
293590792Sgshapiro			DELAY_CONN("ETRN");
293690792Sgshapiro
293790792Sgshapiro			/* Don't leak queue information via debug flags */
293890792Sgshapiro			if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
293990792Sgshapiro			    (RealUid != 0 && RealUid != TrustedUid &&
294090792Sgshapiro			     OpMode == MD_SMTP))
294138032Speter			{
294264562Sgshapiro				/* different message for MSA ? */
294364562Sgshapiro				message("502 5.7.0 Sorry, we do not allow this operation");
294438032Speter				if (LogLevel > 5)
294538032Speter					sm_syslog(LOG_INFO, e->e_id,
2946110560Sgshapiro						  "%s: %s [rejected]",
294764562Sgshapiro						  CurSmtpClient,
294864562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
294938032Speter				break;
295038032Speter			}
295164562Sgshapiro			if (tempfail)
295264562Sgshapiro			{
295364562Sgshapiro				if (LogLevel > 9)
295464562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2955110560Sgshapiro						  "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)",
295664562Sgshapiro						  p, CurSmtpClient);
295790792Sgshapiro				usrerr(MSG_TEMPFAIL);
295864562Sgshapiro				break;
295964562Sgshapiro			}
296038032Speter
296138032Speter			if (strlen(p) <= 0)
296238032Speter			{
296364562Sgshapiro				usrerr("500 5.5.2 Parameter required");
296438032Speter				break;
296538032Speter			}
296638032Speter
296738032Speter			/* crude way to avoid denial-of-service attacks */
2968132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS,
2969132943Sgshapiro							true, "ETRN", e));
297038032Speter
297190792Sgshapiro			/*
297290792Sgshapiro			**  Do config file checking of the parameter.
297390792Sgshapiro			**  Even though we have srv_features now, we still
297490792Sgshapiro			**  need this ruleset because the former is called
297590792Sgshapiro			**  when the connection has been established, while
297690792Sgshapiro			**  this ruleset is called when the command is
297790792Sgshapiro			**  actually issued and therefore has all information
297890792Sgshapiro			**  available to make a decision.
297990792Sgshapiro			*/
298090792Sgshapiro
2981102528Sgshapiro			if (rscheck("check_etrn", p, NULL, e,
2982168515Sgshapiro				    RSF_RMCOMM, 3, NULL, NOQID, NULL)
2983168515Sgshapiro								!= EX_OK ||
2984102528Sgshapiro			    Errors > 0)
298564562Sgshapiro				break;
298664562Sgshapiro
298738032Speter			if (LogLevel > 5)
298838032Speter				sm_syslog(LOG_INFO, e->e_id,
2989110560Sgshapiro					  "%s: ETRN %s", CurSmtpClient,
299064562Sgshapiro					  shortenstring(p, MAXSHORTSTR));
299138032Speter
299238032Speter			id = p;
299390792Sgshapiro			if (*id == '#')
299490792Sgshapiro			{
2995111823Sgshapiro				int i, qgrp;
299690792Sgshapiro
299790792Sgshapiro				id++;
2998111823Sgshapiro				qgrp = name2qid(id);
2999111823Sgshapiro				if (!ISVALIDQGRP(qgrp))
300090792Sgshapiro				{
300190792Sgshapiro					usrerr("459 4.5.4 Queue %s unknown",
300290792Sgshapiro					       id);
300390792Sgshapiro					break;
300490792Sgshapiro				}
3005111823Sgshapiro				for (i = 0; i < NumQueue && Queue[i] != NULL;
3006111823Sgshapiro				     i++)
3007111823Sgshapiro					Queue[i]->qg_nextrun = (time_t) -1;
3008111823Sgshapiro				Queue[qgrp]->qg_nextrun = 0;
3009111823Sgshapiro				ok = run_work_group(Queue[qgrp]->qg_wgrp,
3010111823Sgshapiro						    RWG_FORK|RWG_FORCE);
301190792Sgshapiro				if (ok && Errors == 0)
301290792Sgshapiro					message("250 2.0.0 Queuing for queue group %s started", id);
301390792Sgshapiro				break;
301490792Sgshapiro			}
301590792Sgshapiro
301638032Speter			if (*id == '@')
301738032Speter				id++;
301838032Speter			else
301938032Speter				*--id = '@';
302064562Sgshapiro
302190792Sgshapiro			new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
302290792Sgshapiro			if (new == NULL)
302390792Sgshapiro			{
302490792Sgshapiro				syserr("500 5.5.0 ETRN out of memory");
302590792Sgshapiro				break;
302690792Sgshapiro			}
302738032Speter			new->queue_match = id;
302890792Sgshapiro			new->queue_negate = false;
302938032Speter			new->queue_next = NULL;
303038032Speter			QueueLimitRecipient = new;
303190792Sgshapiro			ok = runqueue(true, false, false, true);
303290792Sgshapiro			sm_free(QueueLimitRecipient); /* XXX */
303338032Speter			QueueLimitRecipient = NULL;
303438032Speter			if (ok && Errors == 0)
303564562Sgshapiro				message("250 2.0.0 Queuing for node %s started", p);
303638032Speter			break;
303738032Speter
303838032Speter		  case CMDHELP:		/* help -- give user info */
303990792Sgshapiro			DELAY_CONN("HELP");
304064562Sgshapiro			help(p, e);
304138032Speter			break;
304238032Speter
304338032Speter		  case CMDNOOP:		/* noop -- do nothing */
304490792Sgshapiro			DELAY_CONN("NOOP");
3045157001Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
3046132943Sgshapiro							true, "NOOP", e));
304764562Sgshapiro			message("250 2.0.0 OK");
304838032Speter			break;
304938032Speter
305038032Speter		  case CMDQUIT:		/* quit -- leave mail */
305164562Sgshapiro			message("221 2.0.0 %s closing connection", MyHostName);
305290792Sgshapiro#if PIPELINING
305390792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
305490792Sgshapiro#endif /* PIPELINING */
305538032Speter
305690792Sgshapiro			if (smtp.sm_nrcpts > 0)
305790792Sgshapiro				logundelrcpts(e, "aborted by sender", 9, false);
305890792Sgshapiro
305938032Speter			/* arrange to ignore any current send list */
306038032Speter			e->e_sendqueue = NULL;
306138032Speter
306290792Sgshapiro#if STARTTLS
306364562Sgshapiro			/* shutdown TLS connection */
306464562Sgshapiro			if (tls_active)
306564562Sgshapiro			{
306664562Sgshapiro				(void) endtls(srv_ssl, "server");
306790792Sgshapiro				tls_active = false;
306864562Sgshapiro			}
306990792Sgshapiro#endif /* STARTTLS */
307090792Sgshapiro#if SASL
307164562Sgshapiro			if (authenticating == SASL_IS_AUTH)
307264562Sgshapiro			{
307364562Sgshapiro				sasl_dispose(&conn);
307464562Sgshapiro				authenticating = SASL_NOT_AUTH;
307590792Sgshapiro				/* XXX sasl_done(); this is a child */
307664562Sgshapiro			}
307790792Sgshapiro#endif /* SASL */
307864562Sgshapiro
307964562Sgshapirodoquit:
308038032Speter			/* avoid future 050 messages */
308138032Speter			disconnect(1, e);
308238032Speter
308390792Sgshapiro#if MILTER
308464562Sgshapiro			/* close out milter filters */
308564562Sgshapiro			milter_quit(e);
308690792Sgshapiro#endif /* MILTER */
308764562Sgshapiro
308864562Sgshapiro			if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
308964562Sgshapiro				logsender(e, NULL);
309064562Sgshapiro			e->e_flags &= ~EF_LOGSENDER;
309164562Sgshapiro
309298121Sgshapiro			if (lognullconnection && LogLevel > 5 &&
309398121Sgshapiro			    nullserver == NULL)
309464562Sgshapiro			{
309564562Sgshapiro				char *d;
309664562Sgshapiro
309790792Sgshapiro				d = macvalue(macid("{daemon_name}"), e);
309864562Sgshapiro				if (d == NULL)
309964562Sgshapiro					d = "stdin";
310090792Sgshapiro
310190792Sgshapiro				/*
310290792Sgshapiro				**  even though this id is "bogus", it makes
310390792Sgshapiro				**  it simpler to "grep" related events, e.g.,
310490792Sgshapiro				**  timeouts for the same connection.
310590792Sgshapiro				*/
310690792Sgshapiro
310790792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3108110560Sgshapiro					  "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
310964562Sgshapiro					  CurSmtpClient, d);
311064562Sgshapiro			}
3111110560Sgshapiro			if (tTd(93, 100))
3112110560Sgshapiro			{
3113110560Sgshapiro				/* return to handle next connection */
3114110560Sgshapiro				return;
3115110560Sgshapiro			}
311690792Sgshapiro			finis(true, true, ExitStat);
311764562Sgshapiro			/* NOTREACHED */
311838032Speter
3119157001Sgshapiro			/* just to avoid bogus warning from some compilers */
3120157001Sgshapiro			exit(EX_OSERR);
3121157001Sgshapiro
312238032Speter		  case CMDVERB:		/* set verbose mode */
312390792Sgshapiro			DELAY_CONN("VERB");
3124110560Sgshapiro			if (!bitset(SRV_OFFER_EXPN, features) ||
3125110560Sgshapiro			    !bitset(SRV_OFFER_VERB, features))
312638032Speter			{
312738032Speter				/* this would give out the same info */
312864562Sgshapiro				message("502 5.7.0 Verbose unavailable");
312938032Speter				break;
313038032Speter			}
3131157001Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
3132132943Sgshapiro							true, "VERB", e));
313338032Speter			Verbose = 1;
313464562Sgshapiro			set_delivery_mode(SM_DELIVER, e);
313564562Sgshapiro			message("250 2.0.0 Verbose mode");
313638032Speter			break;
313738032Speter
313890792Sgshapiro#if SMTPDEBUG
313938032Speter		  case CMDDBGQSHOW:	/* show queues */
314090792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
314190792Sgshapiro					     "Send Queue=");
3142132943Sgshapiro			printaddr(smioout, e->e_sendqueue, true);
314338032Speter			break;
314438032Speter
314538032Speter		  case CMDDBGDEBUG:	/* set debug mode */
3146168515Sgshapiro			tTsetup(tTdvect, sizeof(tTdvect), "0-99.1");
314738032Speter			tTflag(p);
314864562Sgshapiro			message("200 2.0.0 Debug set");
314938032Speter			break;
315038032Speter
315190792Sgshapiro#else /* SMTPDEBUG */
315238032Speter		  case CMDDBGQSHOW:	/* show queues */
315338032Speter		  case CMDDBGDEBUG:	/* set debug mode */
315490792Sgshapiro#endif /* SMTPDEBUG */
315538032Speter		  case CMDLOGBOGUS:	/* bogus command */
315690792Sgshapiro			DELAY_CONN("Bogus");
315738032Speter			if (LogLevel > 0)
315838032Speter				sm_syslog(LOG_CRIT, e->e_id,
3159110560Sgshapiro					  "\"%s\" command from %s (%.100s)",
316064562Sgshapiro					  c->cmd_name, CurSmtpClient,
316164562Sgshapiro					  anynet_ntoa(&RealHostAddr));
316264562Sgshapiro			/* FALLTHROUGH */
316338032Speter
316438032Speter		  case CMDERROR:	/* unknown command */
316590792Sgshapiro#if MAXBADCOMMANDS > 0
316690792Sgshapiro			if (++n_badcmds > MAXBADCOMMANDS)
316738032Speter			{
3168132943Sgshapiro  stopattack:
316964562Sgshapiro				message("421 4.7.0 %s Too many bad commands; closing connection",
317038032Speter					MyHostName);
317164562Sgshapiro
317264562Sgshapiro				/* arrange to ignore any current send list */
317364562Sgshapiro				e->e_sendqueue = NULL;
317438032Speter				goto doquit;
317538032Speter			}
317690792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
317738032Speter
3178132943Sgshapiro#if MILTER && SMFI_VERSION > 2
3179132943Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
3180132943Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
3181132943Sgshapiro			{
3182132943Sgshapiro				char state;
3183132943Sgshapiro				char *response;
3184132943Sgshapiro
3185132943Sgshapiro				if (MilterLogLevel > 9)
3186132943Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
3187132943Sgshapiro						"Sending \"%s\" to Milter", inp);
3188132943Sgshapiro				response = milter_unknown(inp, e, &state);
3189132943Sgshapiro				MILTER_REPLY("unknown");
3190132943Sgshapiro				if (state == SMFIR_REPLYCODE ||
3191132943Sgshapiro				    state == SMFIR_REJECT ||
3192157001Sgshapiro				    state == SMFIR_TEMPFAIL ||
3193157001Sgshapiro				    state == SMFIR_SHUTDOWN)
3194132943Sgshapiro				{
3195132943Sgshapiro					/* MILTER_REPLY already gave an error */
3196132943Sgshapiro					break;
3197132943Sgshapiro				}
3198132943Sgshapiro			}
3199132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 2 */
3200132943Sgshapiro
320164562Sgshapiro			usrerr("500 5.5.1 Command unrecognized: \"%s\"",
320264562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
320338032Speter			break;
320438032Speter
320564562Sgshapiro		  case CMDUNIMPL:
320690792Sgshapiro			DELAY_CONN("Unimpl");
320764562Sgshapiro			usrerr("502 5.5.1 Command not implemented: \"%s\"",
320864562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
320964562Sgshapiro			break;
321064562Sgshapiro
321138032Speter		  default:
321290792Sgshapiro			DELAY_CONN("default");
321338032Speter			errno = 0;
321464562Sgshapiro			syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
321538032Speter			break;
321638032Speter		}
321790792Sgshapiro#if SASL
321864562Sgshapiro		}
321990792Sgshapiro#endif /* SASL */
322090792Sgshapiro	    }
322190792Sgshapiro	    SM_EXCEPT(exc, "[!F]*")
322290792Sgshapiro	    {
322390792Sgshapiro		/*
322490792Sgshapiro		**  The only possible exception is "E:mta.quickabort".
322590792Sgshapiro		**  There is nothing to do except fall through and loop.
322690792Sgshapiro		*/
322790792Sgshapiro	    }
322890792Sgshapiro	    SM_END_TRY
322938032Speter	}
323090792Sgshapiro}
323190792Sgshapiro/*
323290792Sgshapiro**  SMTP_DATA -- implement the SMTP DATA command.
323390792Sgshapiro**
323490792Sgshapiro**	Parameters:
323590792Sgshapiro**		smtp -- status of SMTP connection.
323690792Sgshapiro**		e -- envelope.
323790792Sgshapiro**
323890792Sgshapiro**	Returns:
3239132943Sgshapiro**		true iff SMTP session can continue.
324090792Sgshapiro**
324190792Sgshapiro**	Side Effects:
324290792Sgshapiro**		possibly sends message.
324390792Sgshapiro*/
324464562Sgshapiro
3245132943Sgshapirostatic bool
324690792Sgshapirosmtp_data(smtp, e)
324790792Sgshapiro	SMTP_T *smtp;
324890792Sgshapiro	ENVELOPE *e;
324990792Sgshapiro{
325090792Sgshapiro#if MILTER
325190792Sgshapiro	bool milteraccept;
325290792Sgshapiro#endif /* MILTER */
325390792Sgshapiro	bool aborting;
325490792Sgshapiro	bool doublequeue;
3255168515Sgshapiro	bool rv = true;
325690792Sgshapiro	ADDRESS *a;
325790792Sgshapiro	ENVELOPE *ee;
325890792Sgshapiro	char *id;
325998121Sgshapiro	char *oldid;
3260168515Sgshapiro	unsigned int features;
326190792Sgshapiro	char buf[32];
326290792Sgshapiro
326390792Sgshapiro	SmtpPhase = "server DATA";
326490792Sgshapiro	if (!smtp->sm_gotmail)
326590792Sgshapiro	{
326690792Sgshapiro		usrerr("503 5.0.0 Need MAIL command");
3267132943Sgshapiro		return true;
326890792Sgshapiro	}
326990792Sgshapiro	else if (smtp->sm_nrcpts <= 0)
327090792Sgshapiro	{
327190792Sgshapiro		usrerr("503 5.0.0 Need RCPT (recipient)");
3272132943Sgshapiro		return true;
327390792Sgshapiro	}
3274168515Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "%u", smtp->sm_nrcpts);
327590792Sgshapiro	if (rscheck("check_data", buf, NULL, e,
3276102528Sgshapiro		    RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
3277168515Sgshapiro		    e->e_id, NULL) != EX_OK)
3278132943Sgshapiro		return true;
327990792Sgshapiro
3280132943Sgshapiro#if MILTER && SMFI_VERSION > 3
3281132943Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize &&
3282132943Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
3283132943Sgshapiro	{
3284132943Sgshapiro		char state;
3285132943Sgshapiro		char *response;
3286132943Sgshapiro		int savelogusrerrs = LogUsrErrs;
3287132943Sgshapiro
3288132943Sgshapiro		response = milter_data_cmd(e, &state);
3289132943Sgshapiro		switch (state)
3290132943Sgshapiro		{
3291132943Sgshapiro		  case SMFIR_REPLYCODE:
3292132943Sgshapiro			if (MilterLogLevel > 3)
3293132943Sgshapiro			{
3294132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3295132943Sgshapiro					  "Milter: cmd=data, reject=%s",
3296132943Sgshapiro					  response);
3297132943Sgshapiro				LogUsrErrs = false;
3298132943Sgshapiro			}
3299132943Sgshapiro			usrerr(response);
3300157001Sgshapiro			if (strncmp(response, "421 ", 4) == 0
3301157001Sgshapiro			    || strncmp(response, "421-", 4) == 0)
3302132943Sgshapiro			{
3303132943Sgshapiro				e->e_sendqueue = NULL;
3304132943Sgshapiro				return false;
3305132943Sgshapiro			}
3306132943Sgshapiro			return true;
3307132943Sgshapiro
3308132943Sgshapiro		  case SMFIR_REJECT:
3309132943Sgshapiro			if (MilterLogLevel > 3)
3310132943Sgshapiro			{
3311132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3312132943Sgshapiro					  "Milter: cmd=data, reject=550 5.7.1 Command rejected");
3313132943Sgshapiro				LogUsrErrs = false;
3314132943Sgshapiro			}
3315132943Sgshapiro			usrerr("550 5.7.1 Command rejected");
3316132943Sgshapiro			return true;
3317132943Sgshapiro
3318132943Sgshapiro		  case SMFIR_DISCARD:
3319132943Sgshapiro			if (MilterLogLevel > 3)
3320132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3321132943Sgshapiro					  "Milter: cmd=data, discard");
3322132943Sgshapiro			e->e_flags |= EF_DISCARD;
3323132943Sgshapiro			break;
3324132943Sgshapiro
3325132943Sgshapiro		  case SMFIR_TEMPFAIL:
3326132943Sgshapiro			if (MilterLogLevel > 3)
3327132943Sgshapiro			{
3328132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3329132943Sgshapiro					  "Milter: cmd=data, reject=%s",
3330132943Sgshapiro					  MSG_TEMPFAIL);
3331132943Sgshapiro				LogUsrErrs = false;
3332132943Sgshapiro			}
3333132943Sgshapiro			usrerr(MSG_TEMPFAIL);
3334132943Sgshapiro			return true;
3335157001Sgshapiro
3336157001Sgshapiro		  case SMFIR_SHUTDOWN:
3337157001Sgshapiro			if (MilterLogLevel > 3)
3338157001Sgshapiro			{
3339157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3340157001Sgshapiro					  "Milter: cmd=data, reject=421 4.7.0 %s closing connection",
3341157001Sgshapiro					  MyHostName);
3342157001Sgshapiro				LogUsrErrs = false;
3343157001Sgshapiro			}
3344157001Sgshapiro			usrerr("421 4.7.0 %s closing connection", MyHostName);
3345157001Sgshapiro			e->e_sendqueue = NULL;
3346157001Sgshapiro			return false;
3347132943Sgshapiro		}
3348132943Sgshapiro		LogUsrErrs = savelogusrerrs;
3349132943Sgshapiro		if (response != NULL)
3350132943Sgshapiro			sm_free(response); /* XXX */
3351132943Sgshapiro	}
3352132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 3 */
3353132943Sgshapiro
335490792Sgshapiro	/* put back discard bit */
335590792Sgshapiro	if (smtp->sm_discard)
335690792Sgshapiro		e->e_flags |= EF_DISCARD;
335790792Sgshapiro
335890792Sgshapiro	/* check to see if we need to re-expand aliases */
335990792Sgshapiro	/* also reset QS_BADADDR on already-diagnosted addrs */
336090792Sgshapiro	doublequeue = false;
336190792Sgshapiro	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
336290792Sgshapiro	{
336390792Sgshapiro		if (QS_IS_VERIFIED(a->q_state) &&
336490792Sgshapiro		    !bitset(EF_DISCARD, e->e_flags))
336590792Sgshapiro		{
336690792Sgshapiro			/* need to re-expand aliases */
336790792Sgshapiro			doublequeue = true;
336890792Sgshapiro		}
336990792Sgshapiro		if (QS_IS_BADADDR(a->q_state))
337090792Sgshapiro		{
337190792Sgshapiro			/* make this "go away" */
337290792Sgshapiro			a->q_state = QS_DONTSEND;
337390792Sgshapiro		}
337490792Sgshapiro	}
337590792Sgshapiro
337690792Sgshapiro	/* collect the text of the message */
337790792Sgshapiro	SmtpPhase = "collect";
337890792Sgshapiro	buffer_errors();
337990792Sgshapiro
3380120256Sgshapiro	collect(InChannel, true, NULL, e, true);
338190792Sgshapiro
338290792Sgshapiro	/* redefine message size */
3383168515Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "%ld", e->e_msgsize);
338490792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
338590792Sgshapiro
338690792Sgshapiro	/* rscheck() will set Errors or EF_DISCARD if it trips */
3387102528Sgshapiro	(void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT,
3388168515Sgshapiro		       3, NULL, e->e_id, NULL);
338990792Sgshapiro
339090792Sgshapiro#if MILTER
339190792Sgshapiro	milteraccept = true;
339290792Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize &&
339390792Sgshapiro	    Errors <= 0 &&
339490792Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
339590792Sgshapiro	{
339690792Sgshapiro		char state;
339790792Sgshapiro		char *response;
339890792Sgshapiro
339990792Sgshapiro		response = milter_data(e, &state);
340090792Sgshapiro		switch (state)
340190792Sgshapiro		{
340290792Sgshapiro		  case SMFIR_REPLYCODE:
340390792Sgshapiro			if (MilterLogLevel > 3)
340490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
340590792Sgshapiro					  "Milter: data, reject=%s",
340690792Sgshapiro					  response);
340790792Sgshapiro			milteraccept = false;
340890792Sgshapiro			usrerr(response);
340990792Sgshapiro			break;
341090792Sgshapiro
341190792Sgshapiro		  case SMFIR_REJECT:
341290792Sgshapiro			milteraccept = false;
341390792Sgshapiro			if (MilterLogLevel > 3)
341490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
341590792Sgshapiro					  "Milter: data, reject=554 5.7.1 Command rejected");
341690792Sgshapiro			usrerr("554 5.7.1 Command rejected");
341790792Sgshapiro			break;
341890792Sgshapiro
341990792Sgshapiro		  case SMFIR_DISCARD:
342090792Sgshapiro			if (MilterLogLevel > 3)
342190792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
342290792Sgshapiro					  "Milter: data, discard");
342390792Sgshapiro			milteraccept = false;
342490792Sgshapiro			e->e_flags |= EF_DISCARD;
342590792Sgshapiro			break;
342690792Sgshapiro
342790792Sgshapiro		  case SMFIR_TEMPFAIL:
342890792Sgshapiro			if (MilterLogLevel > 3)
342990792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
343090792Sgshapiro					  "Milter: data, reject=%s",
343190792Sgshapiro					  MSG_TEMPFAIL);
343290792Sgshapiro			milteraccept = false;
343390792Sgshapiro			usrerr(MSG_TEMPFAIL);
343490792Sgshapiro			break;
3435157001Sgshapiro
3436157001Sgshapiro		  case SMFIR_SHUTDOWN:
3437157001Sgshapiro			if (MilterLogLevel > 3)
3438157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3439157001Sgshapiro					  "Milter: data, reject=421 4.7.0 %s closing connection",
3440157001Sgshapiro					  MyHostName);
3441157001Sgshapiro			milteraccept = false;
3442157001Sgshapiro			usrerr("421 4.7.0 %s closing connection", MyHostName);
3443157001Sgshapiro			rv = false;
3444157001Sgshapiro			break;
344590792Sgshapiro		}
344690792Sgshapiro		if (response != NULL)
344790792Sgshapiro			sm_free(response);
344890792Sgshapiro	}
344990792Sgshapiro
345090792Sgshapiro	/* Milter may have changed message size */
3451168515Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "%ld", e->e_msgsize);
345290792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
345390792Sgshapiro
345490792Sgshapiro	/* abort message filters that didn't get the body & log msg is OK */
345590792Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize)
345690792Sgshapiro	{
345790792Sgshapiro		milter_abort(e);
345890792Sgshapiro		if (milteraccept && MilterLogLevel > 9)
345990792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
346090792Sgshapiro	}
3461132943Sgshapiro
3462132943Sgshapiro	/*
3463132943Sgshapiro	**  If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or
3464132943Sgshapiro	**  milter accepted message, sync it now
3465132943Sgshapiro	**
3466132943Sgshapiro	**  XXX This is almost a copy of the code in collect(): put it into
3467132943Sgshapiro	**	a function that is called from both places?
3468132943Sgshapiro	*/
3469132943Sgshapiro
3470132943Sgshapiro	if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER)
3471132943Sgshapiro	{
3472132943Sgshapiro		int afd;
3473132943Sgshapiro		SM_FILE_T *volatile df;
3474132943Sgshapiro		char *dfname;
3475132943Sgshapiro
3476132943Sgshapiro		df = e->e_dfp;
3477132943Sgshapiro		dfname = queuename(e, DATAFL_LETTER);
3478132943Sgshapiro		if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0
3479132943Sgshapiro		    && errno != EINVAL)
3480132943Sgshapiro		{
3481132943Sgshapiro			int save_errno;
3482132943Sgshapiro
3483132943Sgshapiro			save_errno = errno;
3484132943Sgshapiro			if (save_errno == EEXIST)
3485132943Sgshapiro			{
3486132943Sgshapiro				struct stat st;
3487132943Sgshapiro				int dfd;
3488132943Sgshapiro
3489132943Sgshapiro				if (stat(dfname, &st) < 0)
3490132943Sgshapiro					st.st_size = -1;
3491132943Sgshapiro				errno = EEXIST;
3492132943Sgshapiro				syserr("@collect: bfcommit(%s): already on disk, size=%ld",
3493132943Sgshapiro				       dfname, (long) st.st_size);
3494132943Sgshapiro				dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
3495132943Sgshapiro				if (dfd >= 0)
3496132943Sgshapiro					dumpfd(dfd, true, true);
3497132943Sgshapiro			}
3498132943Sgshapiro			errno = save_errno;
3499132943Sgshapiro			dferror(df, "bfcommit", e);
3500132943Sgshapiro			flush_errors(true);
3501132943Sgshapiro			finis(save_errno != EEXIST, true, ExitStat);
3502132943Sgshapiro		}
3503132943Sgshapiro		else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
3504132943Sgshapiro		{
3505132943Sgshapiro			dferror(df, "sm_io_getinfo", e);
3506132943Sgshapiro			flush_errors(true);
3507132943Sgshapiro			finis(true, true, ExitStat);
3508132943Sgshapiro			/* NOTREACHED */
3509132943Sgshapiro		}
3510132943Sgshapiro		else if (fsync(afd) < 0)
3511132943Sgshapiro		{
3512132943Sgshapiro			dferror(df, "fsync", e);
3513132943Sgshapiro			flush_errors(true);
3514132943Sgshapiro			finis(true, true, ExitStat);
3515132943Sgshapiro			/* NOTREACHED */
3516132943Sgshapiro		}
3517132943Sgshapiro		else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
3518132943Sgshapiro		{
3519132943Sgshapiro			dferror(df, "sm_io_close", e);
3520132943Sgshapiro			flush_errors(true);
3521132943Sgshapiro			finis(true, true, ExitStat);
3522132943Sgshapiro			/* NOTREACHED */
3523132943Sgshapiro		}
3524132943Sgshapiro
3525132943Sgshapiro		/* Now reopen the df file */
3526132943Sgshapiro		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
3527132943Sgshapiro					SM_IO_RDONLY, NULL);
3528132943Sgshapiro		if (e->e_dfp == NULL)
3529132943Sgshapiro		{
3530132943Sgshapiro			/* we haven't acked receipt yet, so just chuck this */
3531132943Sgshapiro			syserr("@Cannot reopen %s", dfname);
3532132943Sgshapiro			finis(true, true, ExitStat);
3533132943Sgshapiro			/* NOTREACHED */
3534132943Sgshapiro		}
3535132943Sgshapiro	}
353690792Sgshapiro#endif /* MILTER */
353790792Sgshapiro
353890792Sgshapiro	/* Check if quarantining stats should be updated */
353990792Sgshapiro	if (e->e_quarmsg != NULL)
354090792Sgshapiro		markstats(e, NULL, STATS_QUARANTINE);
354190792Sgshapiro
354290792Sgshapiro	/*
354390792Sgshapiro	**  If a header/body check (header checks or milter)
354490792Sgshapiro	**  set EF_DISCARD, don't queueup the message --
354590792Sgshapiro	**  that would lose the EF_DISCARD bit and deliver
354690792Sgshapiro	**  the message.
354790792Sgshapiro	*/
354890792Sgshapiro
354990792Sgshapiro	if (bitset(EF_DISCARD, e->e_flags))
355090792Sgshapiro		doublequeue = false;
355190792Sgshapiro
355290792Sgshapiro	aborting = Errors > 0;
3553125820Sgshapiro	if (!(aborting || bitset(EF_DISCARD, e->e_flags)) &&
355490792Sgshapiro	    (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
355590792Sgshapiro	    !split_by_recipient(e))
355690792Sgshapiro		aborting = bitset(EF_FATALERRS, e->e_flags);
355790792Sgshapiro
355890792Sgshapiro	if (aborting)
355990792Sgshapiro	{
356090792Sgshapiro		/* Log who the mail would have gone to */
356190792Sgshapiro		logundelrcpts(e, e->e_message, 8, false);
356290792Sgshapiro		flush_errors(true);
356390792Sgshapiro		buffer_errors();
356490792Sgshapiro		goto abortmessage;
356590792Sgshapiro	}
356690792Sgshapiro
356790792Sgshapiro	/* from now on, we have to operate silently */
356890792Sgshapiro	buffer_errors();
356990792Sgshapiro
357090792Sgshapiro#if 0
357190792Sgshapiro	/*
357290792Sgshapiro	**  Clear message, it may contain an error from the SMTP dialogue.
357390792Sgshapiro	**  This error must not show up in the queue.
357490792Sgshapiro	**	Some error message should show up, e.g., alias database
357590792Sgshapiro	**	not available, but others shouldn't, e.g., from check_rcpt.
357690792Sgshapiro	*/
357790792Sgshapiro
357890792Sgshapiro	e->e_message = NULL;
357990792Sgshapiro#endif /* 0 */
358090792Sgshapiro
358190792Sgshapiro	/*
358290792Sgshapiro	**  Arrange to send to everyone.
358390792Sgshapiro	**	If sending to multiple people, mail back
358490792Sgshapiro	**		errors rather than reporting directly.
358590792Sgshapiro	**	In any case, don't mail back errors for
358690792Sgshapiro	**		anything that has happened up to
358790792Sgshapiro	**		now (the other end will do this).
358890792Sgshapiro	**	Truncate our transcript -- the mail has gotten
358990792Sgshapiro	**		to us successfully, and if we have
359090792Sgshapiro	**		to mail this back, it will be easier
359190792Sgshapiro	**		on the reader.
359290792Sgshapiro	**	Then send to everyone.
359390792Sgshapiro	**	Finally give a reply code.  If an error has
359490792Sgshapiro	**		already been given, don't mail a
359590792Sgshapiro	**		message back.
359690792Sgshapiro	**	We goose error returns by clearing error bit.
359790792Sgshapiro	*/
359890792Sgshapiro
359990792Sgshapiro	SmtpPhase = "delivery";
360090792Sgshapiro	(void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
360190792Sgshapiro	id = e->e_id;
360290792Sgshapiro
360390792Sgshapiro#if NAMED_BIND
360490792Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
360590792Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
360690792Sgshapiro#endif /* NAMED_BIND */
360790792Sgshapiro
360890792Sgshapiro	for (ee = e; ee != NULL; ee = ee->e_sibling)
360990792Sgshapiro	{
361090792Sgshapiro		/* make sure we actually do delivery */
361190792Sgshapiro		ee->e_flags &= ~EF_CLRQUEUE;
361290792Sgshapiro
361390792Sgshapiro		/* from now on, operate silently */
361490792Sgshapiro		ee->e_errormode = EM_MAIL;
361590792Sgshapiro
361690792Sgshapiro		if (doublequeue)
361790792Sgshapiro		{
361890792Sgshapiro			/* make sure it is in the queue */
361990792Sgshapiro			queueup(ee, false, true);
362090792Sgshapiro		}
362190792Sgshapiro		else
362290792Sgshapiro		{
3623157001Sgshapiro			int mode;
3624157001Sgshapiro
362590792Sgshapiro			/* send to all recipients */
3626157001Sgshapiro			mode = SM_DEFAULT;
3627157001Sgshapiro#if _FFR_DM_ONE
3628157001Sgshapiro			if (SM_DM_ONE == e->e_sendmode)
3629157001Sgshapiro			{
3630157001Sgshapiro				if (NotFirstDelivery)
3631157001Sgshapiro				{
3632157001Sgshapiro					mode = SM_QUEUE;
3633157001Sgshapiro					e->e_sendmode = SM_QUEUE;
3634157001Sgshapiro				}
3635157001Sgshapiro				else
3636157001Sgshapiro				{
3637157001Sgshapiro					mode = SM_FORK;
3638157001Sgshapiro					NotFirstDelivery = true;
3639157001Sgshapiro				}
3640157001Sgshapiro			}
3641157001Sgshapiro#endif /* _FFR_DM_ONE */
3642157001Sgshapiro			sendall(ee, mode);
364390792Sgshapiro		}
364490792Sgshapiro		ee->e_to = NULL;
364590792Sgshapiro	}
364690792Sgshapiro
364798121Sgshapiro	/* put back id for SMTP logging in putoutmsg() */
364898121Sgshapiro	oldid = CurEnv->e_id;
364998121Sgshapiro	CurEnv->e_id = id;
365098121Sgshapiro
365190792Sgshapiro	/* issue success message */
3652157001Sgshapiro#if _FFR_MSG_ACCEPT
3653157001Sgshapiro	if (MessageAccept != NULL && *MessageAccept != '\0')
3654157001Sgshapiro	{
3655157001Sgshapiro		char msg[MAXLINE];
3656157001Sgshapiro
3657168515Sgshapiro		expand(MessageAccept, msg, sizeof(msg), e);
3658157001Sgshapiro		message("250 2.0.0 %s", msg);
3659157001Sgshapiro	}
3660157001Sgshapiro	else
3661157001Sgshapiro#endif /* _FFR_MSG_ACCEPT */
366290792Sgshapiro	message("250 2.0.0 %s Message accepted for delivery", id);
366398121Sgshapiro	CurEnv->e_id = oldid;
366490792Sgshapiro
366590792Sgshapiro	/* if we just queued, poke it */
366690792Sgshapiro	if (doublequeue)
366790792Sgshapiro	{
366890792Sgshapiro		bool anything_to_send = false;
366990792Sgshapiro
367090792Sgshapiro		sm_getla();
367190792Sgshapiro		for (ee = e; ee != NULL; ee = ee->e_sibling)
367290792Sgshapiro		{
367390792Sgshapiro			if (WILL_BE_QUEUED(ee->e_sendmode))
367490792Sgshapiro				continue;
367590792Sgshapiro			if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
367690792Sgshapiro			{
367790792Sgshapiro				ee->e_sendmode = SM_QUEUE;
367890792Sgshapiro				continue;
367990792Sgshapiro			}
368090792Sgshapiro			else if (QueueMode != QM_QUARANTINE &&
368190792Sgshapiro				 ee->e_quarmsg != NULL)
368290792Sgshapiro			{
368390792Sgshapiro				ee->e_sendmode = SM_QUEUE;
368490792Sgshapiro				continue;
368590792Sgshapiro			}
368690792Sgshapiro			anything_to_send = true;
368790792Sgshapiro
368890792Sgshapiro			/* close all the queue files */
368990792Sgshapiro			closexscript(ee);
369090792Sgshapiro			if (ee->e_dfp != NULL)
369190792Sgshapiro			{
369290792Sgshapiro				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
369390792Sgshapiro				ee->e_dfp = NULL;
369490792Sgshapiro			}
369590792Sgshapiro			unlockqueue(ee);
369690792Sgshapiro		}
369790792Sgshapiro		if (anything_to_send)
369890792Sgshapiro		{
369990792Sgshapiro#if PIPELINING
370090792Sgshapiro			/*
370190792Sgshapiro			**  XXX if we don't do this, we get 250 twice
370290792Sgshapiro			**	because it is also flushed in the child.
370390792Sgshapiro			*/
370490792Sgshapiro
370590792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
370690792Sgshapiro#endif /* PIPELINING */
370790792Sgshapiro			(void) doworklist(e, true, true);
370890792Sgshapiro		}
370990792Sgshapiro	}
371090792Sgshapiro
371190792Sgshapiro  abortmessage:
371290792Sgshapiro	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
371390792Sgshapiro		logsender(e, NULL);
371490792Sgshapiro	e->e_flags &= ~EF_LOGSENDER;
371590792Sgshapiro
371690792Sgshapiro	/* clean up a bit */
371790792Sgshapiro	smtp->sm_gotmail = false;
371890792Sgshapiro
371990792Sgshapiro	/*
372090792Sgshapiro	**  Call dropenvelope if and only if the envelope is *not*
372190792Sgshapiro	**  being processed by the child process forked by doworklist().
372290792Sgshapiro	*/
372390792Sgshapiro
372490792Sgshapiro	if (aborting || bitset(EF_DISCARD, e->e_flags))
372590792Sgshapiro		dropenvelope(e, true, false);
372690792Sgshapiro	else
372790792Sgshapiro	{
372890792Sgshapiro		for (ee = e; ee != NULL; ee = ee->e_sibling)
372990792Sgshapiro		{
373090792Sgshapiro			if (!doublequeue &&
373190792Sgshapiro			    QueueMode != QM_QUARANTINE &&
373290792Sgshapiro			    ee->e_quarmsg != NULL)
373390792Sgshapiro			{
373490792Sgshapiro				dropenvelope(ee, true, false);
373590792Sgshapiro				continue;
373690792Sgshapiro			}
373790792Sgshapiro			if (WILL_BE_QUEUED(ee->e_sendmode))
373890792Sgshapiro				dropenvelope(ee, true, false);
373990792Sgshapiro		}
374090792Sgshapiro	}
374190792Sgshapiro	sm_rpool_free(e->e_rpool);
374290792Sgshapiro
374390792Sgshapiro	/*
374490792Sgshapiro	**  At this point, e == &MainEnvelope, but if we did splitting,
374590792Sgshapiro	**  then CurEnv may point to an envelope structure that was just
374690792Sgshapiro	**  freed with the rpool.  So reset CurEnv *before* calling
374790792Sgshapiro	**  newenvelope.
374890792Sgshapiro	*/
374990792Sgshapiro
375090792Sgshapiro	CurEnv = e;
3751168515Sgshapiro	features = e->e_features;
375290792Sgshapiro	newenvelope(e, e, sm_rpool_new_x(NULL));
375390792Sgshapiro	e->e_flags = BlankEnvelope.e_flags;
3754168515Sgshapiro	e->e_features = features;
375590792Sgshapiro
375690792Sgshapiro	/* restore connection quarantining */
375790792Sgshapiro	if (smtp->sm_quarmsg == NULL)
375890792Sgshapiro	{
375990792Sgshapiro		e->e_quarmsg = NULL;
376090792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
376190792Sgshapiro	}
376290792Sgshapiro	else
376390792Sgshapiro	{
376490792Sgshapiro		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
376590792Sgshapiro		macdefine(&e->e_macro, A_PERM,
376690792Sgshapiro			  macid("{quarantine}"), e->e_quarmsg);
376790792Sgshapiro	}
3768157001Sgshapiro	return rv;
376938032Speter}
377090792Sgshapiro/*
377190792Sgshapiro**  LOGUNDELRCPTS -- log undelivered (or all) recipients.
377290792Sgshapiro**
377390792Sgshapiro**	Parameters:
377490792Sgshapiro**		e -- envelope.
377590792Sgshapiro**		msg -- message for Stat=
377690792Sgshapiro**		level -- log level.
377790792Sgshapiro**		all -- log all recipients.
377890792Sgshapiro**
377990792Sgshapiro**	Returns:
378090792Sgshapiro**		none.
378190792Sgshapiro**
378290792Sgshapiro**	Side Effects:
378390792Sgshapiro**		logs undelivered (or all) recipients
378490792Sgshapiro*/
378590792Sgshapiro
378690792Sgshapirovoid
378790792Sgshapirologundelrcpts(e, msg, level, all)
378890792Sgshapiro	ENVELOPE *e;
378990792Sgshapiro	char *msg;
379090792Sgshapiro	int level;
379190792Sgshapiro	bool all;
379290792Sgshapiro{
379390792Sgshapiro	ADDRESS *a;
379490792Sgshapiro
379590792Sgshapiro	if (LogLevel <= level || msg == NULL || *msg == '\0')
379690792Sgshapiro		return;
379790792Sgshapiro
379890792Sgshapiro	/* Clear $h so relay= doesn't get mislogged by logdelivery() */
379990792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', NULL);
380090792Sgshapiro
380190792Sgshapiro	/* Log who the mail would have gone to */
380290792Sgshapiro	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
380390792Sgshapiro	{
380490792Sgshapiro		if (!QS_IS_UNDELIVERED(a->q_state) && !all)
380590792Sgshapiro			continue;
380690792Sgshapiro		e->e_to = a->q_paddr;
380790792Sgshapiro		logdelivery(NULL, NULL, a->q_status, msg, NULL,
380890792Sgshapiro			    (time_t) 0, e);
380990792Sgshapiro	}
381090792Sgshapiro	e->e_to = NULL;
381190792Sgshapiro}
381290792Sgshapiro/*
381338032Speter**  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
381438032Speter**
381538032Speter**	Parameters:
381638032Speter**		pcounter -- pointer to a counter for this command.
381738032Speter**		maxcount -- maximum value for this counter before we
381838032Speter**			slow down.
381964562Sgshapiro**		waitnow -- sleep now (in this routine)?
382038032Speter**		cname -- command name for logging.
382138032Speter**		e -- the current envelope.
382238032Speter**
382338032Speter**	Returns:
3824132943Sgshapiro**		time to wait,
3825132943Sgshapiro**		STOP_ATTACK if twice as many commands as allowed and
3826132943Sgshapiro**			MaxChildren > 0.
382738032Speter**
382838032Speter**	Side Effects:
382938032Speter**		Slows down if we seem to be under attack.
383038032Speter*/
383138032Speter
383264562Sgshapirostatic time_t
383364562Sgshapirochecksmtpattack(pcounter, maxcount, waitnow, cname, e)
383490792Sgshapiro	volatile unsigned int *pcounter;
3835132943Sgshapiro	unsigned int maxcount;
383664562Sgshapiro	bool waitnow;
383738032Speter	char *cname;
383838032Speter	ENVELOPE *e;
383938032Speter{
384090792Sgshapiro	if (maxcount <= 0)	/* no limit */
384190792Sgshapiro		return (time_t) 0;
384290792Sgshapiro
384338032Speter	if (++(*pcounter) >= maxcount)
384438032Speter	{
3845132943Sgshapiro		unsigned int shift;
384664562Sgshapiro		time_t s;
384764562Sgshapiro
384838032Speter		if (*pcounter == maxcount && LogLevel > 5)
384938032Speter		{
385038032Speter			sm_syslog(LOG_INFO, e->e_id,
3851110560Sgshapiro				  "%s: possible SMTP attack: command=%.40s, count=%u",
385277349Sgshapiro				  CurSmtpClient, cname, *pcounter);
385338032Speter		}
3854132943Sgshapiro		shift = *pcounter - maxcount;
3855132943Sgshapiro		s = 1 << shift;
3856132943Sgshapiro		if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0)
385764562Sgshapiro			s = MAXTIMEOUT;
385890792Sgshapiro
3859132943Sgshapiro#define IS_ATTACK(s)	((MaxChildren > 0 && *pcounter >= maxcount * 2)	\
3860132943Sgshapiro				? STOP_ATTACK : (time_t) s)
3861132943Sgshapiro
386264562Sgshapiro		/* sleep at least 1 second before returning */
386364562Sgshapiro		(void) sleep(*pcounter / maxcount);
386464562Sgshapiro		s -= *pcounter / maxcount;
3865132943Sgshapiro		if (s >= MAXTIMEOUT || s < 0)
3866132943Sgshapiro			s = MAXTIMEOUT;
3867132943Sgshapiro		if (waitnow && s > 0)
386864562Sgshapiro		{
386964562Sgshapiro			(void) sleep(s);
3870132943Sgshapiro			return IS_ATTACK(0);
387164562Sgshapiro		}
3872132943Sgshapiro		return IS_ATTACK(s);
387338032Speter	}
387490792Sgshapiro	return (time_t) 0;
387538032Speter}
387690792Sgshapiro/*
387790792Sgshapiro**  SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
387890792Sgshapiro**
387990792Sgshapiro**	Parameters:
388090792Sgshapiro**		none.
388190792Sgshapiro**
388290792Sgshapiro**	Returns:
388390792Sgshapiro**		nothing.
388490792Sgshapiro**
388590792Sgshapiro**	Side Effects:
388690792Sgshapiro**		may change I/O fd.
388790792Sgshapiro*/
388890792Sgshapiro
388990792Sgshapirostatic void
389090792Sgshapirosetup_smtpd_io()
389190792Sgshapiro{
389290792Sgshapiro	int inchfd, outchfd, outfd;
389390792Sgshapiro
389490792Sgshapiro	inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
389590792Sgshapiro	outchfd  = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
389690792Sgshapiro	outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
389790792Sgshapiro	if (outchfd != outfd)
389890792Sgshapiro	{
389990792Sgshapiro		/* arrange for debugging output to go to remote host */
390090792Sgshapiro		(void) dup2(outchfd, outfd);
390190792Sgshapiro	}
390290792Sgshapiro
390390792Sgshapiro	/*
390490792Sgshapiro	**  if InChannel and OutChannel are stdin/stdout
390590792Sgshapiro	**  and connected to ttys
390690792Sgshapiro	**  and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
390790792Sgshapiro	**  then "chain" them together.
390890792Sgshapiro	*/
390990792Sgshapiro
391090792Sgshapiro	if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
391190792Sgshapiro	    isatty(inchfd) && isatty(outchfd))
391290792Sgshapiro	{
391390792Sgshapiro		int inmode, outmode;
391490792Sgshapiro
391590792Sgshapiro		inmode = fcntl(inchfd, F_GETFL, 0);
391690792Sgshapiro		if (inmode == -1)
391790792Sgshapiro		{
391890792Sgshapiro			if (LogLevel > 11)
391990792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
392090792Sgshapiro					"fcntl(inchfd, F_GETFL) failed: %s",
392190792Sgshapiro					sm_errstring(errno));
392290792Sgshapiro			return;
392390792Sgshapiro		}
392490792Sgshapiro		outmode = fcntl(outchfd, F_GETFL, 0);
392590792Sgshapiro		if (outmode == -1)
392690792Sgshapiro		{
392790792Sgshapiro			if (LogLevel > 11)
392890792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
392990792Sgshapiro					"fcntl(outchfd, F_GETFL) failed: %s",
393090792Sgshapiro					sm_errstring(errno));
393190792Sgshapiro			return;
393290792Sgshapiro		}
393390792Sgshapiro		if (bitset(O_NONBLOCK, inmode) ||
393490792Sgshapiro		    bitset(O_NONBLOCK, outmode) ||
393590792Sgshapiro		    fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
393690792Sgshapiro			return;
393790792Sgshapiro		outmode = fcntl(outchfd, F_GETFL, 0);
393890792Sgshapiro		if (outmode != -1 && bitset(O_NONBLOCK, outmode))
393990792Sgshapiro		{
394090792Sgshapiro			/* changing InChannel also changes OutChannel */
394190792Sgshapiro			sm_io_automode(OutChannel, InChannel);
394290792Sgshapiro			if (tTd(97, 4) && LogLevel > 9)
394390792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
394490792Sgshapiro					  "set automode for I (%d)/O (%d) in SMTP server",
394590792Sgshapiro					  inchfd, outchfd);
394690792Sgshapiro		}
394790792Sgshapiro
394890792Sgshapiro		/* undo change of inchfd */
394990792Sgshapiro		(void) fcntl(inchfd, F_SETFL, inmode);
395090792Sgshapiro	}
395190792Sgshapiro}
395290792Sgshapiro/*
395338032Speter**  SKIPWORD -- skip a fixed word.
395438032Speter**
395538032Speter**	Parameters:
395638032Speter**		p -- place to start looking.
395738032Speter**		w -- word to skip.
395838032Speter**
395938032Speter**	Returns:
396038032Speter**		p following w.
396138032Speter**		NULL on error.
396238032Speter**
396338032Speter**	Side Effects:
396438032Speter**		clobbers the p data area.
396538032Speter*/
396638032Speter
396738032Speterstatic char *
396838032Speterskipword(p, w)
396938032Speter	register char *volatile p;
397038032Speter	char *w;
397138032Speter{
397238032Speter	register char *q;
397338032Speter	char *firstp = p;
397438032Speter
397538032Speter	/* find beginning of word */
397690792Sgshapiro	SKIP_SPACE(p);
397738032Speter	q = p;
397838032Speter
397938032Speter	/* find end of word */
398038032Speter	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
398138032Speter		p++;
398238032Speter	while (isascii(*p) && isspace(*p))
398338032Speter		*p++ = '\0';
398438032Speter	if (*p != ':')
398538032Speter	{
398638032Speter	  syntax:
398764562Sgshapiro		usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
398838032Speter			shortenstring(firstp, MAXSHORTSTR));
398964562Sgshapiro		return NULL;
399038032Speter	}
399138032Speter	*p++ = '\0';
399290792Sgshapiro	SKIP_SPACE(p);
399338032Speter
399438032Speter	if (*p == '\0')
399538032Speter		goto syntax;
399638032Speter
399738032Speter	/* see if the input word matches desired word */
399890792Sgshapiro	if (sm_strcasecmp(q, w))
399938032Speter		goto syntax;
400038032Speter
400164562Sgshapiro	return p;
400238032Speter}
4003159609Sgshapiro
400490792Sgshapiro/*
4005168515Sgshapiro**  RESET_MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
4006168515Sgshapiro**
4007168515Sgshapiro**	Parameters:
4008168515Sgshapiro**		e -- the envelope.
4009168515Sgshapiro**
4010168515Sgshapiro**	Returns:
4011168515Sgshapiro**		none.
4012168515Sgshapiro*/
4013168515Sgshapiro
4014168515Sgshapirovoid
4015168515Sgshapiroreset_mail_esmtp_args(e)
4016168515Sgshapiro	ENVELOPE *e;
4017168515Sgshapiro{
4018168515Sgshapiro	/* "size": no reset */
4019168515Sgshapiro
4020168515Sgshapiro	/* "body" */
4021168515Sgshapiro	SevenBitInput = SevenBitInput_Saved;
4022168515Sgshapiro	e->e_bodytype = NULL;
4023168515Sgshapiro
4024168515Sgshapiro	/* "envid" */
4025168515Sgshapiro	e->e_envid = NULL;
4026168515Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{dsn_envid}"), NULL);
4027168515Sgshapiro
4028168515Sgshapiro	/* "ret" */
4029168515Sgshapiro	e->e_flags &= EF_RET_PARAM;
4030168515Sgshapiro	e->e_flags &= EF_NO_BODY_RETN;
4031168515Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), NULL);
4032168515Sgshapiro
4033168515Sgshapiro#if SASL
4034168515Sgshapiro	/* "auth" */
4035168515Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), NULL);
4036168515Sgshapiro	e->e_auth_param = "";
4037168515Sgshapiro# if _FFR_AUTH_PASSING
4038168515Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_PERM,
4039168515Sgshapiro				  macid("{auth_author}"), NULL);
4040168515Sgshapiro# endif /* _FFR_AUTH_PASSING */
4041168515Sgshapiro#endif /* SASL */
4042168515Sgshapiro
4043168515Sgshapiro	/* "by" */
4044168515Sgshapiro	e->e_deliver_by = 0;
4045168515Sgshapiro	e->e_dlvr_flag = 0;
4046168515Sgshapiro}
4047168515Sgshapiro
4048168515Sgshapiro/*
404938032Speter**  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
405038032Speter**
405138032Speter**	Parameters:
4052168515Sgshapiro**		a -- address (unused, for compatibility with rcpt_esmtp_args)
405338032Speter**		kp -- the parameter key.
405438032Speter**		vp -- the value of that parameter.
405538032Speter**		e -- the envelope.
405638032Speter**
405738032Speter**	Returns:
405838032Speter**		none.
405938032Speter*/
406038032Speter
4061168515Sgshapirovoid
4062168515Sgshapiromail_esmtp_args(a, kp, vp, e)
4063168515Sgshapiro	ADDRESS *a;
406438032Speter	char *kp;
406538032Speter	char *vp;
406638032Speter	ENVELOPE *e;
406738032Speter{
406890792Sgshapiro	if (sm_strcasecmp(kp, "size") == 0)
406938032Speter	{
407038032Speter		if (vp == NULL)
407138032Speter		{
407264562Sgshapiro			usrerr("501 5.5.2 SIZE requires a value");
407338032Speter			/* NOTREACHED */
407438032Speter		}
407590792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
407690792Sgshapiro		errno = 0;
407771345Sgshapiro		e->e_msgsize = strtol(vp, (char **) NULL, 10);
407866494Sgshapiro		if (e->e_msgsize == LONG_MAX && errno == ERANGE)
407966494Sgshapiro		{
408066494Sgshapiro			usrerr("552 5.2.3 Message size exceeds maximum value");
408166494Sgshapiro			/* NOTREACHED */
408266494Sgshapiro		}
408390792Sgshapiro		if (e->e_msgsize < 0)
408490792Sgshapiro		{
408590792Sgshapiro			usrerr("552 5.2.3 Message size invalid");
408690792Sgshapiro			/* NOTREACHED */
408790792Sgshapiro		}
408838032Speter	}
408990792Sgshapiro	else if (sm_strcasecmp(kp, "body") == 0)
409038032Speter	{
409138032Speter		if (vp == NULL)
409238032Speter		{
409364562Sgshapiro			usrerr("501 5.5.2 BODY requires a value");
409438032Speter			/* NOTREACHED */
409538032Speter		}
409690792Sgshapiro		else if (sm_strcasecmp(vp, "8bitmime") == 0)
409738032Speter		{
409890792Sgshapiro			SevenBitInput = false;
409938032Speter		}
410090792Sgshapiro		else if (sm_strcasecmp(vp, "7bit") == 0)
410138032Speter		{
410290792Sgshapiro			SevenBitInput = true;
410338032Speter		}
410438032Speter		else
410538032Speter		{
410690792Sgshapiro			usrerr("501 5.5.4 Unknown BODY type %s", vp);
410738032Speter			/* NOTREACHED */
410838032Speter		}
410990792Sgshapiro		e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
411038032Speter	}
411190792Sgshapiro	else if (sm_strcasecmp(kp, "envid") == 0)
411238032Speter	{
4113168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
411464562Sgshapiro		{
411564562Sgshapiro			usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
411664562Sgshapiro			/* NOTREACHED */
411764562Sgshapiro		}
411838032Speter		if (vp == NULL)
411938032Speter		{
412064562Sgshapiro			usrerr("501 5.5.2 ENVID requires a value");
412138032Speter			/* NOTREACHED */
412238032Speter		}
412338032Speter		if (!xtextok(vp))
412438032Speter		{
412564562Sgshapiro			usrerr("501 5.5.4 Syntax error in ENVID parameter value");
412638032Speter			/* NOTREACHED */
412738032Speter		}
412838032Speter		if (e->e_envid != NULL)
412938032Speter		{
413064562Sgshapiro			usrerr("501 5.5.0 Duplicate ENVID parameter");
413138032Speter			/* NOTREACHED */
413238032Speter		}
413390792Sgshapiro		e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
413490792Sgshapiro		macdefine(&e->e_macro, A_PERM,
413590792Sgshapiro			macid("{dsn_envid}"), e->e_envid);
413638032Speter	}
413790792Sgshapiro	else if (sm_strcasecmp(kp, "ret") == 0)
413838032Speter	{
4139168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
414064562Sgshapiro		{
414164562Sgshapiro			usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
414264562Sgshapiro			/* NOTREACHED */
414364562Sgshapiro		}
414438032Speter		if (vp == NULL)
414538032Speter		{
414664562Sgshapiro			usrerr("501 5.5.2 RET requires a value");
414738032Speter			/* NOTREACHED */
414838032Speter		}
414938032Speter		if (bitset(EF_RET_PARAM, e->e_flags))
415038032Speter		{
415164562Sgshapiro			usrerr("501 5.5.0 Duplicate RET parameter");
415238032Speter			/* NOTREACHED */
415338032Speter		}
415438032Speter		e->e_flags |= EF_RET_PARAM;
415590792Sgshapiro		if (sm_strcasecmp(vp, "hdrs") == 0)
415638032Speter			e->e_flags |= EF_NO_BODY_RETN;
415790792Sgshapiro		else if (sm_strcasecmp(vp, "full") != 0)
415838032Speter		{
415964562Sgshapiro			usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
416038032Speter			/* NOTREACHED */
416138032Speter		}
416290792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
416338032Speter	}
416490792Sgshapiro#if SASL
416590792Sgshapiro	else if (sm_strcasecmp(kp, "auth") == 0)
416664562Sgshapiro	{
416764562Sgshapiro		int len;
416864562Sgshapiro		char *q;
416964562Sgshapiro		char *auth_param;	/* the value of the AUTH=x */
417064562Sgshapiro		bool saveQuickAbort = QuickAbort;
417164562Sgshapiro		bool saveSuprErrs = SuprErrs;
417290792Sgshapiro		bool saveExitStat = ExitStat;
417364562Sgshapiro
417464562Sgshapiro		if (vp == NULL)
417564562Sgshapiro		{
417664562Sgshapiro			usrerr("501 5.5.2 AUTH= requires a value");
417764562Sgshapiro			/* NOTREACHED */
417864562Sgshapiro		}
417964562Sgshapiro		if (e->e_auth_param != NULL)
418064562Sgshapiro		{
418164562Sgshapiro			usrerr("501 5.5.0 Duplicate AUTH parameter");
418264562Sgshapiro			/* NOTREACHED */
418364562Sgshapiro		}
418464562Sgshapiro		if ((q = strchr(vp, ' ')) != NULL)
418564562Sgshapiro			len = q - vp + 1;
418664562Sgshapiro		else
418764562Sgshapiro			len = strlen(vp) + 1;
418864562Sgshapiro		auth_param = xalloc(len);
418990792Sgshapiro		(void) sm_strlcpy(auth_param, vp, len);
419064562Sgshapiro		if (!xtextok(auth_param))
419164562Sgshapiro		{
419264562Sgshapiro			usrerr("501 5.5.4 Syntax error in AUTH parameter value");
419364562Sgshapiro			/* just a warning? */
419464562Sgshapiro			/* NOTREACHED */
419564562Sgshapiro		}
419664562Sgshapiro
419764562Sgshapiro		/* XXX define this always or only if trusted? */
4198132943Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"),
4199132943Sgshapiro			  auth_param);
420064562Sgshapiro
420164562Sgshapiro		/*
420264562Sgshapiro		**  call Strust_auth to find out whether
420364562Sgshapiro		**  auth_param is acceptable (trusted)
420464562Sgshapiro		**  we shouldn't trust it if not authenticated
420564562Sgshapiro		**  (required by RFC, leave it to ruleset?)
420664562Sgshapiro		*/
420764562Sgshapiro
420890792Sgshapiro		SuprErrs = true;
420990792Sgshapiro		QuickAbort = false;
421064562Sgshapiro		if (strcmp(auth_param, "<>") != 0 &&
4211132943Sgshapiro		     (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM,
4212168515Sgshapiro			      9, NULL, NOQID, NULL) != EX_OK || Errors > 0))
421364562Sgshapiro		{
421464562Sgshapiro			if (tTd(95, 8))
421564562Sgshapiro			{
421664562Sgshapiro				q = e->e_auth_param;
421790792Sgshapiro				sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
4218132943Sgshapiro					auth_param, (q == NULL) ? "" : q);
421964562Sgshapiro			}
422090792Sgshapiro
422164562Sgshapiro			/* not trusted */
422290792Sgshapiro			e->e_auth_param = "<>";
422390792Sgshapiro# if _FFR_AUTH_PASSING
422490792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
422590792Sgshapiro				  macid("{auth_author}"), NULL);
422690792Sgshapiro# endif /* _FFR_AUTH_PASSING */
422764562Sgshapiro		}
422864562Sgshapiro		else
422964562Sgshapiro		{
423064562Sgshapiro			if (tTd(95, 8))
4231132943Sgshapiro				sm_dprintf("auth=\"%.100s\" trusted\n", auth_param);
423290792Sgshapiro			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
423390792Sgshapiro							    auth_param);
423464562Sgshapiro		}
423590792Sgshapiro		sm_free(auth_param); /* XXX */
423677349Sgshapiro
423764562Sgshapiro		/* reset values */
423864562Sgshapiro		Errors = 0;
423964562Sgshapiro		QuickAbort = saveQuickAbort;
424064562Sgshapiro		SuprErrs = saveSuprErrs;
424190792Sgshapiro		ExitStat = saveExitStat;
424264562Sgshapiro	}
424390792Sgshapiro#endif /* SASL */
424490792Sgshapiro#define PRTCHAR(c)	((isascii(c) && isprint(c)) ? (c) : '?')
424590792Sgshapiro
424690792Sgshapiro	/*
424790792Sgshapiro	**  "by" is only accepted if DeliverByMin >= 0.
424890792Sgshapiro	**  We maybe could add this to the list of server_features.
424990792Sgshapiro	*/
425090792Sgshapiro
425190792Sgshapiro	else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
425290792Sgshapiro	{
425390792Sgshapiro		char *s;
425490792Sgshapiro
425590792Sgshapiro		if (vp == NULL)
425690792Sgshapiro		{
425790792Sgshapiro			usrerr("501 5.5.2 BY= requires a value");
425890792Sgshapiro			/* NOTREACHED */
425990792Sgshapiro		}
426090792Sgshapiro		errno = 0;
426190792Sgshapiro		e->e_deliver_by = strtol(vp, &s, 10);
426290792Sgshapiro		if (e->e_deliver_by == LONG_MIN ||
426390792Sgshapiro		    e->e_deliver_by == LONG_MAX ||
426490792Sgshapiro		    e->e_deliver_by > 999999999l ||
426590792Sgshapiro		    e->e_deliver_by < -999999999l)
426690792Sgshapiro		{
426790792Sgshapiro			usrerr("501 5.5.2 BY=%s out of range", vp);
426890792Sgshapiro			/* NOTREACHED */
426990792Sgshapiro		}
427090792Sgshapiro		if (s == NULL || *s != ';')
427190792Sgshapiro		{
427290792Sgshapiro			usrerr("501 5.5.2 BY= missing ';'");
427390792Sgshapiro			/* NOTREACHED */
427490792Sgshapiro		}
427590792Sgshapiro		e->e_dlvr_flag = 0;
427690792Sgshapiro		++s;	/* XXX: spaces allowed? */
427790792Sgshapiro		SKIP_SPACE(s);
427890792Sgshapiro		switch (tolower(*s))
427990792Sgshapiro		{
428090792Sgshapiro		  case 'n':
428190792Sgshapiro			e->e_dlvr_flag = DLVR_NOTIFY;
428290792Sgshapiro			break;
428390792Sgshapiro		  case 'r':
428490792Sgshapiro			e->e_dlvr_flag = DLVR_RETURN;
428590792Sgshapiro			if (e->e_deliver_by <= 0)
428690792Sgshapiro			{
428790792Sgshapiro				usrerr("501 5.5.4 mode R requires BY time > 0");
428890792Sgshapiro				/* NOTREACHED */
428990792Sgshapiro			}
429090792Sgshapiro			if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
429190792Sgshapiro			    e->e_deliver_by < DeliverByMin)
429290792Sgshapiro			{
429390792Sgshapiro				usrerr("555 5.5.2 time %ld less than %ld",
429490792Sgshapiro					e->e_deliver_by, (long) DeliverByMin);
429590792Sgshapiro				/* NOTREACHED */
429690792Sgshapiro			}
429790792Sgshapiro			break;
429890792Sgshapiro		  default:
429990792Sgshapiro			usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
430090792Sgshapiro			/* NOTREACHED */
430190792Sgshapiro		}
430290792Sgshapiro		++s;	/* XXX: spaces allowed? */
430390792Sgshapiro		SKIP_SPACE(s);
430490792Sgshapiro		switch (tolower(*s))
430590792Sgshapiro		{
430690792Sgshapiro		  case 't':
430790792Sgshapiro			e->e_dlvr_flag |= DLVR_TRACE;
430890792Sgshapiro			break;
430990792Sgshapiro		  case '\0':
431090792Sgshapiro			break;
431190792Sgshapiro		  default:
431290792Sgshapiro			usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
431390792Sgshapiro			/* NOTREACHED */
431490792Sgshapiro		}
431590792Sgshapiro
431690792Sgshapiro		/* XXX: check whether more characters follow? */
431790792Sgshapiro	}
431838032Speter	else
431938032Speter	{
432066494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
432138032Speter		/* NOTREACHED */
432238032Speter	}
432338032Speter}
4324168515Sgshapiro
432590792Sgshapiro/*
432638032Speter**  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
432738032Speter**
432838032Speter**	Parameters:
432938032Speter**		a -- the address corresponding to the To: parameter.
433038032Speter**		kp -- the parameter key.
433138032Speter**		vp -- the value of that parameter.
433238032Speter**		e -- the envelope.
433338032Speter**
433438032Speter**	Returns:
433538032Speter**		none.
433638032Speter*/
433738032Speter
4338168515Sgshapirovoid
4339168515Sgshapirorcpt_esmtp_args(a, kp, vp, e)
434038032Speter	ADDRESS *a;
434138032Speter	char *kp;
434238032Speter	char *vp;
434338032Speter	ENVELOPE *e;
434438032Speter{
434590792Sgshapiro	if (sm_strcasecmp(kp, "notify") == 0)
434638032Speter	{
434738032Speter		char *p;
434838032Speter
4349168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
435064562Sgshapiro		{
435164562Sgshapiro			usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
435264562Sgshapiro			/* NOTREACHED */
435364562Sgshapiro		}
435438032Speter		if (vp == NULL)
435538032Speter		{
435664562Sgshapiro			usrerr("501 5.5.2 NOTIFY requires a value");
435738032Speter			/* NOTREACHED */
435838032Speter		}
435938032Speter		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
436038032Speter		a->q_flags |= QHASNOTIFY;
436190792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
436264562Sgshapiro
436390792Sgshapiro		if (sm_strcasecmp(vp, "never") == 0)
436438032Speter			return;
436538032Speter		for (p = vp; p != NULL; vp = p)
436638032Speter		{
4367141858Sgshapiro			char *s;
4368141858Sgshapiro
4369141858Sgshapiro			s = p = strchr(p, ',');
437038032Speter			if (p != NULL)
437138032Speter				*p++ = '\0';
437290792Sgshapiro			if (sm_strcasecmp(vp, "success") == 0)
437338032Speter				a->q_flags |= QPINGONSUCCESS;
437490792Sgshapiro			else if (sm_strcasecmp(vp, "failure") == 0)
437538032Speter				a->q_flags |= QPINGONFAILURE;
437690792Sgshapiro			else if (sm_strcasecmp(vp, "delay") == 0)
437738032Speter				a->q_flags |= QPINGONDELAY;
437838032Speter			else
437938032Speter			{
438064562Sgshapiro				usrerr("501 5.5.4 Bad argument \"%s\"  to NOTIFY",
438138032Speter					vp);
438238032Speter				/* NOTREACHED */
438338032Speter			}
4384141858Sgshapiro			if (s != NULL)
4385141858Sgshapiro				*s = ',';
438638032Speter		}
438738032Speter	}
438890792Sgshapiro	else if (sm_strcasecmp(kp, "orcpt") == 0)
438938032Speter	{
4390168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
439164562Sgshapiro		{
439264562Sgshapiro			usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
439364562Sgshapiro			/* NOTREACHED */
439464562Sgshapiro		}
439538032Speter		if (vp == NULL)
439638032Speter		{
439764562Sgshapiro			usrerr("501 5.5.2 ORCPT requires a value");
439838032Speter			/* NOTREACHED */
439938032Speter		}
440038032Speter		if (strchr(vp, ';') == NULL || !xtextok(vp))
440138032Speter		{
440264562Sgshapiro			usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
440338032Speter			/* NOTREACHED */
440438032Speter		}
440538032Speter		if (a->q_orcpt != NULL)
440638032Speter		{
440764562Sgshapiro			usrerr("501 5.5.0 Duplicate ORCPT parameter");
440838032Speter			/* NOTREACHED */
440938032Speter		}
441090792Sgshapiro		a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
441138032Speter	}
441238032Speter	else
441338032Speter	{
441466494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
441538032Speter		/* NOTREACHED */
441638032Speter	}
441738032Speter}
441890792Sgshapiro/*
441938032Speter**  PRINTVRFYADDR -- print an entry in the verify queue
442038032Speter**
442138032Speter**	Parameters:
442290792Sgshapiro**		a -- the address to print.
442338032Speter**		last -- set if this is the last one.
442438032Speter**		vrfy -- set if this is a VRFY command.
442538032Speter**
442638032Speter**	Returns:
442738032Speter**		none.
442838032Speter**
442938032Speter**	Side Effects:
443038032Speter**		Prints the appropriate 250 codes.
443138032Speter*/
443264562Sgshapiro#define OFFF	(3 + 1 + 5 + 1)	/* offset in fmt: SMTP reply + enh. code */
443338032Speter
443464562Sgshapirostatic void
443538032Speterprintvrfyaddr(a, last, vrfy)
443638032Speter	register ADDRESS *a;
443738032Speter	bool last;
443838032Speter	bool vrfy;
443938032Speter{
444064562Sgshapiro	char fmtbuf[30];
444138032Speter
444238032Speter	if (vrfy && a->q_mailer != NULL &&
444338032Speter	    !bitnset(M_VRFY250, a->q_mailer->m_flags))
4444168515Sgshapiro		(void) sm_strlcpy(fmtbuf, "252", sizeof(fmtbuf));
444538032Speter	else
4446168515Sgshapiro		(void) sm_strlcpy(fmtbuf, "250", sizeof(fmtbuf));
444738032Speter	fmtbuf[3] = last ? ' ' : '-';
4448168515Sgshapiro	(void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof(fmtbuf) - 4);
444938032Speter	if (a->q_fullname == NULL)
445038032Speter	{
445164562Sgshapiro		if ((a->q_mailer == NULL ||
445264562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
445390792Sgshapiro		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
445464562Sgshapiro		    strchr(a->q_user, '@') == NULL)
445590792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
4456168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
445738032Speter		else
445890792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
4459168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
446038032Speter		message(fmtbuf, a->q_user, MyHostName);
446138032Speter	}
446238032Speter	else
446338032Speter	{
446464562Sgshapiro		if ((a->q_mailer == NULL ||
446564562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
446690792Sgshapiro		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
446764562Sgshapiro		    strchr(a->q_user, '@') == NULL)
446890792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
4469168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
447038032Speter		else
447190792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
4472168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
447338032Speter		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
447438032Speter	}
447538032Speter}
447638032Speter
447790792Sgshapiro#if SASL
447890792Sgshapiro/*
447964562Sgshapiro**  SASLMECHS -- get list of possible AUTH mechanisms
448064562Sgshapiro**
448164562Sgshapiro**	Parameters:
448290792Sgshapiro**		conn -- SASL connection info.
448390792Sgshapiro**		mechlist -- output parameter for list of mechanisms.
448464562Sgshapiro**
448564562Sgshapiro**	Returns:
448690792Sgshapiro**		number of mechs.
448764562Sgshapiro*/
448864562Sgshapiro
448964562Sgshapirostatic int
449064562Sgshapirosaslmechs(conn, mechlist)
449164562Sgshapiro	sasl_conn_t *conn;
449264562Sgshapiro	char **mechlist;
449364562Sgshapiro{
449464562Sgshapiro	int len, num, result;
449564562Sgshapiro
449664562Sgshapiro	/* "user" is currently unused */
449798121Sgshapiro# if SASL >= 20000
449898121Sgshapiro	result = sasl_listmech(conn, NULL,
449998121Sgshapiro			       "", " ", "", (const char **) mechlist,
4500120256Sgshapiro			       (unsigned int *)&len, &num);
450198121Sgshapiro# else /* SASL >= 20000 */
450264562Sgshapiro	result = sasl_listmech(conn, "user", /* XXX */
450364562Sgshapiro			       "", " ", "", mechlist,
450490792Sgshapiro			       (unsigned int *)&len, (unsigned int *)&num);
450598121Sgshapiro# endif /* SASL >= 20000 */
450690792Sgshapiro	if (result != SASL_OK)
450764562Sgshapiro	{
450890792Sgshapiro		if (LogLevel > 9)
450990792Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
451090792Sgshapiro				  "AUTH error: listmech=%d, num=%d",
451190792Sgshapiro				  result, num);
451290792Sgshapiro		num = 0;
451390792Sgshapiro	}
451490792Sgshapiro	if (num > 0)
451590792Sgshapiro	{
451664562Sgshapiro		if (LogLevel > 11)
451764562Sgshapiro			sm_syslog(LOG_INFO, NOQID,
451890792Sgshapiro				  "AUTH: available mech=%s, allowed mech=%s",
451964562Sgshapiro				  *mechlist, AuthMechanisms);
452090792Sgshapiro		*mechlist = intersect(AuthMechanisms, *mechlist, NULL);
452164562Sgshapiro	}
452264562Sgshapiro	else
452364562Sgshapiro	{
452490792Sgshapiro		*mechlist = NULL;	/* be paranoid... */
452590792Sgshapiro		if (result == SASL_OK && LogLevel > 9)
452664562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
452790792Sgshapiro				  "AUTH warning: no mechanisms");
452864562Sgshapiro	}
452964562Sgshapiro	return num;
453064562Sgshapiro}
453198121Sgshapiro
453298121Sgshapiro# if SASL >= 20000
453390792Sgshapiro/*
453464562Sgshapiro**  PROXY_POLICY -- define proxy policy for AUTH
453564562Sgshapiro**
453664562Sgshapiro**	Parameters:
453798121Sgshapiro**		conn -- unused.
453890792Sgshapiro**		context -- unused.
453998121Sgshapiro**		requested_user -- authorization identity.
454098121Sgshapiro**		rlen -- authorization identity length.
454190792Sgshapiro**		auth_identity -- authentication identity.
454298121Sgshapiro**		alen -- authentication identity length.
454398121Sgshapiro**		def_realm -- default user realm.
454498121Sgshapiro**		urlen -- user realm length.
454598121Sgshapiro**		propctx -- unused.
454698121Sgshapiro**
454798121Sgshapiro**	Returns:
454898121Sgshapiro**		ok?
454998121Sgshapiro**
455098121Sgshapiro**	Side Effects:
455198121Sgshapiro**		sets {auth_authen} macro.
455298121Sgshapiro*/
455398121Sgshapiro
455498121Sgshapiroint
455598121Sgshapiroproxy_policy(conn, context, requested_user, rlen, auth_identity, alen,
455698121Sgshapiro	     def_realm, urlen, propctx)
455798121Sgshapiro	sasl_conn_t *conn;
455898121Sgshapiro	void *context;
455998121Sgshapiro	const char *requested_user;
456098121Sgshapiro	unsigned rlen;
456198121Sgshapiro	const char *auth_identity;
456298121Sgshapiro	unsigned alen;
456398121Sgshapiro	const char *def_realm;
456498121Sgshapiro	unsigned urlen;
456598121Sgshapiro	struct propctx *propctx;
456698121Sgshapiro{
456798121Sgshapiro	if (auth_identity == NULL)
456898121Sgshapiro		return SASL_FAIL;
456998121Sgshapiro
457098121Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP,
457198121Sgshapiro		  macid("{auth_authen}"), (char *) auth_identity);
457298121Sgshapiro
457398121Sgshapiro	return SASL_OK;
457498121Sgshapiro}
457598121Sgshapiro# else /* SASL >= 20000 */
457698121Sgshapiro
457798121Sgshapiro/*
457898121Sgshapiro**  PROXY_POLICY -- define proxy policy for AUTH
457998121Sgshapiro**
458098121Sgshapiro**	Parameters:
458198121Sgshapiro**		context -- unused.
458298121Sgshapiro**		auth_identity -- authentication identity.
458390792Sgshapiro**		requested_user -- authorization identity.
458490792Sgshapiro**		user -- allowed user (output).
458590792Sgshapiro**		errstr -- possible error string (output).
458664562Sgshapiro**
458764562Sgshapiro**	Returns:
458864562Sgshapiro**		ok?
458964562Sgshapiro*/
459064562Sgshapiro
459164562Sgshapiroint
459264562Sgshapiroproxy_policy(context, auth_identity, requested_user, user, errstr)
459364562Sgshapiro	void *context;
459464562Sgshapiro	const char *auth_identity;
459564562Sgshapiro	const char *requested_user;
459664562Sgshapiro	const char **user;
459764562Sgshapiro	const char **errstr;
459864562Sgshapiro{
459964562Sgshapiro	if (user == NULL || auth_identity == NULL)
460064562Sgshapiro		return SASL_FAIL;
460164562Sgshapiro	*user = newstr(auth_identity);
460264562Sgshapiro	return SASL_OK;
460364562Sgshapiro}
460498121Sgshapiro# endif /* SASL >= 20000 */
460590792Sgshapiro#endif /* SASL */
460664562Sgshapiro
460790792Sgshapiro#if STARTTLS
460890792Sgshapiro/*
460990792Sgshapiro**  INITSRVTLS -- initialize server side TLS
461064562Sgshapiro**
461164562Sgshapiro**	Parameters:
461290792Sgshapiro**		tls_ok -- should tls initialization be done?
461364562Sgshapiro**
461464562Sgshapiro**	Returns:
461590792Sgshapiro**		succeeded?
461664562Sgshapiro**
461764562Sgshapiro**	Side Effects:
461890792Sgshapiro**		sets tls_ok_srv which is a static variable in this module.
461990792Sgshapiro**		Do NOT remove assignments to it!
462064562Sgshapiro*/
462164562Sgshapiro
462266494Sgshapirobool
462390792Sgshapiroinitsrvtls(tls_ok)
462490792Sgshapiro	bool tls_ok;
462564562Sgshapiro{
462690792Sgshapiro	if (!tls_ok)
462790792Sgshapiro		return false;
462864562Sgshapiro
462990792Sgshapiro	/* do NOT remove assignment */
4630110560Sgshapiro	tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCertFile,
4631110560Sgshapiro			     SrvKeyFile, CACertPath, CACertFile, DHParams);
463290792Sgshapiro	return tls_ok_srv;
463364562Sgshapiro}
463490792Sgshapiro#endif /* STARTTLS */
463564562Sgshapiro/*
463690792Sgshapiro**  SRVFEATURES -- get features for SMTP server
463764562Sgshapiro**
463864562Sgshapiro**	Parameters:
463990792Sgshapiro**		e -- envelope (should be session context).
464090792Sgshapiro**		clientname -- name of client.
464190792Sgshapiro**		features -- default features for this invocation.
464264562Sgshapiro**
464364562Sgshapiro**	Returns:
464490792Sgshapiro**		server features.
464564562Sgshapiro*/
464664562Sgshapiro
464790792Sgshapiro/* table with options: it uses just one character, how about strings? */
464890792Sgshapirostatic struct
464964562Sgshapiro{
465090792Sgshapiro	char		srvf_opt;
465190792Sgshapiro	unsigned int	srvf_flag;
465290792Sgshapiro} srv_feat_table[] =
465364562Sgshapiro{
465490792Sgshapiro	{ 'A',	SRV_OFFER_AUTH	},
4655132943Sgshapiro	{ 'B',	SRV_OFFER_VERB	},
4656132943Sgshapiro	{ 'C',	SRV_REQ_SEC	},
4657132943Sgshapiro	{ 'D',	SRV_OFFER_DSN	},
4658132943Sgshapiro	{ 'E',	SRV_OFFER_ETRN	},
4659132943Sgshapiro	{ 'L',	SRV_REQ_AUTH	},
466090792Sgshapiro#if PIPELINING
466190792Sgshapiro# if _FFR_NO_PIPE
466290792Sgshapiro	{ 'N',	SRV_NO_PIPE	},
466390792Sgshapiro# endif /* _FFR_NO_PIPE */
466490792Sgshapiro	{ 'P',	SRV_OFFER_PIPE	},
466590792Sgshapiro#endif /* PIPELINING */
4666132943Sgshapiro	{ 'R',	SRV_VRFY_CLT	},	/* same as V; not documented */
466790792Sgshapiro	{ 'S',	SRV_OFFER_TLS	},
466890792Sgshapiro/*	{ 'T',	SRV_TMP_FAIL	},	*/
466990792Sgshapiro	{ 'V',	SRV_VRFY_CLT	},
4670132943Sgshapiro	{ 'X',	SRV_OFFER_EXPN	},
467190792Sgshapiro/*	{ 'Y',	SRV_OFFER_VRFY	},	*/
467290792Sgshapiro	{ '\0',	SRV_NONE	}
467390792Sgshapiro};
467464562Sgshapiro
467590792Sgshapirostatic unsigned int
467690792Sgshapirosrvfeatures(e, clientname, features)
467790792Sgshapiro	ENVELOPE *e;
467890792Sgshapiro	char *clientname;
467990792Sgshapiro	unsigned int features;
468077349Sgshapiro{
468190792Sgshapiro	int r, i, j;
468290792Sgshapiro	char **pvp, c, opt;
468390792Sgshapiro	char pvpbuf[PSBUFSIZE];
468477349Sgshapiro
468590792Sgshapiro	pvp = NULL;
468690792Sgshapiro	r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
468790792Sgshapiro		  sizeof(pvpbuf));
468890792Sgshapiro	if (r != EX_OK)
468990792Sgshapiro		return features;
469090792Sgshapiro	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
469190792Sgshapiro		return features;
469290792Sgshapiro	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
469390792Sgshapiro		return SRV_TMP_FAIL;
469477349Sgshapiro
469564562Sgshapiro	/*
469690792Sgshapiro	**  General rule (see sendmail.h, d_flags):
469790792Sgshapiro	**  lower case: required/offered, upper case: Not required/available
469890792Sgshapiro	**
469990792Sgshapiro	**  Since we can change some features per daemon, we have both
470090792Sgshapiro	**  cases here: turn on/off a feature.
470164562Sgshapiro	*/
470264562Sgshapiro
470390792Sgshapiro	for (i = 1; pvp[i] != NULL; i++)
470464562Sgshapiro	{
470590792Sgshapiro		c = pvp[i][0];
470690792Sgshapiro		j = 0;
470790792Sgshapiro		for (;;)
470864562Sgshapiro		{
470990792Sgshapiro			if ((opt = srv_feat_table[j].srvf_opt) == '\0')
471064562Sgshapiro			{
471190792Sgshapiro				if (LogLevel > 9)
471290792Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
471390792Sgshapiro						  "srvfeatures: unknown feature %s",
471490792Sgshapiro						  pvp[i]);
471590792Sgshapiro				break;
471664562Sgshapiro			}
471790792Sgshapiro			if (c == opt)
471864562Sgshapiro			{
471990792Sgshapiro				features &= ~(srv_feat_table[j].srvf_flag);
472090792Sgshapiro				break;
472164562Sgshapiro			}
472290792Sgshapiro			if (c == tolower(opt))
472364562Sgshapiro			{
472490792Sgshapiro				features |= srv_feat_table[j].srvf_flag;
472590792Sgshapiro				break;
472664562Sgshapiro			}
472790792Sgshapiro			++j;
472864562Sgshapiro		}
472964562Sgshapiro	}
473090792Sgshapiro	return features;
473164562Sgshapiro}
473264562Sgshapiro
473390792Sgshapiro/*
473438032Speter**  HELP -- implement the HELP command.
473538032Speter**
473638032Speter**	Parameters:
473738032Speter**		topic -- the topic we want help for.
473890792Sgshapiro**		e -- envelope.
473938032Speter**
474038032Speter**	Returns:
474138032Speter**		none.
474238032Speter**
474338032Speter**	Side Effects:
474438032Speter**		outputs the help file to message output.
474538032Speter*/
474664562Sgshapiro#define HELPVSTR	"#vers	"
474764562Sgshapiro#define HELPVERSION	2
474838032Speter
474938032Spetervoid
475064562Sgshapirohelp(topic, e)
475138032Speter	char *topic;
475264562Sgshapiro	ENVELOPE *e;
475338032Speter{
475490792Sgshapiro	register SM_FILE_T *hf;
475564562Sgshapiro	register char *p;
475638032Speter	int len;
475738032Speter	bool noinfo;
475890792Sgshapiro	bool first = true;
475964562Sgshapiro	long sff = SFF_OPENASROOT|SFF_REGONLY;
476038032Speter	char buf[MAXLINE];
476164562Sgshapiro	char inp[MAXLINE];
476264562Sgshapiro	static int foundvers = -1;
476338032Speter	extern char Version[];
476438032Speter
476538032Speter	if (DontLockReadFiles)
476638032Speter		sff |= SFF_NOLOCK;
476764562Sgshapiro	if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
476838032Speter		sff |= SFF_SAFEDIRPATH;
476938032Speter
477038032Speter	if (HelpFile == NULL ||
477138032Speter	    (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
477238032Speter	{
477338032Speter		/* no help */
477438032Speter		errno = 0;
477564562Sgshapiro		message("502 5.3.0 Sendmail %s -- HELP not implemented",
477664562Sgshapiro			Version);
477738032Speter		return;
477838032Speter	}
477938032Speter
478038032Speter	if (topic == NULL || *topic == '\0')
478138032Speter	{
478238032Speter		topic = "smtp";
478390792Sgshapiro		noinfo = false;
478438032Speter	}
478538032Speter	else
478638032Speter	{
478738032Speter		makelower(topic);
478890792Sgshapiro		noinfo = true;
478938032Speter	}
479038032Speter
479138032Speter	len = strlen(topic);
479238032Speter
4793168515Sgshapiro	while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
479438032Speter	{
479564562Sgshapiro		if (buf[0] == '#')
479664562Sgshapiro		{
479764562Sgshapiro			if (foundvers < 0 &&
479864562Sgshapiro			    strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
479964562Sgshapiro			{
480064562Sgshapiro				int h;
480164562Sgshapiro
480290792Sgshapiro				if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
480390792Sgshapiro						 &h) == 1)
480464562Sgshapiro					foundvers = h;
480564562Sgshapiro			}
480664562Sgshapiro			continue;
480764562Sgshapiro		}
480838032Speter		if (strncmp(buf, topic, len) == 0)
480938032Speter		{
481064562Sgshapiro			if (first)
481164562Sgshapiro			{
481290792Sgshapiro				first = false;
481338032Speter
481464562Sgshapiro				/* print version if no/old vers# in file */
481564562Sgshapiro				if (foundvers < 2 && !noinfo)
481664562Sgshapiro					message("214-2.0.0 This is Sendmail version %s", Version);
481764562Sgshapiro			}
481864562Sgshapiro			p = strpbrk(buf, " \t");
481938032Speter			if (p == NULL)
482064562Sgshapiro				p = buf + strlen(buf) - 1;
482138032Speter			else
482238032Speter				p++;
482390792Sgshapiro			fixcrlf(p, true);
482464562Sgshapiro			if (foundvers >= 2)
482564562Sgshapiro			{
4826168515Sgshapiro				char *lbp;
4827168515Sgshapiro				int lbs = sizeof(buf) - (p - buf);
4828168515Sgshapiro
4829168515Sgshapiro				lbp = translate_dollars(p, p, &lbs);
4830168515Sgshapiro				expand(lbp, inp, sizeof(inp), e);
4831168515Sgshapiro				if (p != lbp)
4832168515Sgshapiro					sm_free(lbp);
483364562Sgshapiro				p = inp;
483464562Sgshapiro			}
483564562Sgshapiro			message("214-2.0.0 %s", p);
483690792Sgshapiro			noinfo = false;
483738032Speter		}
483838032Speter	}
483938032Speter
484038032Speter	if (noinfo)
484164562Sgshapiro		message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
484238032Speter	else
484364562Sgshapiro		message("214 2.0.0 End of HELP info");
484464562Sgshapiro
484564562Sgshapiro	if (foundvers != 0 && foundvers < HELPVERSION)
484664562Sgshapiro	{
484764562Sgshapiro		if (LogLevel > 1)
484864562Sgshapiro			sm_syslog(LOG_WARNING, e->e_id,
484964562Sgshapiro				  "%s too old (require version %d)",
485064562Sgshapiro				  HelpFile, HELPVERSION);
485164562Sgshapiro
485264562Sgshapiro		/* avoid log next time */
485364562Sgshapiro		foundvers = 0;
485464562Sgshapiro	}
485564562Sgshapiro
485690792Sgshapiro	(void) sm_io_close(hf, SM_TIME_DEFAULT);
485738032Speter}
4858120256Sgshapiro
4859120256Sgshapiro#if SASL
4860120256Sgshapiro/*
4861120256Sgshapiro**  RESET_SASLCONN -- reset SASL connection data
4862120256Sgshapiro**
4863120256Sgshapiro**	Parameters:
4864120256Sgshapiro**		conn -- SASL connection context
4865120256Sgshapiro**		hostname -- host name
4866120256Sgshapiro**		various connection data
4867120256Sgshapiro**
4868120256Sgshapiro**	Returns:
4869120256Sgshapiro**		SASL result
4870120256Sgshapiro*/
4871120256Sgshapiro
4872120256Sgshapirostatic int
4873147078Sgshapiroreset_saslconn(sasl_conn_t **conn, char *hostname,
4874120256Sgshapiro# if SASL >= 20000
4875120256Sgshapiro	       char *remoteip, char *localip,
4876120256Sgshapiro	       char *auth_id, sasl_ssf_t * ext_ssf)
4877120256Sgshapiro# else /* SASL >= 20000 */
4878147078Sgshapiro	       struct sockaddr_in *saddr_r, struct sockaddr_in *saddr_l,
4879120256Sgshapiro	       sasl_external_properties_t * ext_ssf)
4880120256Sgshapiro# endif /* SASL >= 20000 */
4881120256Sgshapiro{
4882120256Sgshapiro	int result;
4883120256Sgshapiro
4884120256Sgshapiro	sasl_dispose(conn);
4885120256Sgshapiro# if SASL >= 20000
4886120256Sgshapiro	result = sasl_server_new("smtp", hostname, NULL, NULL, NULL,
4887120256Sgshapiro				 NULL, 0, conn);
4888120256Sgshapiro# elif SASL > 10505
4889120256Sgshapiro	/* use empty realm: only works in SASL > 1.5.5 */
4890120256Sgshapiro	result = sasl_server_new("smtp", hostname, "", NULL, 0, conn);
4891120256Sgshapiro# else /* SASL >= 20000 */
4892120256Sgshapiro	/* use no realm -> realm is set to hostname by SASL lib */
4893120256Sgshapiro	result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
4894120256Sgshapiro				 conn);
4895120256Sgshapiro# endif /* SASL >= 20000 */
4896120256Sgshapiro	if (result != SASL_OK)
4897120256Sgshapiro		return result;
4898120256Sgshapiro
4899120256Sgshapiro# if SASL >= 20000
4900120256Sgshapiro#  if NETINET || NETINET6
4901147078Sgshapiro	if (remoteip != NULL && *remoteip != '\0')
4902120256Sgshapiro		result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip);
4903120256Sgshapiro	if (result != SASL_OK)
4904120256Sgshapiro		return result;
4905120256Sgshapiro
4906147078Sgshapiro	if (localip != NULL && *localip != '\0')
4907120256Sgshapiro		result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip);
4908120256Sgshapiro	if (result != SASL_OK)
4909120256Sgshapiro		return result;
4910120256Sgshapiro#  endif /* NETINET || NETINET6 */
4911120256Sgshapiro
4912120256Sgshapiro	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
4913120256Sgshapiro	if (result != SASL_OK)
4914120256Sgshapiro		return result;
4915120256Sgshapiro
4916120256Sgshapiro	result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id);
4917120256Sgshapiro	if (result != SASL_OK)
4918120256Sgshapiro		return result;
4919120256Sgshapiro# else /* SASL >= 20000 */
4920120256Sgshapiro#  if NETINET
4921120256Sgshapiro	if (saddr_r != NULL)
4922120256Sgshapiro		result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r);
4923120256Sgshapiro	if (result != SASL_OK)
4924120256Sgshapiro		return result;
4925120256Sgshapiro
4926120256Sgshapiro	if (saddr_l != NULL)
4927120256Sgshapiro		result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l);
4928120256Sgshapiro	if (result != SASL_OK)
4929120256Sgshapiro		return result;
4930120256Sgshapiro#  endif /* NETINET */
4931120256Sgshapiro
4932120256Sgshapiro	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
4933120256Sgshapiro	if (result != SASL_OK)
4934120256Sgshapiro		return result;
4935120256Sgshapiro# endif /* SASL >= 20000 */
4936120256Sgshapiro	return SASL_OK;
4937120256Sgshapiro}
4938120256Sgshapiro#endif /* SASL */
4939