138032Speter/*
2285229Sgshapiro * Copyright (c) 1998-2010, 2012-2014 Proofpoint, 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>
18363466Sgshapiro#endif
1964562Sgshapiro
20266527SgshapiroSM_RCSID("@(#)$Id: srvrsmtp.c,v 8.1016 2013-11-22 20:51:56 ca Exp $")
2164562Sgshapiro
22157001Sgshapiro#include <sm/time.h>
23132943Sgshapiro#include <sm/fdset.h>
24132943Sgshapiro
2590792Sgshapiro#if SASL || STARTTLS
26363466Sgshapiro# include <tls.h>
2790792Sgshapiro# include "sfsasl.h"
28363466Sgshapiro#endif
2990792Sgshapiro#if SASL
3090792Sgshapiro# define ENC64LEN(l)	(((l) + 2) * 4 / 3 + 1)
3164562Sgshapirostatic int saslmechs __P((sasl_conn_t *, char **));
32363466Sgshapiro#endif
3390792Sgshapiro#if STARTTLS
34285229Sgshapiro# include <openssl/err.h>
3590792Sgshapiro# include <sysexits.h>
3638032Speter
3790792Sgshapirostatic SSL_CTX	*srv_ctx = NULL;	/* TLS server context */
3890792Sgshapirostatic SSL	*srv_ssl = NULL;	/* per connection context */
39363466Sgshapirostatic tlsi_ctx_T tlsi_ctx;		/* TLS information context */
4038032Speter
4190792Sgshapirostatic bool	tls_ok_srv = false;
4290792Sgshapiro
4390792Sgshapiro# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
4490792Sgshapiro				bitset(SRV_VRFY_CLT, features))
4590792Sgshapiro#endif /* STARTTLS */
4690792Sgshapiro
47168515Sgshapiro#if _FFR_DM_ONE
48168515Sgshapirostatic bool	NotFirstDelivery = false;
49363466Sgshapiro#endif
50168515Sgshapiro
5190792Sgshapiro/* server features */
5290792Sgshapiro#define SRV_NONE	0x0000	/* none... */
5390792Sgshapiro#define SRV_OFFER_TLS	0x0001	/* offer STARTTLS */
5490792Sgshapiro#define SRV_VRFY_CLT	0x0002	/* request a cert */
5590792Sgshapiro#define SRV_OFFER_AUTH	0x0004	/* offer AUTH */
5690792Sgshapiro#define SRV_OFFER_ETRN	0x0008	/* offer ETRN */
5790792Sgshapiro#define SRV_OFFER_VRFY	0x0010	/* offer VRFY (not yet used) */
5890792Sgshapiro#define SRV_OFFER_EXPN	0x0020	/* offer EXPN */
5990792Sgshapiro#define SRV_OFFER_VERB	0x0040	/* offer VERB */
6090792Sgshapiro#define SRV_OFFER_DSN	0x0080	/* offer DSN */
6190792Sgshapiro#if PIPELINING
6290792Sgshapiro# define SRV_OFFER_PIPE	0x0100	/* offer PIPELINING */
6390792Sgshapiro# if _FFR_NO_PIPE
6490792Sgshapiro#  define SRV_NO_PIPE	0x0200	/* disable PIPELINING, sleep if used */
65363466Sgshapiro# endif
6690792Sgshapiro#endif /* PIPELINING */
6790792Sgshapiro#define SRV_REQ_AUTH	0x0400	/* require AUTH */
68132943Sgshapiro#define SRV_REQ_SEC	0x0800	/* require security - equiv to AuthOptions=p */
6990792Sgshapiro#define SRV_TMP_FAIL	0x1000	/* ruleset caused a temporary failure */
70363466Sgshapiro#if _FFR_EAI
71363466Sgshapiro# define SRV_OFFER_EAI	0x2000	/* offer SMTPUTF* */
72363466Sgshapiro#endif
7390792Sgshapiro
7490792Sgshapirostatic unsigned int	srvfeatures __P((ENVELOPE *, char *, unsigned int));
7590792Sgshapiro
76132943Sgshapiro#define	STOP_ATTACK	((time_t) -1)
77132943Sgshapirostatic time_t	checksmtpattack __P((volatile unsigned int *, unsigned int,
78132943Sgshapiro				     bool, char *, ENVELOPE *));
7964562Sgshapirostatic void	printvrfyaddr __P((ADDRESS *, bool, bool));
8064562Sgshapirostatic char	*skipword __P((char *volatile, char *));
8190792Sgshapirostatic void	setup_smtpd_io __P((void));
82120256Sgshapiro
83120256Sgshapiro#if SASL
84363466Sgshapiro#  ifndef MAX_AUTH_USER_LEN
85363466Sgshapiro#   define MAX_AUTH_USER_LEN 256
86363466Sgshapiro#  endif
87363466Sgshapiro#  ifndef MAX_AUTH_LOG_LEN
88363466Sgshapiro#   define MAX_AUTH_LOG_LEN 64
89363466Sgshapiro#  endif
90363466Sgshapirostatic void get_sasl_user __P((char *, unsigned int, const char *, char *out, size_t));
91363466Sgshapiro#  define RESET_AUTH_FAIL_LOG_USER	\
92363466Sgshapiro	do	\
93363466Sgshapiro	{	\
94363466Sgshapiro		(void) memset(auth_user, 0, sizeof(auth_user));	\
95363466Sgshapiro		(void) memset(auth_user_tmp, 0, sizeof(auth_user_tmp));	\
96363466Sgshapiro		auth_user_len = 0;	\
97363466Sgshapiro	} while (0)
98363466Sgshapiro#  define SET_AUTH_USER_TMP(s, len)	\
99363466Sgshapiro	do	\
100363466Sgshapiro	{	\
101363466Sgshapiro		auth_user_len = SM_MIN(len, MAX_AUTH_USER_LEN-1);	\
102363466Sgshapiro		(void) memcpy(auth_user_tmp, s, auth_user_len);	\
103363466Sgshapiro	} while (0)
104363466Sgshapiro#  define SET_AUTH_USER	\
105363466Sgshapiro	get_sasl_user(auth_user_tmp, auth_user_len, auth_type, auth_user, sizeof(auth_user))
106363466Sgshapiro#  define SET_AUTH_USER_CONDITIONALLY	\
107363466Sgshapiro		if ('\0' == auth_user[0])	\
108363466Sgshapiro			SET_AUTH_USER;
109363466Sgshapiro#  define LOG_AUTH_FAIL_USER ", user=", (int)MAX_AUTH_LOG_LEN, auth_user
110120256Sgshapiro# if SASL >= 20000
111120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
112120256Sgshapiro				char *_remoteip, char *_localip,
113120256Sgshapiro				char *_auth_id, sasl_ssf_t *_ext_ssf));
114120256Sgshapiro
115120256Sgshapiro# define RESET_SASLCONN	\
116147078Sgshapiro	do							\
117147078Sgshapiro	{							\
118363466Sgshapiro		RESET_AUTH_FAIL_LOG_USER;			\
119147078Sgshapiro		result = reset_saslconn(&conn, AuthRealm, remoteip, \
120147078Sgshapiro					localip, auth_id, &ext_ssf); \
121147078Sgshapiro		if (result != SASL_OK)				\
122147078Sgshapiro			sasl_ok = false;			\
123147078Sgshapiro	} while (0)
124120256Sgshapiro
125120256Sgshapiro# else /* SASL >= 20000 */
126120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
127120256Sgshapiro				struct sockaddr_in *_saddr_r,
128120256Sgshapiro				struct sockaddr_in *_saddr_l,
129120256Sgshapiro				sasl_external_properties_t *_ext_ssf));
130120256Sgshapiro# define RESET_SASLCONN	\
131147078Sgshapiro	do							\
132147078Sgshapiro	{							\
133363466Sgshapiro		RESET_AUTH_FAIL_LOG_USER;			\
134147078Sgshapiro		result = reset_saslconn(&conn, AuthRealm, &saddr_r, \
135147078Sgshapiro					&saddr_l, &ext_ssf);	\
136147078Sgshapiro		if (result != SASL_OK)				\
137147078Sgshapiro			sasl_ok = false;			\
138147078Sgshapiro	} while (0)
139120256Sgshapiro
140120256Sgshapiro# endif /* SASL >= 20000 */
141120256Sgshapiro#endif /* SASL */
142120256Sgshapiro
143363466Sgshapiro#if !defined(RESET_AUTH_FAIL_LOG_USER)
144363466Sgshapiro# define RESET_AUTH_FAIL_LOG_USER
145363466Sgshapiro#endif
146363466Sgshapiro
14764562Sgshapiroextern ENVELOPE	BlankEnvelope;
14838032Speter
149132943Sgshapiro#define NBADRCPTS						\
150132943Sgshapiro	do							\
151132943Sgshapiro	{							\
152132943Sgshapiro		char buf[16];					\
153168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%d",	\
154132943Sgshapiro			BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \
155132943Sgshapiro				? n_badrcpts - 1 : n_badrcpts);	\
156132943Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
157132943Sgshapiro	} while (0)
158132943Sgshapiro
159363466Sgshapiro#define SKIP_SPACE(s)	while (SM_ISSPACE(*s))	\
16090792Sgshapiro				(s)++
16190792Sgshapiro
162363466Sgshapiro#if _FFR_EAI
16338032Speter/*
164363466Sgshapiro**  ADDR_IS_ASCII -- check whether an address is 100% printable ASCII
165168515Sgshapiro**
166168515Sgshapiro**	Parameters:
167363466Sgshapiro**		a -- an address (or other string)
168363466Sgshapiro**
169363466Sgshapiro**	Returns:
170363466Sgshapiro**		TRUE if a is non-NULL and points to only printable ASCII
171363466Sgshapiro**		FALSE if a is NULL and points to printable ASCII
172363466Sgshapiro**		FALSE if a is non-NULL and points to something containing 8-bittery
173363466Sgshapiro*/
174363466Sgshapiro
175363466Sgshapirobool
176363466Sgshapiroaddr_is_ascii(a)
177363466Sgshapiro	const char * a;
178363466Sgshapiro{
179363466Sgshapiro	while (a != NULL && *a != '\0' && *a >= ' ' && (unsigned char)*a < 127)
180363466Sgshapiro		a++;
181363466Sgshapiro	return (a != NULL && *a == '\0');
182363466Sgshapiro}
183363466Sgshapiro#endif
184363466Sgshapiro
185363466Sgshapiro/*
186363466Sgshapiro**  PARSE_ESMTP_ARGS -- parse ESMTP arguments (for MAIL, RCPT)
187363466Sgshapiro**
188363466Sgshapiro**	Parameters:
189168515Sgshapiro**		e -- the envelope
190168515Sgshapiro**		addr_st -- address (RCPT only)
191168515Sgshapiro**		p -- read buffer
192168515Sgshapiro**		delimptr -- current position in read buffer
193168515Sgshapiro**		which -- MAIL/RCPT
194168515Sgshapiro**		args -- arguments (output)
195168515Sgshapiro**		esmtp_args -- function to process a single ESMTP argument
196168515Sgshapiro**
197168515Sgshapiro**	Returns:
198168515Sgshapiro**		none
199168515Sgshapiro*/
200168515Sgshapiro
201168515Sgshapirovoid
202168515Sgshapiroparse_esmtp_args(e, addr_st, p, delimptr, which, args, esmtp_args)
203168515Sgshapiro	ENVELOPE *e;
204168515Sgshapiro	ADDRESS *addr_st;
205168515Sgshapiro	char *p;
206168515Sgshapiro	char *delimptr;
207168515Sgshapiro	char *which;
208168515Sgshapiro	char *args[];
209168515Sgshapiro	esmtp_args_F esmtp_args;
210168515Sgshapiro{
211168515Sgshapiro	int argno;
212168515Sgshapiro
213168515Sgshapiro	argno = 0;
214168515Sgshapiro	if (args != NULL)
215168515Sgshapiro		args[argno++] = p;
216168515Sgshapiro	p = delimptr;
217168515Sgshapiro	while (p != NULL && *p != '\0')
218168515Sgshapiro	{
219168515Sgshapiro		char *kp;
220168515Sgshapiro		char *vp = NULL;
221168515Sgshapiro		char *equal = NULL;
222168515Sgshapiro
223168515Sgshapiro		/* locate the beginning of the keyword */
224168515Sgshapiro		SKIP_SPACE(p);
225168515Sgshapiro		if (*p == '\0')
226168515Sgshapiro			break;
227168515Sgshapiro		kp = p;
228168515Sgshapiro
229168515Sgshapiro		/* skip to the value portion */
230168515Sgshapiro		while ((isascii(*p) && isalnum(*p)) || *p == '-')
231168515Sgshapiro			p++;
232168515Sgshapiro		if (*p == '=')
233168515Sgshapiro		{
234168515Sgshapiro			equal = p;
235168515Sgshapiro			*p++ = '\0';
236168515Sgshapiro			vp = p;
237168515Sgshapiro
238168515Sgshapiro			/* skip to the end of the value */
239168515Sgshapiro			while (*p != '\0' && *p != ' ' &&
240168515Sgshapiro			       !(isascii(*p) && iscntrl(*p)) &&
241168515Sgshapiro			       *p != '=')
242168515Sgshapiro				p++;
243168515Sgshapiro		}
244168515Sgshapiro
245168515Sgshapiro		if (*p != '\0')
246168515Sgshapiro			*p++ = '\0';
247168515Sgshapiro
248168515Sgshapiro		if (tTd(19, 1))
249168515Sgshapiro			sm_dprintf("%s: got arg %s=\"%s\"\n", which, kp,
250168515Sgshapiro				vp == NULL ? "<null>" : vp);
251168515Sgshapiro
252168515Sgshapiro		esmtp_args(addr_st, kp, vp, e);
253168515Sgshapiro		if (equal != NULL)
254168515Sgshapiro			*equal = '=';
255168515Sgshapiro		if (args != NULL)
256168515Sgshapiro			args[argno] = kp;
257168515Sgshapiro		argno++;
258168515Sgshapiro		if (argno >= MAXSMTPARGS - 1)
259168515Sgshapiro			usrerr("501 5.5.4 Too many parameters");
260168515Sgshapiro		if (Errors > 0)
261168515Sgshapiro			break;
262168515Sgshapiro	}
263168515Sgshapiro	if (args != NULL)
264168515Sgshapiro		args[argno] = NULL;
265168515Sgshapiro}
266168515Sgshapiro
267285229Sgshapiro#if _FFR_ADD_BCC
268285229Sgshapiro
269168515Sgshapiro/*
270285229Sgshapiro**  ADDRCPT -- Add a rcpt to sendq list
271285229Sgshapiro**
272285229Sgshapiro**	Parameters:
273285229Sgshapiro**		rcpt -- rcpt
274285229Sgshapiro**		sendq -- a pointer to the head of a queue to put
275285229Sgshapiro**			these people into.
276285229Sgshapiro**		e -- the envelope in which to add these recipients.
277285229Sgshapiro**
278285229Sgshapiro**	Returns:
279285229Sgshapiro**		The number of addresses added to the list.
280285229Sgshapiro*/
281285229Sgshapiro
282285229Sgshapirostatic int
283285229Sgshapiroaddrcpt(rcpt, sendq, e)
284285229Sgshapiro	char *rcpt;
285285229Sgshapiro	ADDRESS **sendq;
286285229Sgshapiro	ENVELOPE *e;
287285229Sgshapiro{
288285229Sgshapiro	int r;
289285229Sgshapiro	char *oldto;
290285229Sgshapiro	ADDRESS *a;
291285229Sgshapiro
292285229Sgshapiro	SM_REQUIRE(rcpt != NULL);
293285229Sgshapiro	SM_REQUIRE(sendq != NULL);
294285229Sgshapiro	SM_REQUIRE(e != NULL);
295285229Sgshapiro	oldto = e->e_to;
296285229Sgshapiro	if (tTd(25, 1))
297285229Sgshapiro		sm_dprintf("addrcpt: rcpt=%s\n", rcpt);
298285229Sgshapiro	r = Errors;
299285229Sgshapiro	a = NULL;
300285229Sgshapiro	SM_TRY
301285229Sgshapiro	{
302285229Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e b");
303285229Sgshapiro		a = parseaddr(rcpt, NULLADDR, RF_COPYALL, ' ', NULL, e, true);
304285229Sgshapiro		if (a == NULL)
305285229Sgshapiro			return 0;
306285229Sgshapiro
307285229Sgshapiro		a->q_flags &= ~Q_PINGFLAGS;
308285229Sgshapiro		a->q_flags |= QINTBCC;
309285229Sgshapiro		a->q_owner = "<>";
310285229Sgshapiro
311285229Sgshapiro		/* disable alias expansion? */
312285229Sgshapiro		a = recipient(a, sendq, 0, e);
313285229Sgshapiro	}
314285229Sgshapiro	SM_FINALLY
315285229Sgshapiro	{
316285229Sgshapiro		e->e_to = oldto;
317285229Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL);
318285229Sgshapiro	}
319285229Sgshapiro	SM_END_TRY
320285229Sgshapiro	if (tTd(25, 1))
321285229Sgshapiro		sm_dprintf("addrcpt: rcpt=%s, flags=%#lx\n", rcpt,
322285229Sgshapiro			a != NULL ? a->q_flags : 0);
323285229Sgshapiro	Errors = r;
324285229Sgshapiro	return 1;
325285229Sgshapiro}
326285229Sgshapiro
327285229Sgshapiro/*
328285229Sgshapiro**  ADDBCC -- Maybe create a copy of an e-mail
329285229Sgshapiro**
330285229Sgshapiro**	Parameters:
331285229Sgshapiro**		a -- current RCPT
332285229Sgshapiro**		e -- the envelope.
333285229Sgshapiro**
334285229Sgshapiro**	Returns:
335285229Sgshapiro**		nothing
336285229Sgshapiro**
337285229Sgshapiro**	Side Effects:
338285229Sgshapiro**		rscheck() can trigger an "exception"
339285229Sgshapiro*/
340285229Sgshapiro
341285229Sgshapirostatic void
342285229Sgshapiroaddbcc(a, e)
343285229Sgshapiro	ADDRESS *a;
344285229Sgshapiro	ENVELOPE *e;
345285229Sgshapiro{
346285229Sgshapiro	int nobcc;
347285229Sgshapiro	char *newrcpt, empty[1];
348285229Sgshapiro
349285229Sgshapiro	if (!AddBcc)
350285229Sgshapiro		return;
351285229Sgshapiro
352285229Sgshapiro	nobcc = false;
353285229Sgshapiro	empty[0] = '\0';
354285229Sgshapiro	newrcpt = empty;
355285229Sgshapiro
356285229Sgshapiro	nobcc = rscheck("bcc", a->q_paddr, NULL, e, RSF_ADDR, 12, NULL, NOQID,
357285229Sgshapiro			NULL, &newrcpt);
358285229Sgshapiro	if (tTd(25, 1))
359285229Sgshapiro		sm_dprintf("addbcc: nobcc=%d, Errors=%d, newrcpt=<%s>\n", nobcc, Errors, newrcpt);
360285229Sgshapiro	if (nobcc != EX_OK || Errors > 0 || *newrcpt == '\0')
361285229Sgshapiro		return;
362285229Sgshapiro
363285229Sgshapiro	(void) addrcpt(newrcpt, &e->e_sendqueue, e);
364285229Sgshapiro	return;
365285229Sgshapiro}
366285229Sgshapiro#else /* _FFR_ADD_BCC */
367285229Sgshapiro# define addbcc(a, e)
368285229Sgshapiro#endif /* _FFR_ADD_BCC */
369285229Sgshapiro
370285229Sgshapiro#if _FFR_RCPTFLAGS
371285229Sgshapiro/*
372285229Sgshapiro**  RCPTMODS -- Perform rcpt modifications if requested
373285229Sgshapiro**
374285229Sgshapiro**	Parameters:
375285229Sgshapiro**		rcpt -- current RCPT
376285229Sgshapiro**		e -- the envelope.
377285229Sgshapiro**
378285229Sgshapiro**	Returns:
379285229Sgshapiro**		nothing.
380285229Sgshapiro*/
381285229Sgshapiro
382285229Sgshapirovoid
383285229Sgshapirorcptmods(rcpt, e)
384285229Sgshapiro	ADDRESS *rcpt;
385285229Sgshapiro	ENVELOPE *e;
386285229Sgshapiro{
387285229Sgshapiro	char *fl;
388285229Sgshapiro
389285229Sgshapiro	SM_REQUIRE(rcpt != NULL);
390285229Sgshapiro	SM_REQUIRE(e != NULL);
391285229Sgshapiro
392285229Sgshapiro	fl = macvalue(macid("{rcpt_flags}"), e);
393285229Sgshapiro	if (fl == NULL || *fl == '\0')
394285229Sgshapiro		return;
395285229Sgshapiro	if (tTd(25, 1))
396285229Sgshapiro		sm_dprintf("rcptmods: rcpt=%s, flags=%s\n", rcpt->q_paddr, fl);
397285229Sgshapiro
398285229Sgshapiro	/* parse flags */
399285229Sgshapiro	for ( ; *fl != '\0'; ++fl)
400285229Sgshapiro	{
401285229Sgshapiro		switch (*fl)
402285229Sgshapiro		{
403285229Sgshapiro		  case 'n':
404285229Sgshapiro			rcpt->q_flags &= ~Q_PINGFLAGS;
405285229Sgshapiro			rcpt->q_flags |= QINTBCC;
406285229Sgshapiro			rcpt->q_owner = "<>";
407285229Sgshapiro			break;
408285229Sgshapiro
409285229Sgshapiro		  case 'N':
410285229Sgshapiro			rcpt->q_flags &= ~Q_PINGFLAGS;
411285229Sgshapiro			rcpt->q_owner = "<>";
412285229Sgshapiro			break;
413285229Sgshapiro
414285229Sgshapiro		  case QDYNMAILFLG:
415285229Sgshapiro			rcpt->q_flags |= QDYNMAILER;
416285229Sgshapiro			newmodmailer(rcpt, *fl);
417285229Sgshapiro			break;
418285229Sgshapiro
419285229Sgshapiro		  default:
420285229Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
421285229Sgshapiro				  "rcpt=%s, rcpt_flags=%s, status=unknown",
422285229Sgshapiro				  rcpt->q_paddr, fl);
423285229Sgshapiro			break;
424285229Sgshapiro		}
425285229Sgshapiro	}
426285229Sgshapiro
427285229Sgshapiro	/* reset macro to avoid confusion later on */
428285229Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{rcpt_flags}"), NULL);
429285229Sgshapiro
430285229Sgshapiro}
431285229Sgshapiro#else /* _FFR_RCPTFLAGS */
432285229Sgshapiro# define rcptmods(a, e)
433285229Sgshapiro#endif /* _FFR_RCPTFLAGS */
434285229Sgshapiro
435285229Sgshapiro/*
43638032Speter**  SMTP -- run the SMTP protocol.
43738032Speter**
43838032Speter**	Parameters:
43938032Speter**		nullserver -- if non-NULL, rejection message for
44090792Sgshapiro**			(almost) all SMTP commands.
44190792Sgshapiro**		d_flags -- daemon flags
44238032Speter**		e -- the envelope.
44338032Speter**
44438032Speter**	Returns:
44538032Speter**		never.
44638032Speter**
44738032Speter**	Side Effects:
44890792Sgshapiro**		Reads commands from the input channel and processes them.
44938032Speter*/
45038032Speter
45190792Sgshapiro/*
45290792Sgshapiro**  Notice: The smtp server doesn't have a session context like the client
45390792Sgshapiro**	side has (mci). Therefore some data (session oriented) is allocated
45490792Sgshapiro**	or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
45590792Sgshapiro**	This should be fixed in a successor version.
45690792Sgshapiro*/
45790792Sgshapiro
45838032Speterstruct cmd
45938032Speter{
46064562Sgshapiro	char	*cmd_name;	/* command name */
46164562Sgshapiro	int	cmd_code;	/* internal code, see below */
46238032Speter};
46338032Speter
46464562Sgshapiro/* values for cmd_code */
46590792Sgshapiro#define CMDERROR	0	/* bad command */
46690792Sgshapiro#define CMDMAIL	1	/* mail -- designate sender */
46790792Sgshapiro#define CMDRCPT	2	/* rcpt -- designate recipient */
46890792Sgshapiro#define CMDDATA	3	/* data -- send message text */
46990792Sgshapiro#define CMDRSET	4	/* rset -- reset state */
47090792Sgshapiro#define CMDVRFY	5	/* vrfy -- verify address */
47190792Sgshapiro#define CMDEXPN	6	/* expn -- expand address */
47290792Sgshapiro#define CMDNOOP	7	/* noop -- do nothing */
47390792Sgshapiro#define CMDQUIT	8	/* quit -- close connection and die */
47490792Sgshapiro#define CMDHELO	9	/* helo -- be polite */
47590792Sgshapiro#define CMDHELP	10	/* help -- give usage info */
47690792Sgshapiro#define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
47790792Sgshapiro#define CMDETRN	12	/* etrn -- flush queue */
47890792Sgshapiro#if SASL
47990792Sgshapiro# define CMDAUTH	13	/* auth -- SASL authenticate */
480363466Sgshapiro#endif
48190792Sgshapiro#if STARTTLS
48290792Sgshapiro# define CMDSTLS	14	/* STARTTLS -- start TLS session */
483363466Sgshapiro#endif
48438032Speter/* non-standard commands */
48590792Sgshapiro#define CMDVERB	17	/* verb -- go into verbose mode */
48664562Sgshapiro/* unimplemented commands from RFC 821 */
48790792Sgshapiro#define CMDUNIMPL	19	/* unimplemented rfc821 commands */
48838032Speter/* use this to catch and log "door handle" attempts on your system */
48990792Sgshapiro#define CMDLOGBOGUS	23	/* bogus command that should be logged */
49038032Speter/* debugging-only commands, only enabled if SMTPDEBUG is defined */
49190792Sgshapiro#define CMDDBGQSHOW	24	/* showq -- show send queue */
49290792Sgshapiro#define CMDDBGDEBUG	25	/* debug -- set debug mode */
49338032Speter
49466494Sgshapiro/*
49590792Sgshapiro**  Note: If you change this list, remember to update 'helpfile'
49666494Sgshapiro*/
49766494Sgshapiro
49838032Speterstatic struct cmd	CmdTab[] =
49938032Speter{
50038032Speter	{ "mail",	CMDMAIL		},
50138032Speter	{ "rcpt",	CMDRCPT		},
50238032Speter	{ "data",	CMDDATA		},
50338032Speter	{ "rset",	CMDRSET		},
50438032Speter	{ "vrfy",	CMDVRFY		},
50538032Speter	{ "expn",	CMDEXPN		},
50638032Speter	{ "help",	CMDHELP		},
50738032Speter	{ "noop",	CMDNOOP		},
50838032Speter	{ "quit",	CMDQUIT		},
50938032Speter	{ "helo",	CMDHELO		},
51038032Speter	{ "ehlo",	CMDEHLO		},
51138032Speter	{ "etrn",	CMDETRN		},
51238032Speter	{ "verb",	CMDVERB		},
51364562Sgshapiro	{ "send",	CMDUNIMPL	},
51464562Sgshapiro	{ "saml",	CMDUNIMPL	},
51564562Sgshapiro	{ "soml",	CMDUNIMPL	},
51664562Sgshapiro	{ "turn",	CMDUNIMPL	},
51790792Sgshapiro#if SASL
51864562Sgshapiro	{ "auth",	CMDAUTH,	},
519363466Sgshapiro#endif
52090792Sgshapiro#if STARTTLS
52164562Sgshapiro	{ "starttls",	CMDSTLS,	},
522363466Sgshapiro#endif
52338032Speter    /* remaining commands are here only to trap and log attempts to use them */
52438032Speter	{ "showq",	CMDDBGQSHOW	},
52538032Speter	{ "debug",	CMDDBGDEBUG	},
52638032Speter	{ "wiz",	CMDLOGBOGUS	},
52738032Speter
52838032Speter	{ NULL,		CMDERROR	}
52938032Speter};
53038032Speter
53164562Sgshapirostatic char	*CurSmtpClient;		/* who's at the other end of channel */
53238032Speter
53390792Sgshapiro#ifndef MAXBADCOMMANDS
53490792Sgshapiro# define MAXBADCOMMANDS 25	/* maximum number of bad commands */
535363466Sgshapiro#endif
53690792Sgshapiro#ifndef MAXHELOCOMMANDS
53790792Sgshapiro# define MAXHELOCOMMANDS 3	/* max HELO/EHLO commands before slowdown */
538363466Sgshapiro#endif
53990792Sgshapiro#ifndef MAXVRFYCOMMANDS
54090792Sgshapiro# define MAXVRFYCOMMANDS 6	/* max VRFY/EXPN commands before slowdown */
541363466Sgshapiro#endif
54290792Sgshapiro#ifndef MAXETRNCOMMANDS
54390792Sgshapiro# define MAXETRNCOMMANDS 8	/* max ETRN commands before slowdown */
544363466Sgshapiro#endif
54590792Sgshapiro#ifndef MAXTIMEOUT
54690792Sgshapiro# define MAXTIMEOUT (4 * 60)	/* max timeout for bad commands */
547363466Sgshapiro#endif
54838032Speter
549132943Sgshapiro/*
550132943Sgshapiro**  Maximum shift value to compute timeout for bad commands.
551132943Sgshapiro**  This introduces an upper limit of 2^MAXSHIFT for the timeout.
552132943Sgshapiro*/
553132943Sgshapiro
554132943Sgshapiro#ifndef MAXSHIFT
555132943Sgshapiro# define MAXSHIFT 8
556363466Sgshapiro#endif
557132943Sgshapiro#if MAXSHIFT > 31
558132943Sgshapiro ERROR _MAXSHIFT > 31 is invalid
559363466Sgshapiro#endif
560132943Sgshapiro
561132943Sgshapiro
562132943Sgshapiro#if MAXBADCOMMANDS > 0
563132943Sgshapiro# define STOP_IF_ATTACK(r)	do		\
564132943Sgshapiro	{					\
565132943Sgshapiro		if ((r) == STOP_ATTACK)		\
566132943Sgshapiro			goto stopattack;	\
567132943Sgshapiro	} while (0)
568132943Sgshapiro
569132943Sgshapiro#else /* MAXBADCOMMANDS > 0 */
570132943Sgshapiro# define STOP_IF_ATTACK(r)	r
571132943Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
572132943Sgshapiro
573132943Sgshapiro
57490792Sgshapiro#if SM_HEAP_CHECK
57590792Sgshapirostatic SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
57690792Sgshapiro	"@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
577363466Sgshapiro#endif
57838032Speter
57990792Sgshapirotypedef struct
58090792Sgshapiro{
581173340Sgshapiro	bool		sm_gotmail;	/* mail command received */
582173340Sgshapiro	unsigned int	sm_nrcpts;	/* number of successful RCPT commands */
583173340Sgshapiro	bool		sm_discard;
58490792Sgshapiro#if MILTER
585173340Sgshapiro	bool		sm_milterize;
586173340Sgshapiro	bool		sm_milterlist;	/* any filters in the list? */
587173340Sgshapiro	milters_T	sm_milters;
588173340Sgshapiro
589173340Sgshapiro	/* e_nrcpts from envelope before recipient() call */
590173340Sgshapiro	unsigned int	sm_e_nrcpts_orig;
59190792Sgshapiro#endif /* MILTER */
592173340Sgshapiro	char		*sm_quarmsg;	/* carry quarantining across messages */
59390792Sgshapiro} SMTP_T;
59490792Sgshapiro
595132943Sgshapirostatic bool	smtp_data __P((SMTP_T *, ENVELOPE *));
59690792Sgshapiro
597132943Sgshapiro#define MSG_TEMPFAIL "451 4.3.2 Please try again later"
59890792Sgshapiro
59990792Sgshapiro#if MILTER
60090792Sgshapiro# define MILTER_ABORT(e)	milter_abort((e))
601110560Sgshapiro
60290792Sgshapiro# define MILTER_REPLY(str)						\
60390792Sgshapiro	{								\
60490792Sgshapiro		int savelogusrerrs = LogUsrErrs;			\
60590792Sgshapiro									\
606168515Sgshapiro		milter_cmd_fail = true;					\
60790792Sgshapiro		switch (state)						\
60890792Sgshapiro		{							\
609157001Sgshapiro		  case SMFIR_SHUTDOWN:					\
610157001Sgshapiro			if (MilterLogLevel > 3)				\
611157001Sgshapiro			{						\
612157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
613157001Sgshapiro					  "Milter: %s=%s, reject=421, errormode=4",	\
614157001Sgshapiro					  str, addr);			\
615157001Sgshapiro				LogUsrErrs = false;			\
616157001Sgshapiro			}						\
617157001Sgshapiro			{						\
618157001Sgshapiro				bool tsave = QuickAbort;		\
619157001Sgshapiro									\
620157001Sgshapiro				QuickAbort = false;			\
621157001Sgshapiro				usrerr("421 4.3.0 closing connection");	\
622157001Sgshapiro				QuickAbort = tsave;			\
623157001Sgshapiro				e->e_sendqueue = NULL;			\
624157001Sgshapiro				goto doquit;				\
625157001Sgshapiro			}						\
626157001Sgshapiro			break;						\
62790792Sgshapiro		  case SMFIR_REPLYCODE:					\
62890792Sgshapiro			if (MilterLogLevel > 3)				\
62990792Sgshapiro			{						\
63090792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
63190792Sgshapiro					  "Milter: %s=%s, reject=%s",	\
63290792Sgshapiro					  str, addr, response);		\
63390792Sgshapiro				LogUsrErrs = false;			\
63490792Sgshapiro			}						\
635157001Sgshapiro			if (strncmp(response, "421 ", 4) == 0		\
636157001Sgshapiro			    || strncmp(response, "421-", 4) == 0)	\
637132943Sgshapiro			{						\
638132943Sgshapiro				bool tsave = QuickAbort;		\
639132943Sgshapiro									\
640132943Sgshapiro				QuickAbort = false;			\
641132943Sgshapiro				usrerr(response);			\
642132943Sgshapiro				QuickAbort = tsave;			\
643132943Sgshapiro				e->e_sendqueue = NULL;			\
644132943Sgshapiro				goto doquit;				\
645132943Sgshapiro			}						\
646132943Sgshapiro			else						\
647132943Sgshapiro				usrerr(response);			\
64890792Sgshapiro			break;						\
64990792Sgshapiro									\
65090792Sgshapiro		  case SMFIR_REJECT:					\
65190792Sgshapiro			if (MilterLogLevel > 3)				\
65290792Sgshapiro			{						\
65390792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
65490792Sgshapiro					  "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
65590792Sgshapiro					  str, addr);			\
65690792Sgshapiro				LogUsrErrs = false;			\
65790792Sgshapiro			}						\
65890792Sgshapiro			usrerr("550 5.7.1 Command rejected");		\
65990792Sgshapiro			break;						\
66090792Sgshapiro									\
66190792Sgshapiro		  case SMFIR_DISCARD:					\
66290792Sgshapiro			if (MilterLogLevel > 3)				\
66390792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
66490792Sgshapiro					  "Milter: %s=%s, discard",	\
66590792Sgshapiro					  str, addr);			\
66690792Sgshapiro			e->e_flags |= EF_DISCARD;			\
667168515Sgshapiro			milter_cmd_fail = false;			\
66890792Sgshapiro			break;						\
66990792Sgshapiro									\
67090792Sgshapiro		  case SMFIR_TEMPFAIL:					\
67190792Sgshapiro			if (MilterLogLevel > 3)				\
67290792Sgshapiro			{						\
67390792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
67490792Sgshapiro					  "Milter: %s=%s, reject=%s",	\
67590792Sgshapiro					  str, addr, MSG_TEMPFAIL);	\
67690792Sgshapiro				LogUsrErrs = false;			\
67790792Sgshapiro			}						\
67890792Sgshapiro			usrerr(MSG_TEMPFAIL);				\
67990792Sgshapiro			break;						\
680168515Sgshapiro		  default:						\
681168515Sgshapiro			milter_cmd_fail = false;			\
682168515Sgshapiro			break;						\
68390792Sgshapiro		}							\
68490792Sgshapiro		LogUsrErrs = savelogusrerrs;				\
68590792Sgshapiro		if (response != NULL)					\
68690792Sgshapiro			sm_free(response); /* XXX */			\
68790792Sgshapiro	}
68890792Sgshapiro
68990792Sgshapiro#else /* MILTER */
69090792Sgshapiro# define MILTER_ABORT(e)
69190792Sgshapiro#endif /* MILTER */
69290792Sgshapiro
69390792Sgshapiro/* clear all SMTP state (for HELO/EHLO/RSET) */
69490792Sgshapiro#define CLEAR_STATE(cmd)					\
695132943Sgshapirodo								\
69690792Sgshapiro{								\
69790792Sgshapiro	/* abort milter filters */				\
69890792Sgshapiro	MILTER_ABORT(e);					\
69990792Sgshapiro								\
70090792Sgshapiro	if (smtp.sm_nrcpts > 0)					\
70190792Sgshapiro	{							\
70290792Sgshapiro		logundelrcpts(e, cmd, 10, false);		\
70390792Sgshapiro		smtp.sm_nrcpts = 0;				\
70490792Sgshapiro		macdefine(&e->e_macro, A_PERM,			\
70590792Sgshapiro			  macid("{nrcpts}"), "0");		\
70690792Sgshapiro	}							\
70790792Sgshapiro								\
70890792Sgshapiro	e->e_sendqueue = NULL;					\
70990792Sgshapiro	e->e_flags |= EF_CLRQUEUE;				\
71090792Sgshapiro								\
711203004Sgshapiro	if (tTd(92, 2))						\
712203004Sgshapiro		sm_dprintf("CLEAR_STATE: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",\
713203004Sgshapiro			e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel);\
71490792Sgshapiro	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))	\
71590792Sgshapiro		logsender(e, NULL);				\
71690792Sgshapiro	e->e_flags &= ~EF_LOGSENDER;				\
71790792Sgshapiro								\
71890792Sgshapiro	/* clean up a bit */					\
71990792Sgshapiro	smtp.sm_gotmail = false;				\
72090792Sgshapiro	SuprErrs = true;					\
721203004Sgshapiro	(void) dropenvelope(e, true, false);			\
72290792Sgshapiro	sm_rpool_free(e->e_rpool);				\
72390792Sgshapiro	e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL));	\
72490792Sgshapiro	CurEnv = e;						\
725168515Sgshapiro	e->e_features = features;				\
726132943Sgshapiro								\
727132943Sgshapiro	/* put back discard bit */				\
728132943Sgshapiro	if (smtp.sm_discard)					\
729132943Sgshapiro		e->e_flags |= EF_DISCARD;			\
730132943Sgshapiro								\
731132943Sgshapiro	/* restore connection quarantining */			\
732132943Sgshapiro	if (smtp.sm_quarmsg == NULL)				\
733132943Sgshapiro	{							\
734132943Sgshapiro		e->e_quarmsg = NULL;				\
735132943Sgshapiro		macdefine(&e->e_macro, A_PERM,			\
736132943Sgshapiro			macid("{quarantine}"), "");		\
737132943Sgshapiro	}							\
738132943Sgshapiro	else							\
739132943Sgshapiro	{							\
740132943Sgshapiro		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,	\
741132943Sgshapiro						smtp.sm_quarmsg);	\
742132943Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"),	\
743132943Sgshapiro			  e->e_quarmsg);			\
744132943Sgshapiro	}							\
745132943Sgshapiro} while (0)
74690792Sgshapiro
74790792Sgshapiro/* sleep to flatten out connection load */
74890792Sgshapiro#define MIN_DELAY_LOG	15	/* wait before logging this again */
74990792Sgshapiro
75090792Sgshapiro/* is it worth setting the process title for 1s? */
75190792Sgshapiro#define DELAY_CONN(cmd)						\
75290792Sgshapiro	if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA)	\
75390792Sgshapiro	{							\
75490792Sgshapiro		time_t dnow;					\
75590792Sgshapiro								\
75690792Sgshapiro		sm_setproctitle(true, e,			\
75790792Sgshapiro				"%s: %s: delaying %s: load average: %d", \
75890792Sgshapiro				qid_printname(e), CurSmtpClient,	\
75990792Sgshapiro				cmd, DelayLA);	\
76090792Sgshapiro		if (LogLevel > 8 && (dnow = curtime()) > log_delay)	\
76190792Sgshapiro		{						\
76290792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,		\
76390792Sgshapiro				  "delaying=%s, load average=%d >= %d",	\
76490792Sgshapiro				  cmd, CurrentLA, DelayLA);		\
76590792Sgshapiro			log_delay = dnow + MIN_DELAY_LOG;	\
76690792Sgshapiro		}						\
76790792Sgshapiro		(void) sleep(1);				\
76890792Sgshapiro		sm_setproctitle(true, e, "%s %s: %.80s",	\
76990792Sgshapiro				qid_printname(e), CurSmtpClient, inp);	\
77090792Sgshapiro	}
77190792Sgshapiro
772285229Sgshapiro/*
773285229Sgshapiro**  Determine the correct protocol keyword to use in the
774285229Sgshapiro**  Received: header, following RFC 3848.
775285229Sgshapiro*/
776285229Sgshapiro
777285229Sgshapiro#if !STARTTLS
778285229Sgshapiro# define tls_active	false
779285229Sgshapiro#endif
780285229Sgshapiro#if SASL
781285229Sgshapiro# define auth_active	(authenticating == SASL_IS_AUTH)
782285229Sgshapiro#else
783285229Sgshapiro# define auth_active	false
784285229Sgshapiro#endif
785363466Sgshapiro#if _FFR_EAI
786285229Sgshapiro#define GET_PROTOCOL()					\
787363466Sgshapiro	(e->e_smtputf8					\
788363466Sgshapiro	    ? (auth_active				\
789363466Sgshapiro		? (tls_active ? "UTF8SMTPSA" : "UTF8SMTPA") \
790363466Sgshapiro		: (tls_active ? "UTF8SMTPS"  : "UTF8SMTP")) \
791363466Sgshapiro	    : (auth_active				\
792363466Sgshapiro		? (tls_active ? "ESMTPSA" : "ESMTPA")	\
793363466Sgshapiro		: (tls_active ? "ESMTPS"  : "ESMTP")))
794363466Sgshapiro#else /* _FFR_EAI */
795363466Sgshapiro#define GET_PROTOCOL()					\
796285229Sgshapiro	(auth_active					\
797285229Sgshapiro	    ? (tls_active ? "ESMTPSA" : "ESMTPA")	\
798285229Sgshapiro	    : (tls_active ? "ESMTPS"  : "ESMTP"))
799363466Sgshapiro#endif /* _FFR_EAI */
800285229Sgshapiro
801168515Sgshapirostatic bool SevenBitInput_Saved;	/* saved version of SevenBitInput */
80290792Sgshapiro
80338032Spetervoid
80464562Sgshapirosmtp(nullserver, d_flags, e)
80564562Sgshapiro	char *volatile nullserver;
80664562Sgshapiro	BITMAP256 d_flags;
80738032Speter	register ENVELOPE *volatile e;
80838032Speter{
80938032Speter	register char *volatile p;
81064562Sgshapiro	register struct cmd *volatile c = NULL;
81138032Speter	char *cmd;
81238032Speter	auto ADDRESS *vrfyqueue;
81338032Speter	ADDRESS *a;
81438032Speter	volatile bool gothello;		/* helo command received */
81538032Speter	bool vrfy;			/* set if this is a vrfy command */
81638032Speter	char *volatile protocol;	/* sending protocol */
81738032Speter	char *volatile sendinghost;	/* sending hostname */
81838032Speter	char *volatile peerhostname;	/* name of SMTP peer or "localhost" */
81938032Speter	auto char *delimptr;
82038032Speter	char *id;
82190792Sgshapiro	volatile unsigned int n_badcmds = 0;	/* count of bad commands */
82290792Sgshapiro	volatile unsigned int n_badrcpts = 0;	/* number of rejected RCPT */
82390792Sgshapiro	volatile unsigned int n_verifies = 0;	/* count of VRFY/EXPN */
82490792Sgshapiro	volatile unsigned int n_etrn = 0;	/* count of ETRN */
82590792Sgshapiro	volatile unsigned int n_noop = 0;	/* count of NOOP/VERB/etc */
82690792Sgshapiro	volatile unsigned int n_helo = 0;	/* count of HELO/EHLO */
82738032Speter	bool ok;
82890792Sgshapiro	volatile bool first;
82990792Sgshapiro	volatile bool tempfail = false;
83064562Sgshapiro	volatile time_t wt;		/* timeout after too many commands */
83164562Sgshapiro	volatile time_t previous;	/* time after checksmtpattack() */
83290792Sgshapiro	volatile bool lognullconnection = true;
83338032Speter	register char *q;
83490792Sgshapiro	SMTP_T smtp;
83564562Sgshapiro	char *addr;
83664562Sgshapiro	char *greetcode = "220";
837285229Sgshapiro	const char *greetmsg = "not accepting messages";
83890792Sgshapiro	char *hostname;			/* my hostname ($j) */
83938032Speter	QUEUE_CHAR *new;
84064562Sgshapiro	char *args[MAXSMTPARGS];
841168515Sgshapiro	char inp[MAXINPLINE];
842168515Sgshapiro#if MAXINPLINE < MAXLINE
843168515Sgshapiro ERROR _MAXINPLINE must NOT be less than _MAXLINE: MAXINPLINE < MAXLINE
844363466Sgshapiro#endif
84538032Speter	char cmdbuf[MAXLINE];
84690792Sgshapiro#if SASL
84764562Sgshapiro	sasl_conn_t *conn;
84864562Sgshapiro	volatile bool sasl_ok;
84990792Sgshapiro	volatile unsigned int n_auth = 0;	/* count of AUTH commands */
85064562Sgshapiro	bool ismore;
85164562Sgshapiro	int result;
85264562Sgshapiro	volatile int authenticating;
85364562Sgshapiro	char *user;
85498121Sgshapiro	char *in, *out2;
855363466Sgshapiro	char auth_user[MAX_AUTH_USER_LEN], auth_user_tmp[MAX_AUTH_USER_LEN];
856363466Sgshapiro	unsigned int auth_user_len;
85798121Sgshapiro# if SASL >= 20000
858168515Sgshapiro	char *auth_id = NULL;
85998121Sgshapiro	const char *out;
860102528Sgshapiro	sasl_ssf_t ext_ssf;
861120256Sgshapiro	char localip[60], remoteip[60];
86298121Sgshapiro# else /* SASL >= 20000 */
86398121Sgshapiro	char *out;
86464562Sgshapiro	const char *errstr;
86598121Sgshapiro	sasl_external_properties_t ext_ssf;
866120256Sgshapiro	struct sockaddr_in saddr_l;
867120256Sgshapiro	struct sockaddr_in saddr_r;
86898121Sgshapiro# endif /* SASL >= 20000 */
86998121Sgshapiro	sasl_security_properties_t ssp;
87098121Sgshapiro	sasl_ssf_t *ssf;
87190792Sgshapiro	unsigned int inlen, out2len;
87264562Sgshapiro	unsigned int outlen;
87364562Sgshapiro	char *volatile auth_type;
87464562Sgshapiro	char *mechlist;
87590792Sgshapiro	volatile unsigned int n_mechs;
87690792Sgshapiro	unsigned int len;
87790792Sgshapiro#endif /* SASL */
878132943Sgshapiro	int r;
87990792Sgshapiro#if STARTTLS
88066494Sgshapiro	int rfd, wfd;
88190792Sgshapiro	volatile bool tls_active = false;
882132943Sgshapiro	volatile bool smtps = bitnset(D_SMTPS, d_flags);
88364562Sgshapiro	bool saveQuickAbort;
88464562Sgshapiro	bool saveSuprErrs;
88590792Sgshapiro	time_t tlsstart;
886363466Sgshapiro	int ssl_err, tlsret;
887363466Sgshapiro	int save_errno;
888363466Sgshapiro	extern int TLSsslidx;
88990792Sgshapiro#endif /* STARTTLS */
89090792Sgshapiro	volatile unsigned int features;
89190792Sgshapiro#if PIPELINING
89290792Sgshapiro# if _FFR_NO_PIPE
89390792Sgshapiro	int np_log = 0;
894363466Sgshapiro# endif
89590792Sgshapiro#endif /* PIPELINING */
89690792Sgshapiro	volatile time_t log_delay = (time_t) 0;
897168515Sgshapiro#if MILTER
898168515Sgshapiro	volatile bool milter_cmd_done, milter_cmd_safe;
899168515Sgshapiro	volatile bool milter_rcpt_added, milter_cmd_fail;
900168515Sgshapiro	ADDRESS addr_st;
901168515Sgshapiro# define p_addr_st	&addr_st
902168515Sgshapiro#else /* MILTER */
903168515Sgshapiro# define p_addr_st	NULL
904168515Sgshapiro#endif /* MILTER */
905168515Sgshapiro	size_t inplen;
906182352Sgshapiro#if _FFR_BADRCPT_SHUTDOWN
907182352Sgshapiro	int n_badrcpts_adj;
908363466Sgshapiro#endif
90938032Speter
910363466Sgshapiro	RESET_AUTH_FAIL_LOG_USER;
911168515Sgshapiro	SevenBitInput_Saved = SevenBitInput;
91290792Sgshapiro	smtp.sm_nrcpts = 0;
91390792Sgshapiro#if MILTER
91490792Sgshapiro	smtp.sm_milterize = (nullserver == NULL);
91590792Sgshapiro	smtp.sm_milterlist = false;
916168515Sgshapiro	addr = NULL;
917363466Sgshapiro#endif
91890792Sgshapiro
91990792Sgshapiro	/* setup I/O fd correctly for the SMTP server */
92090792Sgshapiro	setup_smtpd_io();
92190792Sgshapiro
92290792Sgshapiro#if SM_HEAP_CHECK
92390792Sgshapiro	if (sm_debug_active(&DebugLeakSmtp, 1))
92438032Speter	{
92590792Sgshapiro		sm_heap_newgroup();
92690792Sgshapiro		sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
92738032Speter	}
92890792Sgshapiro#endif /* SM_HEAP_CHECK */
92964562Sgshapiro
93090792Sgshapiro	/* XXX the rpool should be set when e is initialized in main() */
93190792Sgshapiro	e->e_rpool = sm_rpool_new_x(NULL);
93290792Sgshapiro	e->e_macro.mac_rpool = e->e_rpool;
93390792Sgshapiro
93438032Speter	settime(e);
93590792Sgshapiro	sm_getla();
93638032Speter	peerhostname = RealHostName;
93738032Speter	if (peerhostname == NULL)
93838032Speter		peerhostname = "localhost";
93938032Speter	CurHostName = peerhostname;
94038032Speter	CurSmtpClient = macvalue('_', e);
94138032Speter	if (CurSmtpClient == NULL)
94238032Speter		CurSmtpClient = CurHostName;
94338032Speter
94438032Speter	/* check_relay may have set discard bit, save for later */
94590792Sgshapiro	smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
94638032Speter
94790792Sgshapiro#if PIPELINING
94890792Sgshapiro	/* auto-flush output when reading input */
94990792Sgshapiro	(void) sm_io_autoflush(InChannel, OutChannel);
95090792Sgshapiro#endif /* PIPELINING */
95164562Sgshapiro
95290792Sgshapiro	sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
95390792Sgshapiro
95490792Sgshapiro	/* Set default features for server. */
95590792Sgshapiro	features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
95690792Sgshapiro		     bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
95790792Sgshapiro		| (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
95890792Sgshapiro		| (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
95990792Sgshapiro			: (SRV_OFFER_EXPN
96090792Sgshapiro			  | (bitset(PRIV_NOVERB, PrivacyFlags)
96190792Sgshapiro			     ? SRV_NONE : SRV_OFFER_VERB)))
962159609Sgshapiro		| ((bitset(PRIV_NORECEIPTS, PrivacyFlags) || !SendMIMEErrors)
963159609Sgshapiro			 ? SRV_NONE : SRV_OFFER_DSN)
96490792Sgshapiro#if SASL
96590792Sgshapiro		| (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
966132943Sgshapiro		| (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC
967132943Sgshapiro							  : SRV_NONE)
96890792Sgshapiro#endif /* SASL */
96990792Sgshapiro#if PIPELINING
97090792Sgshapiro		| SRV_OFFER_PIPE
971363466Sgshapiro#endif
97290792Sgshapiro#if STARTTLS
97390792Sgshapiro		| (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
97490792Sgshapiro		| (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
97590792Sgshapiro						       : SRV_VRFY_CLT)
976363466Sgshapiro#endif
977363466Sgshapiro#if _FFR_EAI
978363466Sgshapiro		| SRV_OFFER_EAI
979363466Sgshapiro#endif /* _FFR_EAI */
98090792Sgshapiro		;
98190792Sgshapiro	if (nullserver == NULL)
98290792Sgshapiro	{
98390792Sgshapiro		features = srvfeatures(e, CurSmtpClient, features);
98490792Sgshapiro		if (bitset(SRV_TMP_FAIL, features))
98590792Sgshapiro		{
98690792Sgshapiro			if (LogLevel > 4)
98790792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
98890792Sgshapiro					  "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
98990792Sgshapiro					  CurSmtpClient);
99090792Sgshapiro			nullserver = "450 4.3.0 Please try again later.";
99190792Sgshapiro		}
992132943Sgshapiro		else
993132943Sgshapiro		{
99490792Sgshapiro#if PIPELINING
99590792Sgshapiro# if _FFR_NO_PIPE
996132943Sgshapiro			if (bitset(SRV_NO_PIPE, features))
997132943Sgshapiro			{
998132943Sgshapiro				/* for consistency */
999132943Sgshapiro				features &= ~SRV_OFFER_PIPE;
1000132943Sgshapiro			}
100190792Sgshapiro# endif /* _FFR_NO_PIPE */
100290792Sgshapiro#endif /* PIPELINING */
1003132943Sgshapiro#if SASL
1004132943Sgshapiro			if (bitset(SRV_REQ_SEC, features))
1005132943Sgshapiro				SASLOpts |= SASL_SEC_NOPLAINTEXT;
1006132943Sgshapiro			else
1007132943Sgshapiro				SASLOpts &= ~SASL_SEC_NOPLAINTEXT;
1008132943Sgshapiro#endif /* SASL */
1009132943Sgshapiro		}
101090792Sgshapiro	}
1011132943Sgshapiro	else if (strncmp(nullserver, "421 ", 4) == 0)
1012132943Sgshapiro	{
1013363466Sgshapiro		/* Can't use ("%s", ...) due to message() requirements */
1014132943Sgshapiro		message(nullserver);
1015132943Sgshapiro		goto doquit;
1016132943Sgshapiro	}
101790792Sgshapiro
1018168515Sgshapiro	e->e_features = features;
101990792Sgshapiro	hostname = macvalue('j', e);
102090792Sgshapiro#if SASL
1021132943Sgshapiro	if (AuthRealm == NULL)
1022132943Sgshapiro		AuthRealm = hostname;
102390792Sgshapiro	sasl_ok = bitset(SRV_OFFER_AUTH, features);
102464562Sgshapiro	n_mechs = 0;
102590792Sgshapiro	authenticating = SASL_NOT_AUTH;
102664562Sgshapiro
102764562Sgshapiro	/* SASL server new connection */
102890792Sgshapiro	if (sasl_ok)
102938032Speter	{
103098121Sgshapiro# if SASL >= 20000
1031132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL,
103298121Sgshapiro					 NULL, 0, &conn);
103398121Sgshapiro# elif SASL > 10505
103490792Sgshapiro		/* use empty realm: only works in SASL > 1.5.5 */
1035132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn);
103698121Sgshapiro# else /* SASL >= 20000 */
103790792Sgshapiro		/* use no realm -> realm is set to hostname by SASL lib */
1038132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0,
103990792Sgshapiro					 &conn);
104098121Sgshapiro# endif /* SASL >= 20000 */
104190792Sgshapiro		sasl_ok = result == SASL_OK;
104290792Sgshapiro		if (!sasl_ok)
104390792Sgshapiro		{
104490792Sgshapiro			if (LogLevel > 9)
104590792Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
104690792Sgshapiro					  "AUTH error: sasl_server_new failed=%d",
104790792Sgshapiro					  result);
104890792Sgshapiro		}
104990792Sgshapiro	}
105090792Sgshapiro	if (sasl_ok)
105190792Sgshapiro	{
105264562Sgshapiro		/*
105364562Sgshapiro		**  SASL set properties for sasl
105464562Sgshapiro		**  set local/remote IP
105598121Sgshapiro		**  XXX Cyrus SASL v1 only supports IPv4
105664562Sgshapiro		**
105764562Sgshapiro		**  XXX where exactly are these used/required?
105864562Sgshapiro		**  Kerberos_v4
105964562Sgshapiro		*/
106064562Sgshapiro
106198121Sgshapiro# if SASL >= 20000
1062147078Sgshapiro		localip[0] = remoteip[0] = '\0';
106398121Sgshapiro#  if NETINET || NETINET6
106490792Sgshapiro		in = macvalue(macid("{daemon_family}"), e);
106598121Sgshapiro		if (in != NULL && (
106698121Sgshapiro#   if NETINET6
106798121Sgshapiro		    strcmp(in, "inet6") == 0 ||
1068363466Sgshapiro#   endif
106998121Sgshapiro		    strcmp(in, "inet") == 0))
107098121Sgshapiro		{
107198121Sgshapiro			SOCKADDR_LEN_T addrsize;
107298121Sgshapiro			SOCKADDR saddr_l;
107398121Sgshapiro			SOCKADDR saddr_r;
107498121Sgshapiro
107598121Sgshapiro			addrsize = sizeof(saddr_r);
107698121Sgshapiro			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
107798121Sgshapiro						      NULL),
107898121Sgshapiro					(struct sockaddr *) &saddr_r,
107998121Sgshapiro					&addrsize) == 0)
108098121Sgshapiro			{
108198121Sgshapiro				if (iptostring(&saddr_r, addrsize,
1082168515Sgshapiro					       remoteip, sizeof(remoteip)))
108398121Sgshapiro				{
108498121Sgshapiro					sasl_setprop(conn, SASL_IPREMOTEPORT,
108598121Sgshapiro						     remoteip);
108698121Sgshapiro				}
108798121Sgshapiro				addrsize = sizeof(saddr_l);
108898121Sgshapiro				if (getsockname(sm_io_getinfo(InChannel,
108998121Sgshapiro							      SM_IO_WHAT_FD,
109098121Sgshapiro							      NULL),
109198121Sgshapiro						(struct sockaddr *) &saddr_l,
109298121Sgshapiro						&addrsize) == 0)
109398121Sgshapiro				{
109498121Sgshapiro					if (iptostring(&saddr_l, addrsize,
109598121Sgshapiro						       localip,
1096168515Sgshapiro						       sizeof(localip)))
109798121Sgshapiro					{
109898121Sgshapiro						sasl_setprop(conn,
109998121Sgshapiro							     SASL_IPLOCALPORT,
110098121Sgshapiro							     localip);
110198121Sgshapiro					}
110298121Sgshapiro				}
110398121Sgshapiro			}
110498121Sgshapiro		}
110598121Sgshapiro#  endif /* NETINET || NETINET6 */
110698121Sgshapiro# else /* SASL >= 20000 */
110798121Sgshapiro#  if NETINET
110898121Sgshapiro		in = macvalue(macid("{daemon_family}"), e);
110964562Sgshapiro		if (in != NULL && strcmp(in, "inet") == 0)
111064562Sgshapiro		{
111164562Sgshapiro			SOCKADDR_LEN_T addrsize;
111264562Sgshapiro
111364562Sgshapiro			addrsize = sizeof(struct sockaddr_in);
111490792Sgshapiro			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
111590792Sgshapiro						      NULL),
111664562Sgshapiro					(struct sockaddr *)&saddr_r,
111764562Sgshapiro					&addrsize) == 0)
111864562Sgshapiro			{
111964562Sgshapiro				sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
112064562Sgshapiro				addrsize = sizeof(struct sockaddr_in);
112190792Sgshapiro				if (getsockname(sm_io_getinfo(InChannel,
112290792Sgshapiro							      SM_IO_WHAT_FD,
112390792Sgshapiro							      NULL),
112464562Sgshapiro						(struct sockaddr *)&saddr_l,
112564562Sgshapiro						&addrsize) == 0)
112664562Sgshapiro					sasl_setprop(conn, SASL_IP_LOCAL,
112764562Sgshapiro						     &saddr_l);
112864562Sgshapiro			}
112964562Sgshapiro		}
113098121Sgshapiro#  endif /* NETINET */
113198121Sgshapiro# endif /* SASL >= 20000 */
113264562Sgshapiro
113364562Sgshapiro		auth_type = NULL;
113464562Sgshapiro		mechlist = NULL;
113564562Sgshapiro		user = NULL;
113690792Sgshapiro# if 0
113790792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
113890792Sgshapiro			macid("{auth_author}"), NULL);
1139363466Sgshapiro# endif
114064562Sgshapiro
114164562Sgshapiro		/* set properties */
1142168515Sgshapiro		(void) memset(&ssp, '\0', sizeof(ssp));
114390792Sgshapiro
114464562Sgshapiro		/* XXX should these be options settable via .cf ? */
114564562Sgshapiro		/* ssp.min_ssf = 0; is default due to memset() */
1146223067Sgshapiro		ssp.max_ssf = MaxSLBits;
1147223067Sgshapiro		ssp.maxbufsize = MAXOUTLEN;
114864562Sgshapiro		ssp.security_flags = SASLOpts & SASL_SEC_MASK;
114964562Sgshapiro		sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
115064562Sgshapiro
115164562Sgshapiro		if (sasl_ok)
115264562Sgshapiro		{
115364562Sgshapiro			/*
115464562Sgshapiro			**  external security strength factor;
115590792Sgshapiro			**	currently we have none so zero
115664562Sgshapiro			*/
115790792Sgshapiro
115898121Sgshapiro# if SASL >= 20000
115998121Sgshapiro			ext_ssf = 0;
116098121Sgshapiro			auth_id = NULL;
116198121Sgshapiro			sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL,
116298121Sgshapiro						 &ext_ssf) == SASL_OK) &&
116398121Sgshapiro				   (sasl_setprop(conn, SASL_AUTH_EXTERNAL,
1164102528Sgshapiro						 auth_id) == SASL_OK));
116598121Sgshapiro# else /* SASL >= 20000 */
116664562Sgshapiro			ext_ssf.ssf = 0;
116764562Sgshapiro			ext_ssf.auth_id = NULL;
116864562Sgshapiro			sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
116964562Sgshapiro					       &ext_ssf) == SASL_OK;
117098121Sgshapiro# endif /* SASL >= 20000 */
117164562Sgshapiro		}
117264562Sgshapiro		if (sasl_ok)
117364562Sgshapiro			n_mechs = saslmechs(conn, &mechlist);
117438032Speter	}
117590792Sgshapiro#endif /* SASL */
117638032Speter
1177285229Sgshapiro	(void) set_tls_rd_tmo(TimeOuts.to_nextcommand);
1178182352Sgshapiro
117990792Sgshapiro#if MILTER
118090792Sgshapiro	if (smtp.sm_milterize)
118164562Sgshapiro	{
118264562Sgshapiro		char state;
118364562Sgshapiro
118464562Sgshapiro		/* initialize mail filter connection */
1185173340Sgshapiro		smtp.sm_milterlist = milter_init(e, &state, &smtp.sm_milters);
118664562Sgshapiro		switch (state)
118764562Sgshapiro		{
118864562Sgshapiro		  case SMFIR_REJECT:
118990792Sgshapiro			if (MilterLogLevel > 3)
119090792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
119194334Sgshapiro					  "Milter: initialization failed, rejecting commands");
119264562Sgshapiro			greetcode = "554";
119364562Sgshapiro			nullserver = "Command rejected";
119490792Sgshapiro			smtp.sm_milterize = false;
119564562Sgshapiro			break;
119664562Sgshapiro
119764562Sgshapiro		  case SMFIR_TEMPFAIL:
119890792Sgshapiro			if (MilterLogLevel > 3)
119990792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
120094334Sgshapiro					  "Milter: initialization failed, temp failing commands");
120190792Sgshapiro			tempfail = true;
120290792Sgshapiro			smtp.sm_milterize = false;
120364562Sgshapiro			break;
1204157001Sgshapiro
1205157001Sgshapiro		  case SMFIR_SHUTDOWN:
1206157001Sgshapiro			if (MilterLogLevel > 3)
1207157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
1208157001Sgshapiro					  "Milter: initialization failed, closing connection");
1209157001Sgshapiro			tempfail = true;
1210157001Sgshapiro			smtp.sm_milterize = false;
1211157001Sgshapiro			message("421 4.7.0 %s closing connection",
1212157001Sgshapiro					MyHostName);
1213157001Sgshapiro
1214157001Sgshapiro			/* arrange to ignore send list */
1215157001Sgshapiro			e->e_sendqueue = NULL;
1216182352Sgshapiro			lognullconnection = false;
1217157001Sgshapiro			goto doquit;
121864562Sgshapiro		}
121964562Sgshapiro	}
122064562Sgshapiro
122190792Sgshapiro	if (smtp.sm_milterlist && smtp.sm_milterize &&
122290792Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
122364562Sgshapiro	{
122464562Sgshapiro		char state;
122590792Sgshapiro		char *response;
122664562Sgshapiro
1227161389Sgshapiro		q = macvalue(macid("{client_name}"), e);
1228168515Sgshapiro		SM_ASSERT(q != NULL || OpMode == MD_SMTP);
1229168515Sgshapiro		if (q == NULL)
1230168515Sgshapiro			q = "localhost";
1231161389Sgshapiro		response = milter_connect(q, RealHostAddr, e, &state);
123264562Sgshapiro		switch (state)
123364562Sgshapiro		{
1234285229Sgshapiro#if _FFR_MILTER_CONNECT_REPLYCODE
1235285229Sgshapiro		  case SMFIR_REPLYCODE:
1236285229Sgshapiro			if (*response == '5')
1237285229Sgshapiro			{
1238285229Sgshapiro				if (MilterLogLevel > 3)
1239285229Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1240285229Sgshapiro						  "Milter: connect: host=%s, addr=%s, reject=%s",
1241285229Sgshapiro						  peerhostname,
1242285229Sgshapiro						  anynet_ntoa(&RealHostAddr),
1243285229Sgshapiro						  response);
1244285229Sgshapiro				greetcode = "554"; /* Required by 2821 3.1 */
1245285229Sgshapiro				nullserver = newstr(response);
1246285229Sgshapiro				if (strlen(nullserver) > 4)
1247285229Sgshapiro				{
1248285229Sgshapiro					int skip;
1249285229Sgshapiro
1250285229Sgshapiro					greetmsg = nullserver + 4;
1251285229Sgshapiro
1252285229Sgshapiro					/* skip over enhanced status code */
1253285229Sgshapiro					skip = isenhsc(greetmsg, ' ');
1254285229Sgshapiro					if (skip > 0)
1255285229Sgshapiro						greetmsg += skip + 1;
1256285229Sgshapiro				}
1257285229Sgshapiro				smtp.sm_milterize = false;
1258285229Sgshapiro				break;
1259285229Sgshapiro			}
1260285229Sgshapiro			else if (strncmp(response, "421 ", 4) == 0)
1261285229Sgshapiro			{
1262285229Sgshapiro				int skip;
1263285229Sgshapiro				const char *msg = response + 4;
1264285229Sgshapiro
1265285229Sgshapiro				if (MilterLogLevel > 3)
1266285229Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1267285229Sgshapiro						  "Milter: connect: host=%s, addr=%s, shutdown=%s",
1268285229Sgshapiro						  peerhostname,
1269285229Sgshapiro						  anynet_ntoa(&RealHostAddr),
1270285229Sgshapiro						  response);
1271285229Sgshapiro				tempfail = true;
1272285229Sgshapiro				smtp.sm_milterize = false;
1273285229Sgshapiro
1274285229Sgshapiro				/* skip over enhanced status code */
1275285229Sgshapiro				skip = isenhsc(msg, ' ');
1276285229Sgshapiro				if (skip > 0)
1277285229Sgshapiro					msg += skip + 1;
1278285229Sgshapiro				message("421 %s %s", MyHostName, msg);
1279285229Sgshapiro
1280285229Sgshapiro				/* arrange to ignore send list */
1281285229Sgshapiro				e->e_sendqueue = NULL;
1282285229Sgshapiro				goto doquit;
1283285229Sgshapiro			}
1284285229Sgshapiro			else
1285285229Sgshapiro			{
1286285229Sgshapiro				if (MilterLogLevel > 3)
1287285229Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1288285229Sgshapiro						  "Milter: connect: host=%s, addr=%s, temp failing commands=%s",
1289285229Sgshapiro						  peerhostname,
1290285229Sgshapiro						  anynet_ntoa(&RealHostAddr),
1291285229Sgshapiro						  response);
1292285229Sgshapiro				/*tempfail = true;*/
1293285229Sgshapiro				smtp.sm_milterize = false;
1294285229Sgshapiro				nullserver = newstr(response);
1295285229Sgshapiro				break;
1296285229Sgshapiro			}
1297285229Sgshapiro
1298285229Sgshapiro#else /* _FFR_MILTER_CONNECT_REPLYCODE */
129964562Sgshapiro		  case SMFIR_REPLYCODE:	/* REPLYCODE shouldn't happen */
1300285229Sgshapiro#endif /* _FFR_MILTER_CONNECT_REPLYCODE */
130164562Sgshapiro		  case SMFIR_REJECT:
130290792Sgshapiro			if (MilterLogLevel > 3)
130390792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
130490792Sgshapiro					  "Milter: connect: host=%s, addr=%s, rejecting commands",
130590792Sgshapiro					  peerhostname,
130690792Sgshapiro					  anynet_ntoa(&RealHostAddr));
130764562Sgshapiro			greetcode = "554";
130864562Sgshapiro			nullserver = "Command rejected";
130990792Sgshapiro			smtp.sm_milterize = false;
131064562Sgshapiro			break;
131164562Sgshapiro
131264562Sgshapiro		  case SMFIR_TEMPFAIL:
131390792Sgshapiro			if (MilterLogLevel > 3)
131490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
131590792Sgshapiro					  "Milter: connect: host=%s, addr=%s, temp failing commands",
131690792Sgshapiro					  peerhostname,
131790792Sgshapiro					  anynet_ntoa(&RealHostAddr));
131890792Sgshapiro			tempfail = true;
131990792Sgshapiro			smtp.sm_milterize = false;
132064562Sgshapiro			break;
1321110560Sgshapiro
1322110560Sgshapiro		  case SMFIR_SHUTDOWN:
1323110560Sgshapiro			if (MilterLogLevel > 3)
1324110560Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
1325110560Sgshapiro					  "Milter: connect: host=%s, addr=%s, shutdown",
1326110560Sgshapiro					  peerhostname,
1327110560Sgshapiro					  anynet_ntoa(&RealHostAddr));
1328110560Sgshapiro			tempfail = true;
1329110560Sgshapiro			smtp.sm_milterize = false;
1330110560Sgshapiro			message("421 4.7.0 %s closing connection",
1331110560Sgshapiro					MyHostName);
1332110560Sgshapiro
1333110560Sgshapiro			/* arrange to ignore send list */
1334110560Sgshapiro			e->e_sendqueue = NULL;
1335110560Sgshapiro			goto doquit;
133664562Sgshapiro		}
133790792Sgshapiro		if (response != NULL)
1338285229Sgshapiro			sm_free(response);
133964562Sgshapiro	}
134090792Sgshapiro#endif /* MILTER */
134164562Sgshapiro
1342132943Sgshapiro	/*
1343132943Sgshapiro	**  Broken proxies and SMTP slammers
1344132943Sgshapiro	**  push data without waiting, catch them
1345132943Sgshapiro	*/
1346132943Sgshapiro
1347132943Sgshapiro	if (
134890792Sgshapiro#if STARTTLS
1349132943Sgshapiro	    !smtps &&
1350363466Sgshapiro#endif
1351168515Sgshapiro	    *greetcode == '2' && nullserver == NULL)
1352132943Sgshapiro	{
1353132943Sgshapiro		time_t msecs = 0;
1354132943Sgshapiro		char **pvp;
1355132943Sgshapiro		char pvpbuf[PSBUFSIZE];
1356132943Sgshapiro
1357132943Sgshapiro		/* Ask the rulesets how long to pause */
1358132943Sgshapiro		pvp = NULL;
1359132943Sgshapiro		r = rscap("greet_pause", peerhostname,
1360132943Sgshapiro			  anynet_ntoa(&RealHostAddr), e,
1361132943Sgshapiro			  &pvp, pvpbuf, sizeof(pvpbuf));
1362132943Sgshapiro		if (r == EX_OK && pvp != NULL && pvp[0] != NULL &&
1363132943Sgshapiro		    (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
1364132943Sgshapiro		{
1365132943Sgshapiro			msecs = strtol(pvp[1], NULL, 10);
1366132943Sgshapiro		}
1367132943Sgshapiro
1368132943Sgshapiro		if (msecs > 0)
1369132943Sgshapiro		{
1370132943Sgshapiro			int fd;
1371132943Sgshapiro			fd_set readfds;
1372132943Sgshapiro			struct timeval timeout;
1373157001Sgshapiro			struct timeval bp, ep, tp; /* {begin,end,total}pause */
1374168515Sgshapiro			int eoftest;
1375132943Sgshapiro
1376132943Sgshapiro			/* pause for a moment */
1377132943Sgshapiro			timeout.tv_sec = msecs / 1000;
1378132943Sgshapiro			timeout.tv_usec = (msecs % 1000) * 1000;
1379132943Sgshapiro
1380132943Sgshapiro			/* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
1381132943Sgshapiro			if (timeout.tv_sec >= 300)
1382132943Sgshapiro			{
1383132943Sgshapiro				timeout.tv_sec = 300;
1384132943Sgshapiro				timeout.tv_usec = 0;
1385132943Sgshapiro			}
1386132943Sgshapiro
1387132943Sgshapiro			/* check if data is on the socket during the pause */
1388132943Sgshapiro			fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
1389132943Sgshapiro			FD_ZERO(&readfds);
1390132943Sgshapiro			SM_FD_SET(fd, &readfds);
1391157001Sgshapiro			gettimeofday(&bp, NULL);
1392132943Sgshapiro			if (select(fd + 1, FDSET_CAST &readfds,
1393132943Sgshapiro			    NULL, NULL, &timeout) > 0 &&
1394168515Sgshapiro			    FD_ISSET(fd, &readfds) &&
1395182352Sgshapiro			    (eoftest = sm_io_getc(InChannel, SM_TIME_DEFAULT))
1396168515Sgshapiro			    != SM_IO_EOF)
1397132943Sgshapiro			{
1398182352Sgshapiro				sm_io_ungetc(InChannel, SM_TIME_DEFAULT,
1399168515Sgshapiro					     eoftest);
1400157001Sgshapiro				gettimeofday(&ep, NULL);
1401157001Sgshapiro				timersub(&ep, &bp, &tp);
1402132943Sgshapiro				greetcode = "554";
1403132943Sgshapiro				nullserver = "Command rejected";
1404132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
1405168515Sgshapiro					  "rejecting commands from %s [%s] due to pre-greeting traffic after %d seconds",
1406132943Sgshapiro					  peerhostname,
1407168515Sgshapiro					  anynet_ntoa(&RealHostAddr),
1408168515Sgshapiro					  (int) tp.tv_sec +
1409157001Sgshapiro						(tp.tv_usec >= 500000 ? 1 : 0)
1410157001Sgshapiro					 );
1411132943Sgshapiro			}
1412132943Sgshapiro		}
1413132943Sgshapiro	}
1414132943Sgshapiro
1415132943Sgshapiro#if STARTTLS
141690792Sgshapiro	/* If this an smtps connection, start TLS now */
141790792Sgshapiro	if (smtps)
1418120256Sgshapiro	{
1419363466Sgshapiro		if (!tls_ok_srv || srv_ctx == NULL)
1420363466Sgshapiro		{
1421363466Sgshapiro			sm_syslog(LOG_ERR, e->e_id,
1422363466Sgshapiro				"smtps: TLS not available, exiting");
1423363466Sgshapiro			exit(EX_CONFIG);
1424363466Sgshapiro		}
1425120256Sgshapiro		Errors = 0;
1426363466Sgshapiro		first = true;
1427363466Sgshapiro		gothello = false;
1428363466Sgshapiro		smtp.sm_gotmail = false;
142990792Sgshapiro		goto starttls;
1430120256Sgshapiro	}
143190792Sgshapiro
143290792Sgshapiro  greeting:
143390792Sgshapiro
143490792Sgshapiro#endif /* STARTTLS */
143590792Sgshapiro
143638032Speter	/* output the first line, inserting "ESMTP" as second word */
143790792Sgshapiro	if (*greetcode == '5')
1438285229Sgshapiro		(void) sm_snprintf(inp, sizeof(inp), "%s %s", hostname,
1439285229Sgshapiro				   greetmsg);
144090792Sgshapiro	else
1441168515Sgshapiro		expand(SmtpGreeting, inp, sizeof(inp), e);
144290792Sgshapiro
144338032Speter	p = strchr(inp, '\n');
144438032Speter	if (p != NULL)
144538032Speter		*p++ = '\0';
144638032Speter	id = strchr(inp, ' ');
144738032Speter	if (id == NULL)
144838032Speter		id = &inp[strlen(inp)];
144964562Sgshapiro	if (p == NULL)
1450168515Sgshapiro		(void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
145164562Sgshapiro			 "%s %%.*s ESMTP%%s", greetcode);
145264562Sgshapiro	else
1453168515Sgshapiro		(void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
145464562Sgshapiro			 "%s-%%.*s ESMTP%%s", greetcode);
145566494Sgshapiro	message(cmdbuf, (int) (id - inp), inp, id);
145638032Speter
145738032Speter	/* output remaining lines */
145838032Speter	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
145938032Speter	{
146038032Speter		*p++ = '\0';
1461363466Sgshapiro		if (SM_ISSPACE(*id))
146238032Speter			id++;
1463168515Sgshapiro		(void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, "-%s");
146464562Sgshapiro		message(cmdbuf, id);
146538032Speter	}
146638032Speter	if (id != NULL)
146738032Speter	{
1468363466Sgshapiro		if (SM_ISSPACE(*id))
146938032Speter			id++;
1470168515Sgshapiro		(void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, " %s");
147164562Sgshapiro		message(cmdbuf, id);
147238032Speter	}
147338032Speter
147438032Speter	protocol = NULL;
147538032Speter	sendinghost = macvalue('s', e);
147690792Sgshapiro
147790792Sgshapiro	/* If quarantining by a connect/ehlo action, save between messages */
147890792Sgshapiro	if (e->e_quarmsg == NULL)
147990792Sgshapiro		smtp.sm_quarmsg = NULL;
148090792Sgshapiro	else
148190792Sgshapiro		smtp.sm_quarmsg = newstr(e->e_quarmsg);
148290792Sgshapiro
148390792Sgshapiro	/* sendinghost's storage must outlive the current envelope */
148490792Sgshapiro	if (sendinghost != NULL)
148590792Sgshapiro		sendinghost = sm_strdup_x(sendinghost);
148690792Sgshapiro	first = true;
148790792Sgshapiro	gothello = false;
148890792Sgshapiro	smtp.sm_gotmail = false;
148938032Speter	for (;;)
149038032Speter	{
149190792Sgshapiro	    SM_TRY
149290792Sgshapiro	    {
149390792Sgshapiro		QuickAbort = false;
149490792Sgshapiro		HoldErrs = false;
149590792Sgshapiro		SuprErrs = false;
149690792Sgshapiro		LogUsrErrs = false;
149790792Sgshapiro		OnlyOneError = true;
149838032Speter		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
1499168515Sgshapiro#if MILTER
1500168515Sgshapiro		milter_cmd_fail = false;
1501363466Sgshapiro#endif
150238032Speter
150338032Speter		/* setup for the read */
150438032Speter		e->e_to = NULL;
150538032Speter		Errors = 0;
150664562Sgshapiro		FileName = NULL;
150790792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
150838032Speter
150938032Speter		/* read the input line */
151038032Speter		SmtpPhase = "server cmd read";
151190792Sgshapiro		sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
151238032Speter
151338032Speter		/* handle errors */
151490792Sgshapiro		if (sm_io_error(OutChannel) ||
1515168515Sgshapiro		    (p = sfgets(inp, sizeof(inp), InChannel,
151664562Sgshapiro				TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
151738032Speter		{
151864562Sgshapiro			char *d;
151964562Sgshapiro
152090792Sgshapiro			d = macvalue(macid("{daemon_name}"), e);
152164562Sgshapiro			if (d == NULL)
152264562Sgshapiro				d = "stdin";
152338032Speter			/* end of file, just die */
152438032Speter			disconnect(1, e);
152564562Sgshapiro
152690792Sgshapiro#if MILTER
152764562Sgshapiro			/* close out milter filters */
152864562Sgshapiro			milter_quit(e);
1529363466Sgshapiro#endif
153064562Sgshapiro
153164562Sgshapiro			message("421 4.4.1 %s Lost input channel from %s",
153238032Speter				MyHostName, CurSmtpClient);
153390792Sgshapiro			if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
153438032Speter				sm_syslog(LOG_NOTICE, e->e_id,
1535110560Sgshapiro					  "lost input channel from %s to %s after %s",
153664562Sgshapiro					  CurSmtpClient, d,
153764562Sgshapiro					  (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
153838032Speter			/*
153942575Speter			**  If have not accepted mail (DATA), do not bounce
154042575Speter			**  bad addresses back to sender.
154138032Speter			*/
154264562Sgshapiro
154338032Speter			if (bitset(EF_CLRQUEUE, e->e_flags))
154438032Speter				e->e_sendqueue = NULL;
154564562Sgshapiro			goto doquit;
154638032Speter		}
154738032Speter
1548168515Sgshapiro		/* also used by "proxy" check below */
1549168515Sgshapiro		inplen = strlen(inp);
1550168515Sgshapiro#if SASL
1551168515Sgshapiro		/*
1552168515Sgshapiro		**  SMTP AUTH requires accepting any length,
1553168515Sgshapiro		**  at least for challenge/response. However, not imposing
1554168515Sgshapiro		**  a limit is a bad idea (denial of service).
1555168515Sgshapiro		*/
1556168515Sgshapiro
1557168515Sgshapiro		if (authenticating != SASL_PROC_AUTH
1558168515Sgshapiro		    && sm_strncasecmp(inp, "AUTH ", 5) != 0
1559168515Sgshapiro		    && inplen > MAXLINE)
1560168515Sgshapiro		{
1561168515Sgshapiro			message("421 4.7.0 %s Command too long, possible attack %s",
1562168515Sgshapiro				MyHostName, CurSmtpClient);
1563168515Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
1564168515Sgshapiro				  "%s: SMTP violation, input too long: %lu",
1565168515Sgshapiro				  CurSmtpClient, (unsigned long) inplen);
1566168515Sgshapiro			goto doquit;
1567168515Sgshapiro		}
1568168515Sgshapiro#endif /* SASL */
1569168515Sgshapiro
157090792Sgshapiro		if (first)
157190792Sgshapiro		{
1572168515Sgshapiro			size_t cmdlen;
1573110560Sgshapiro			int idx;
1574110560Sgshapiro			char *http_cmd;
1575110560Sgshapiro			static char *http_cmds[] = { "GET", "POST",
1576110560Sgshapiro						     "CONNECT", "USER", NULL };
1577110560Sgshapiro
1578110560Sgshapiro			for (idx = 0; (http_cmd = http_cmds[idx]) != NULL;
1579110560Sgshapiro			     idx++)
1580110560Sgshapiro			{
1581110560Sgshapiro				cmdlen = strlen(http_cmd);
1582110560Sgshapiro				if (cmdlen < inplen &&
1583110560Sgshapiro				    sm_strncasecmp(inp, http_cmd, cmdlen) == 0 &&
1584363466Sgshapiro				    SM_ISSPACE(inp[cmdlen]))
1585110560Sgshapiro				{
1586110560Sgshapiro					/* Open proxy, drop it */
1587110560Sgshapiro					message("421 4.7.0 %s Rejecting open proxy %s",
1588110560Sgshapiro						MyHostName, CurSmtpClient);
1589110560Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1590110560Sgshapiro						  "%s: probable open proxy: command=%.40s",
1591110560Sgshapiro						  CurSmtpClient, inp);
1592110560Sgshapiro					goto doquit;
1593110560Sgshapiro				}
1594110560Sgshapiro			}
159590792Sgshapiro			first = false;
159690792Sgshapiro		}
159790792Sgshapiro
159838032Speter		/* clean up end of line */
159990792Sgshapiro		fixcrlf(inp, true);
160038032Speter
160190792Sgshapiro#if PIPELINING
160290792Sgshapiro# if _FFR_NO_PIPE
160390792Sgshapiro		/*
160490792Sgshapiro		**  if there is more input and pipelining is disabled:
160590792Sgshapiro		**	delay ... (and maybe discard the input?)
160690792Sgshapiro		**  XXX this doesn't really work, at least in tests using
160790792Sgshapiro		**  telnet SM_IO_IS_READABLE only returns 1 if there were
160890792Sgshapiro		**  more than 2 input lines available.
160990792Sgshapiro		*/
161090792Sgshapiro
161190792Sgshapiro		if (bitset(SRV_NO_PIPE, features) &&
1612110560Sgshapiro		    sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
161390792Sgshapiro		{
161490792Sgshapiro			if (++np_log < 3)
161590792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
1616203004Sgshapiro					  "unauthorized PIPELINING, sleeping, relay=%.100s",
1617203004Sgshapiro					   CurSmtpClient);
161890792Sgshapiro			sleep(1);
161990792Sgshapiro		}
162090792Sgshapiro
162190792Sgshapiro# endif /* _FFR_NO_PIPE */
162290792Sgshapiro#endif /* PIPELINING */
162390792Sgshapiro
162490792Sgshapiro#if SASL
162564562Sgshapiro		if (authenticating == SASL_PROC_AUTH)
162664562Sgshapiro		{
162790792Sgshapiro# if 0
162864562Sgshapiro			if (*inp == '\0')
162964562Sgshapiro			{
163064562Sgshapiro				authenticating = SASL_NOT_AUTH;
163164562Sgshapiro				message("501 5.5.2 missing input");
1632120256Sgshapiro				RESET_SASLCONN;
163364562Sgshapiro				continue;
163464562Sgshapiro			}
163590792Sgshapiro# endif /* 0 */
163664562Sgshapiro			if (*inp == '*' && *(inp + 1) == '\0')
163764562Sgshapiro			{
163864562Sgshapiro				authenticating = SASL_NOT_AUTH;
163964562Sgshapiro
1640173340Sgshapiro				/* RFC 2554 4. */
164164562Sgshapiro				message("501 5.0.0 AUTH aborted");
1642120256Sgshapiro				RESET_SASLCONN;
164364562Sgshapiro				continue;
164464562Sgshapiro			}
164564562Sgshapiro
164664562Sgshapiro			/* could this be shorter? XXX */
164798121Sgshapiro# if SASL >= 20000
164898121Sgshapiro			in = xalloc(strlen(inp) + 1);
164998121Sgshapiro			result = sasl_decode64(inp, strlen(inp), in,
165098121Sgshapiro					       strlen(inp), &inlen);
165198121Sgshapiro# else /* SASL >= 20000 */
165264562Sgshapiro			out = xalloc(strlen(inp));
165364562Sgshapiro			result = sasl_decode64(inp, strlen(inp), out, &outlen);
165498121Sgshapiro# endif /* SASL >= 20000 */
165564562Sgshapiro			if (result != SASL_OK)
165664562Sgshapiro			{
165764562Sgshapiro				authenticating = SASL_NOT_AUTH;
165864562Sgshapiro
1659173340Sgshapiro				/* RFC 2554 4. */
166064562Sgshapiro				message("501 5.5.4 cannot decode AUTH parameter %s",
166164562Sgshapiro					inp);
166298121Sgshapiro# if SASL >= 20000
166398121Sgshapiro				sm_free(in);
1664363466Sgshapiro# endif
1665120256Sgshapiro				RESET_SASLCONN;
166664562Sgshapiro				continue;
166764562Sgshapiro			}
166864562Sgshapiro
166998121Sgshapiro# if SASL >= 20000
1670363466Sgshapiro			SET_AUTH_USER_TMP(in, inlen);
167198121Sgshapiro			result = sasl_server_step(conn,	in, inlen,
167298121Sgshapiro						  &out, &outlen);
167398121Sgshapiro			sm_free(in);
167498121Sgshapiro# else /* SASL >= 20000 */
1675363466Sgshapiro			SET_AUTH_USER_TMP(out, outlen);
167664562Sgshapiro			result = sasl_server_step(conn,	out, outlen,
167764562Sgshapiro						  &out, &outlen, &errstr);
167898121Sgshapiro# endif /* SASL >= 20000 */
167964562Sgshapiro
168064562Sgshapiro			/* get an OK if we're done */
168164562Sgshapiro			if (result == SASL_OK)
168264562Sgshapiro			{
168364562Sgshapiro  authenticated:
168464562Sgshapiro				message("235 2.0.0 OK Authenticated");
168564562Sgshapiro				authenticating = SASL_IS_AUTH;
168690792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
168790792Sgshapiro					macid("{auth_type}"), auth_type);
168864562Sgshapiro
168998121Sgshapiro# if SASL >= 20000
169098121Sgshapiro				user = macvalue(macid("{auth_authen}"), e);
169198121Sgshapiro
169298121Sgshapiro				/* get security strength (features) */
169398121Sgshapiro				result = sasl_getprop(conn, SASL_SSF,
169498121Sgshapiro						      (const void **) &ssf);
169598121Sgshapiro# else /* SASL >= 20000 */
169664562Sgshapiro				result = sasl_getprop(conn, SASL_USERNAME,
169764562Sgshapiro						      (void **)&user);
169864562Sgshapiro				if (result != SASL_OK)
169964562Sgshapiro				{
170064562Sgshapiro					user = "";
170190792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
170290792Sgshapiro						  A_PERM,
170390792Sgshapiro						  macid("{auth_authen}"), NULL);
170464562Sgshapiro				}
170564562Sgshapiro				else
170664562Sgshapiro				{
170790792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
170890792Sgshapiro						  A_TEMP,
1709132943Sgshapiro						  macid("{auth_authen}"),
1710132943Sgshapiro						  xtextify(user, "<>\")"));
171164562Sgshapiro				}
171264562Sgshapiro
171390792Sgshapiro# if 0
171464562Sgshapiro				/* get realm? */
171564562Sgshapiro				sasl_getprop(conn, SASL_REALM, (void **) &data);
1716363466Sgshapiro# endif
171764562Sgshapiro
171864562Sgshapiro				/* get security strength (features) */
171964562Sgshapiro				result = sasl_getprop(conn, SASL_SSF,
172064562Sgshapiro						      (void **) &ssf);
172198121Sgshapiro# endif /* SASL >= 20000 */
172264562Sgshapiro				if (result != SASL_OK)
172364562Sgshapiro				{
172490792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
172590792Sgshapiro						  A_PERM,
172690792Sgshapiro						  macid("{auth_ssf}"), "0");
172764562Sgshapiro					ssf = NULL;
172864562Sgshapiro				}
172964562Sgshapiro				else
173064562Sgshapiro				{
173164562Sgshapiro					char pbuf[8];
173264562Sgshapiro
1733168515Sgshapiro					(void) sm_snprintf(pbuf, sizeof(pbuf),
173490792Sgshapiro							   "%u", *ssf);
173590792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
173690792Sgshapiro						  A_TEMP,
173790792Sgshapiro						  macid("{auth_ssf}"), pbuf);
173864562Sgshapiro					if (tTd(95, 8))
173990792Sgshapiro						sm_dprintf("AUTH auth_ssf: %u\n",
174090792Sgshapiro							   *ssf);
174164562Sgshapiro				}
174290792Sgshapiro
1743285229Sgshapiro				protocol = GET_PROTOCOL();
1744285229Sgshapiro
174564562Sgshapiro				/*
174690792Sgshapiro				**  Only switch to encrypted connection
174764562Sgshapiro				**  if a security layer has been negotiated
174864562Sgshapiro				*/
174990792Sgshapiro
175064562Sgshapiro				if (ssf != NULL && *ssf > 0)
175164562Sgshapiro				{
1752159609Sgshapiro					int tmo;
1753159609Sgshapiro
175464562Sgshapiro					/*
175590792Sgshapiro					**  Convert I/O layer to use SASL.
175690792Sgshapiro					**  If the call fails, the connection
175790792Sgshapiro					**  is aborted.
175864562Sgshapiro					*/
175990792Sgshapiro
1760159609Sgshapiro					tmo = TimeOuts.to_datablock * 1000;
176190792Sgshapiro					if (sfdcsasl(&InChannel, &OutChannel,
1762159609Sgshapiro						     conn, tmo) == 0)
176364562Sgshapiro					{
176464562Sgshapiro						/* restart dialogue */
176564562Sgshapiro						n_helo = 0;
176694334Sgshapiro# if PIPELINING
176790792Sgshapiro						(void) sm_io_autoflush(InChannel,
176890792Sgshapiro								       OutChannel);
176994334Sgshapiro# endif /* PIPELINING */
177064562Sgshapiro					}
177164562Sgshapiro					else
177264562Sgshapiro						syserr("503 5.3.3 SASL TLS failed");
177364562Sgshapiro				}
177490792Sgshapiro
177590792Sgshapiro				/* NULL pointer ok since it's our function */
177690792Sgshapiro				if (LogLevel > 8)
177764562Sgshapiro					sm_syslog(LOG_INFO, NOQID,
1778110560Sgshapiro						  "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d",
177990792Sgshapiro						  CurSmtpClient,
178090792Sgshapiro						  shortenstring(user, 128),
178190792Sgshapiro						  auth_type, *ssf);
178264562Sgshapiro			}
178364562Sgshapiro			else if (result == SASL_CONTINUE)
178464562Sgshapiro			{
1785363466Sgshapiro				SET_AUTH_USER;
1786363466Sgshapiro
178764562Sgshapiro				len = ENC64LEN(outlen);
178864562Sgshapiro				out2 = xalloc(len);
178964562Sgshapiro				result = sasl_encode64(out, outlen, out2, len,
179090792Sgshapiro						       &out2len);
179164562Sgshapiro				if (result != SASL_OK)
179264562Sgshapiro				{
179364562Sgshapiro					/* correct code? XXX */
179464562Sgshapiro					/* 454 Temp. authentication failure */
179564562Sgshapiro					message("454 4.5.4 Internal error: unable to encode64");
179664562Sgshapiro					if (LogLevel > 5)
179764562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
1798203004Sgshapiro							  "AUTH encode64 error [%d for \"%s\"], relay=%.100s",
1799203004Sgshapiro							  result, out,
1800203004Sgshapiro							  CurSmtpClient);
180164562Sgshapiro					/* start over? */
180264562Sgshapiro					authenticating = SASL_NOT_AUTH;
180364562Sgshapiro				}
180464562Sgshapiro				else
180564562Sgshapiro				{
180664562Sgshapiro					message("334 %s", out2);
180764562Sgshapiro					if (tTd(95, 2))
180890792Sgshapiro						sm_dprintf("AUTH continue: msg='%s' len=%u\n",
180990792Sgshapiro							   out2, out2len);
181064562Sgshapiro				}
181198121Sgshapiro# if SASL >= 20000
181298121Sgshapiro				sm_free(out2);
1813363466Sgshapiro# endif
181464562Sgshapiro			}
181564562Sgshapiro			else
181664562Sgshapiro			{
1817363466Sgshapiro
181898121Sgshapiro# if SASL >= 20000
1819363466Sgshapiro#  define SASLERR sasl_errdetail(conn)
1820363466Sgshapiro# else
1821363466Sgshapiro#  define SASLERR errstr == NULL ? "" : errstr
1822363466Sgshapiro# endif
1823363466Sgshapiro#define LOGAUTHFAIL	\
1824363466Sgshapiro	do	\
1825363466Sgshapiro	{	\
1826363466Sgshapiro		SET_AUTH_USER_CONDITIONALLY	\
1827363466Sgshapiro		message("535 5.7.0 authentication failed");	\
1828363466Sgshapiro		if (LogLevel >= 9)	\
1829363466Sgshapiro			sm_syslog(LOG_WARNING, e->e_id,	\
1830363466Sgshapiro				  "AUTH failure (%s): %s (%d) %s%s%.*s, relay=%.100s",	\
1831363466Sgshapiro				  (auth_type != NULL) ? auth_type : "unknown", \
1832363466Sgshapiro				  sasl_errstring(result, NULL, NULL),	\
1833363466Sgshapiro				  result,	\
1834363466Sgshapiro				  SASLERR, \
1835363466Sgshapiro				  LOG_AUTH_FAIL_USER,	\
1836363466Sgshapiro				  CurSmtpClient);	\
1837363466Sgshapiro		RESET_SASLCONN;	\
1838363466Sgshapiro	} while (0)
1839363466Sgshapiro
1840363466Sgshapiro
1841363466Sgshapiro				LOGAUTHFAIL;
184264562Sgshapiro				authenticating = SASL_NOT_AUTH;
184364562Sgshapiro			}
184464562Sgshapiro		}
184564562Sgshapiro		else
184664562Sgshapiro		{
184764562Sgshapiro			/* don't want to do any of this if authenticating */
184890792Sgshapiro#endif /* SASL */
184964562Sgshapiro
185038032Speter		/* echo command to transcript */
185138032Speter		if (e->e_xfp != NULL)
185290792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
185390792Sgshapiro					     "<<< %s\n", inp);
185438032Speter
185590792Sgshapiro		if (LogLevel > 14)
185690792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
185738032Speter
185838032Speter		/* break off command */
1859363466Sgshapiro		for (p = inp; SM_ISSPACE(*p); p++)
186038032Speter			continue;
186138032Speter		cmd = cmdbuf;
186238032Speter		while (*p != '\0' &&
1863363466Sgshapiro		       !(SM_ISSPACE(*p)) &&
1864168515Sgshapiro		       cmd < &cmdbuf[sizeof(cmdbuf) - 2])
186538032Speter			*cmd++ = *p++;
186638032Speter		*cmd = '\0';
186738032Speter
186838032Speter		/* throw away leading whitespace */
186990792Sgshapiro		SKIP_SPACE(p);
187038032Speter
187138032Speter		/* decode command */
187264562Sgshapiro		for (c = CmdTab; c->cmd_name != NULL; c++)
187338032Speter		{
187490792Sgshapiro			if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
187538032Speter				break;
187638032Speter		}
187738032Speter
187838032Speter		/* reset errors */
187938032Speter		errno = 0;
188038032Speter
188190792Sgshapiro		/* check whether a "non-null" command has been used */
188290792Sgshapiro		switch (c->cmd_code)
188390792Sgshapiro		{
188490792Sgshapiro#if SASL
188590792Sgshapiro		  case CMDAUTH:
188690792Sgshapiro			/* avoid information leak; take first two words? */
188790792Sgshapiro			q = "AUTH";
188890792Sgshapiro			break;
188990792Sgshapiro#endif /* SASL */
189090792Sgshapiro
189190792Sgshapiro		  case CMDMAIL:
189290792Sgshapiro		  case CMDEXPN:
189390792Sgshapiro		  case CMDVRFY:
189490792Sgshapiro		  case CMDETRN:
189590792Sgshapiro			lognullconnection = false;
189690792Sgshapiro			/* FALLTHROUGH */
189790792Sgshapiro		  default:
189890792Sgshapiro			q = inp;
189990792Sgshapiro			break;
190090792Sgshapiro		}
190190792Sgshapiro
190290792Sgshapiro		if (e->e_id == NULL)
190390792Sgshapiro			sm_setproctitle(true, e, "%s: %.80s",
190490792Sgshapiro					CurSmtpClient, q);
190590792Sgshapiro		else
190690792Sgshapiro			sm_setproctitle(true, e, "%s %s: %.80s",
190790792Sgshapiro					qid_printname(e),
190890792Sgshapiro					CurSmtpClient, q);
190990792Sgshapiro
191038032Speter		/*
191138032Speter		**  Process command.
191238032Speter		**
191338032Speter		**	If we are running as a null server, return 550
191490792Sgshapiro		**	to almost everything.
191538032Speter		*/
191638032Speter
191764562Sgshapiro		if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
191838032Speter		{
191964562Sgshapiro			switch (c->cmd_code)
192038032Speter			{
192138032Speter			  case CMDQUIT:
192238032Speter			  case CMDHELO:
192338032Speter			  case CMDEHLO:
192438032Speter			  case CMDNOOP:
192564562Sgshapiro			  case CMDRSET:
1926125820Sgshapiro			  case CMDERROR:
192738032Speter				/* process normally */
192838032Speter				break;
192938032Speter
193064562Sgshapiro			  case CMDETRN:
193164562Sgshapiro				if (bitnset(D_ETRNONLY, d_flags) &&
193264562Sgshapiro				    nullserver == NULL)
193364562Sgshapiro					break;
193490792Sgshapiro				DELAY_CONN("ETRN");
193580785Sgshapiro				/* FALLTHROUGH */
193664562Sgshapiro
193738032Speter			  default:
193890792Sgshapiro#if MAXBADCOMMANDS > 0
193990792Sgshapiro				/* theoretically this could overflow */
194090792Sgshapiro				if (nullserver != NULL &&
194190792Sgshapiro				    ++n_badcmds > MAXBADCOMMANDS)
194264562Sgshapiro				{
194390792Sgshapiro					message("421 4.7.0 %s Too many bad commands; closing connection",
194490792Sgshapiro						MyHostName);
194590792Sgshapiro
194690792Sgshapiro					/* arrange to ignore send list */
194790792Sgshapiro					e->e_sendqueue = NULL;
194890792Sgshapiro					goto doquit;
194964562Sgshapiro				}
195090792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
195164562Sgshapiro				if (nullserver != NULL)
195264562Sgshapiro				{
195364562Sgshapiro					if (ISSMTPREPLY(nullserver))
1954363466Sgshapiro					{
1955363466Sgshapiro						/* Can't use ("%s", ...) due to usrerr() requirements */
195664562Sgshapiro						usrerr(nullserver);
1957363466Sgshapiro					}
195864562Sgshapiro					else
1959363466Sgshapiro					{
196090792Sgshapiro						usrerr("550 5.0.0 %s",
196190792Sgshapiro						       nullserver);
1962363466Sgshapiro					}
196364562Sgshapiro				}
196464562Sgshapiro				else
196564562Sgshapiro					usrerr("452 4.4.5 Insufficient disk space; try again later");
196638032Speter				continue;
196738032Speter			}
196838032Speter		}
196938032Speter
197064562Sgshapiro		switch (c->cmd_code)
197138032Speter		{
197290792Sgshapiro#if SASL
197364562Sgshapiro		  case CMDAUTH: /* sasl */
197490792Sgshapiro			DELAY_CONN("AUTH");
197590792Sgshapiro			if (!sasl_ok || n_mechs <= 0)
197664562Sgshapiro			{
197764562Sgshapiro				message("503 5.3.3 AUTH not available");
197864562Sgshapiro				break;
197964562Sgshapiro			}
198064562Sgshapiro			if (authenticating == SASL_IS_AUTH)
198164562Sgshapiro			{
198264562Sgshapiro				message("503 5.5.0 Already Authenticated");
198364562Sgshapiro				break;
198464562Sgshapiro			}
198590792Sgshapiro			if (smtp.sm_gotmail)
198664562Sgshapiro			{
198764562Sgshapiro				message("503 5.5.0 AUTH not permitted during a mail transaction");
198864562Sgshapiro				break;
198964562Sgshapiro			}
199064562Sgshapiro			if (tempfail)
199164562Sgshapiro			{
199264562Sgshapiro				if (LogLevel > 9)
199364562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1994110560Sgshapiro						  "SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)",
199564562Sgshapiro						  p, CurSmtpClient);
1996132943Sgshapiro				usrerr("454 4.3.0 Please try again later");
199764562Sgshapiro				break;
199864562Sgshapiro			}
199964562Sgshapiro
200090792Sgshapiro			ismore = false;
200164562Sgshapiro
200264562Sgshapiro			/* crude way to avoid crack attempts */
2003132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1,
2004132943Sgshapiro							true, "AUTH", e));
200564562Sgshapiro
200690792Sgshapiro			/* make sure mechanism (p) is a valid string */
200764562Sgshapiro			for (q = p; *q != '\0' && isascii(*q); q++)
200864562Sgshapiro			{
200964562Sgshapiro				if (isspace(*q))
201064562Sgshapiro				{
201164562Sgshapiro					*q = '\0';
2012363466Sgshapiro					while (*++q != '\0' && SM_ISSPACE(*q))
201364562Sgshapiro						continue;
201464562Sgshapiro					*(q - 1) = '\0';
201564562Sgshapiro					ismore = (*q != '\0');
201664562Sgshapiro					break;
201764562Sgshapiro				}
201864562Sgshapiro			}
201964562Sgshapiro
202098121Sgshapiro			if (*p == '\0')
202198121Sgshapiro			{
202298121Sgshapiro				message("501 5.5.2 AUTH mechanism must be specified");
202398121Sgshapiro				break;
202498121Sgshapiro			}
202598121Sgshapiro
202664562Sgshapiro			/* check whether mechanism is available */
202764562Sgshapiro			if (iteminlist(p, mechlist, " ") == NULL)
202864562Sgshapiro			{
202998121Sgshapiro				message("504 5.3.3 AUTH mechanism %.32s not available",
203064562Sgshapiro					p);
203164562Sgshapiro				break;
203264562Sgshapiro			}
203364562Sgshapiro
2034173340Sgshapiro			/*
2035173340Sgshapiro			**  RFC 2554 4.
2036173340Sgshapiro			**  Unlike a zero-length client answer to a
2037173340Sgshapiro			**  334 reply, a zero- length initial response
2038173340Sgshapiro			**  is sent as a single equals sign ("=").
2039173340Sgshapiro			*/
2040173340Sgshapiro
2041173340Sgshapiro			if (ismore && *q == '=' && *(q + 1) == '\0')
204264562Sgshapiro			{
2043173340Sgshapiro				/* will be free()d, don't use in=""; */
2044173340Sgshapiro				in = xalloc(1);
2045173340Sgshapiro				*in = '\0';
2046173340Sgshapiro				inlen = 0;
2047173340Sgshapiro			}
2048173340Sgshapiro			else if (ismore)
2049173340Sgshapiro			{
205064562Sgshapiro				/* could this be shorter? XXX */
205198121Sgshapiro# if SASL >= 20000
205298121Sgshapiro				in = xalloc(strlen(q) + 1);
2053102528Sgshapiro				result = sasl_decode64(q, strlen(q), in,
205498121Sgshapiro						       strlen(q), &inlen);
205598121Sgshapiro# else /* SASL >= 20000 */
205690792Sgshapiro				in = sm_rpool_malloc(e->e_rpool, strlen(q));
205764562Sgshapiro				result = sasl_decode64(q, strlen(q), in,
205890792Sgshapiro						       &inlen);
205998121Sgshapiro# endif /* SASL >= 20000 */
2060363466Sgshapiro
206164562Sgshapiro				if (result != SASL_OK)
206264562Sgshapiro				{
206364562Sgshapiro					message("501 5.5.4 cannot BASE64 decode '%s'",
206464562Sgshapiro						q);
206564562Sgshapiro					if (LogLevel > 5)
206664562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
2067203004Sgshapiro							  "AUTH decode64 error [%d for \"%s\"], relay=%.100s",
2068203004Sgshapiro							  result, q,
2069203004Sgshapiro							  CurSmtpClient);
207064562Sgshapiro					/* start over? */
207164562Sgshapiro					authenticating = SASL_NOT_AUTH;
207298121Sgshapiro# if SASL >= 20000
207398121Sgshapiro					sm_free(in);
2074363466Sgshapiro# endif
207564562Sgshapiro					in = NULL;
207664562Sgshapiro					inlen = 0;
207764562Sgshapiro					break;
207864562Sgshapiro				}
2079363466Sgshapiro				SET_AUTH_USER_TMP(in, inlen);
208064562Sgshapiro			}
208164562Sgshapiro			else
208264562Sgshapiro			{
208364562Sgshapiro				in = NULL;
208464562Sgshapiro				inlen = 0;
208564562Sgshapiro			}
208664562Sgshapiro
208764562Sgshapiro			/* see if that auth type exists */
208898121Sgshapiro# if SASL >= 20000
2089102528Sgshapiro			result = sasl_server_start(conn, p, in, inlen,
209098121Sgshapiro						   &out, &outlen);
2091363466Sgshapiro			SM_FREE(in);
209298121Sgshapiro# else /* SASL >= 20000 */
209364562Sgshapiro			result = sasl_server_start(conn, p, in, inlen,
209464562Sgshapiro						   &out, &outlen, &errstr);
209598121Sgshapiro# endif /* SASL >= 20000 */
209664562Sgshapiro
2097363466Sgshapiro			if (p != NULL)
2098363466Sgshapiro				auth_type = newstr(p);
209964562Sgshapiro			if (result != SASL_OK && result != SASL_CONTINUE)
210064562Sgshapiro			{
2101363466Sgshapiro				LOGAUTHFAIL;
210264562Sgshapiro				break;
210364562Sgshapiro			}
210464562Sgshapiro
210564562Sgshapiro			if (result == SASL_OK)
210664562Sgshapiro			{
210764562Sgshapiro				/* ugly, but same code */
210864562Sgshapiro				goto authenticated;
210964562Sgshapiro				/* authenticated by the initial response */
211064562Sgshapiro			}
211164562Sgshapiro
2112363466Sgshapiro			SET_AUTH_USER;
2113363466Sgshapiro
211464562Sgshapiro			/* len is at least 2 */
211564562Sgshapiro			len = ENC64LEN(outlen);
211664562Sgshapiro			out2 = xalloc(len);
211764562Sgshapiro			result = sasl_encode64(out, outlen, out2, len,
211890792Sgshapiro					       &out2len);
211964562Sgshapiro
212064562Sgshapiro			if (result != SASL_OK)
212164562Sgshapiro			{
212264562Sgshapiro				message("454 4.5.4 Temporary authentication failure");
212364562Sgshapiro				if (LogLevel > 5)
212464562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
212590792Sgshapiro						  "AUTH encode64 error [%d for \"%s\"]",
212664562Sgshapiro						  result, out);
212764562Sgshapiro
212864562Sgshapiro				/* start over? */
212964562Sgshapiro				authenticating = SASL_NOT_AUTH;
2130120256Sgshapiro				RESET_SASLCONN;
213164562Sgshapiro			}
213264562Sgshapiro			else
213364562Sgshapiro			{
213464562Sgshapiro				message("334 %s", out2);
213564562Sgshapiro				authenticating = SASL_PROC_AUTH;
213664562Sgshapiro			}
213798121Sgshapiro# if SASL >= 20000
213898121Sgshapiro			sm_free(out2);
2139363466Sgshapiro# endif
214064562Sgshapiro			break;
214190792Sgshapiro#endif /* SASL */
214264562Sgshapiro
214390792Sgshapiro#if STARTTLS
214464562Sgshapiro		  case CMDSTLS: /* starttls */
214590792Sgshapiro			DELAY_CONN("STARTTLS");
214664562Sgshapiro			if (*p != '\0')
214764562Sgshapiro			{
214864562Sgshapiro				message("501 5.5.2 Syntax error (no parameters allowed)");
214964562Sgshapiro				break;
215064562Sgshapiro			}
215190792Sgshapiro			if (!bitset(SRV_OFFER_TLS, features))
215264562Sgshapiro			{
215364562Sgshapiro				message("503 5.5.0 TLS not available");
215464562Sgshapiro				break;
215564562Sgshapiro			}
2156363466Sgshapiro  starttls:
215777349Sgshapiro			if (!tls_ok_srv)
215864562Sgshapiro			{
215964562Sgshapiro				message("454 4.3.3 TLS not available after start");
216064562Sgshapiro				break;
216164562Sgshapiro			}
216290792Sgshapiro			if (smtp.sm_gotmail)
216364562Sgshapiro			{
216464562Sgshapiro				message("503 5.5.0 TLS not permitted during a mail transaction");
216564562Sgshapiro				break;
216664562Sgshapiro			}
216764562Sgshapiro			if (tempfail)
216864562Sgshapiro			{
216964562Sgshapiro				if (LogLevel > 9)
217064562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2171110560Sgshapiro						  "SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)",
217264562Sgshapiro						  p, CurSmtpClient);
2173132943Sgshapiro				usrerr("454 4.7.0 Please try again later");
217464562Sgshapiro				break;
217564562Sgshapiro			}
2176363466Sgshapiro			if (!TLS_set_engine(SSLEngine, false))
2177223067Sgshapiro			{
2178363466Sgshapiro				sm_syslog(LOG_ERR, NOQID,
2179363466Sgshapiro					  "STARTTLS=server, engine=%s, TLS_set_engine=failed",
2180363466Sgshapiro					  SSLEngine);
2181363466Sgshapiro				tls_ok_srv = false;
2182363466Sgshapiro				message("454 4.3.3 TLS not available right now");
2183363466Sgshapiro				break;
2184223067Sgshapiro			}
218564562Sgshapiro# if TLS_NO_RSA
218664562Sgshapiro			/*
218764562Sgshapiro			**  XXX do we need a temp key ?
218864562Sgshapiro			*/
2189363466Sgshapiro# endif
219090792Sgshapiro
219190792Sgshapiro# if TLS_VRFY_PER_CTX
219290792Sgshapiro			/*
219390792Sgshapiro			**  Note: this sets the verification globally
219490792Sgshapiro			**  (per SSL_CTX)
219590792Sgshapiro			**  it's ok since it applies only to one transaction
219690792Sgshapiro			*/
219790792Sgshapiro
219890792Sgshapiro			TLS_VERIFY_CLIENT();
219990792Sgshapiro# endif /* TLS_VRFY_PER_CTX */
220090792Sgshapiro
2201363466Sgshapiro#define SMTLSFAILED				\
2202363466Sgshapiro	do {					\
2203363466Sgshapiro		SM_SSL_FREE(srv_ssl);		\
2204363466Sgshapiro		goto tls_done;			\
2205363466Sgshapiro	} while (0)
2206363466Sgshapiro
220764562Sgshapiro			if (srv_ssl != NULL)
220864562Sgshapiro				SSL_clear(srv_ssl);
220964562Sgshapiro			else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
221064562Sgshapiro			{
221164562Sgshapiro				message("454 4.3.3 TLS not available: error generating SSL handle");
2212363466Sgshapiro				tlslogerr(LOG_WARNING, 8, "server");
221390792Sgshapiro				goto tls_done;
221464562Sgshapiro			}
2215363466Sgshapiro			if (get_tls_se_options(e, srv_ssl, &tlsi_ctx, true)
2216363466Sgshapiro			    != 0)
2217285229Sgshapiro			{
2218285229Sgshapiro				message("454 4.3.3 TLS not available: error setting options");
2219363466Sgshapiro				SMTLSFAILED;
2220285229Sgshapiro			}
2221363466Sgshapiro			r = SSL_set_ex_data(srv_ssl, TLSsslidx, &tlsi_ctx);
2222363466Sgshapiro			if (0 == r)
2223363466Sgshapiro			{
2224363466Sgshapiro				if (LogLevel > 5)
2225363466Sgshapiro				{
2226363466Sgshapiro					sm_syslog(LOG_ERR, NOQID,
2227363466Sgshapiro						"STARTTLS=server, error: SSL_set_ex_data failed=%d",
2228363466Sgshapiro						r);
2229363466Sgshapiro					tlslogerr(LOG_WARNING, 9, "server");
2230363466Sgshapiro				}
2231363466Sgshapiro				SMTLSFAILED;
2232363466Sgshapiro			}
2233285229Sgshapiro
223490792Sgshapiro# if !TLS_VRFY_PER_CTX
223590792Sgshapiro			/*
223690792Sgshapiro			**  this could be used if it were possible to set
223790792Sgshapiro			**  verification per SSL (connection)
223890792Sgshapiro			**  not just per SSL_CTX (global)
223990792Sgshapiro			*/
224090792Sgshapiro
224190792Sgshapiro			TLS_VERIFY_CLIENT();
224290792Sgshapiro# endif /* !TLS_VRFY_PER_CTX */
224390792Sgshapiro
224490792Sgshapiro			rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
224590792Sgshapiro			wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
224690792Sgshapiro
224766494Sgshapiro			if (rfd < 0 || wfd < 0 ||
224866494Sgshapiro			    SSL_set_rfd(srv_ssl, rfd) <= 0 ||
224966494Sgshapiro			    SSL_set_wfd(srv_ssl, wfd) <= 0)
225064562Sgshapiro			{
225164562Sgshapiro				message("454 4.3.3 TLS not available: error set fd");
2252363466Sgshapiro				SMTLSFAILED;
225364562Sgshapiro			}
225490792Sgshapiro			if (!smtps)
225590792Sgshapiro				message("220 2.0.0 Ready to start TLS");
225690792Sgshapiro# if PIPELINING
225790792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
2258363466Sgshapiro# endif
225990792Sgshapiro
226064562Sgshapiro			SSL_set_accept_state(srv_ssl);
226164562Sgshapiro
226290792Sgshapiro			tlsstart = curtime();
2263363466Sgshapiro
2264363466Sgshapiro			ssl_err = SSL_ERROR_WANT_READ;
2265363466Sgshapiro			save_errno = 0;
2266363466Sgshapiro			do
226764562Sgshapiro			{
2268363466Sgshapiro				tlsret = tls_retry(srv_ssl, rfd, wfd, tlsstart,
2269157001Sgshapiro						TimeOuts.to_starttls, ssl_err,
2270157001Sgshapiro						"server");
2271363466Sgshapiro				if (tlsret <= 0)
227264562Sgshapiro				{
2273363466Sgshapiro					if (LogLevel > 5)
2274363466Sgshapiro					{
2275363466Sgshapiro						unsigned long l;
2276363466Sgshapiro						const char *sr;
2277244833Sgshapiro
2278363466Sgshapiro						l = ERR_peek_error();
2279363466Sgshapiro						sr = ERR_reason_error_string(l);
2280363466Sgshapiro
2281363466Sgshapiro						sm_syslog(LOG_WARNING, NOQID,
2282363466Sgshapiro							  "STARTTLS=server, error: accept failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d, relay=%.100s",
2283363466Sgshapiro							  r, sr == NULL ? "unknown"
2284363466Sgshapiro									: sr,
2285363466Sgshapiro							  ssl_err, save_errno,
2286363466Sgshapiro							  tlsret, CurSmtpClient);
2287363466Sgshapiro						tlslogerr(LOG_WARNING, 9, "server");
2288363466Sgshapiro					}
2289363466Sgshapiro					tls_ok_srv = false;
2290363466Sgshapiro					SM_SSL_FREE(srv_ssl);
2291363466Sgshapiro
2292363466Sgshapiro					/*
2293363466Sgshapiro					**  according to the next draft of
2294363466Sgshapiro					**  RFC 2487 the connection should
2295363466Sgshapiro					**  be dropped
2296363466Sgshapiro					**
2297363466Sgshapiro					**  arrange to ignore any current
2298363466Sgshapiro					**  send list
2299363466Sgshapiro					*/
2300363466Sgshapiro
2301363466Sgshapiro					e->e_sendqueue = NULL;
2302363466Sgshapiro					goto doquit;
230364562Sgshapiro				}
230464562Sgshapiro
2305363466Sgshapiro				r = SSL_accept(srv_ssl);
2306363466Sgshapiro				save_errno = 0;
2307363466Sgshapiro				if (r <= 0)
2308363466Sgshapiro					ssl_err = SSL_get_error(srv_ssl, r);
2309363466Sgshapiro			} while (r <= 0);
231064562Sgshapiro
231164562Sgshapiro			/* ignore return code for now, it's in {verify} */
231290792Sgshapiro			(void) tls_get_info(srv_ssl, true,
231390792Sgshapiro					    CurSmtpClient,
231490792Sgshapiro					    &BlankEnvelope.e_macro,
231590792Sgshapiro					    bitset(SRV_VRFY_CLT, features));
231664562Sgshapiro
231764562Sgshapiro			/*
231864562Sgshapiro			**  call Stls_client to find out whether
231964562Sgshapiro			**  to accept the connection from the client
232064562Sgshapiro			*/
232164562Sgshapiro
232264562Sgshapiro			saveQuickAbort = QuickAbort;
232364562Sgshapiro			saveSuprErrs = SuprErrs;
232490792Sgshapiro			SuprErrs = true;
232590792Sgshapiro			QuickAbort = false;
232664562Sgshapiro			if (rscheck("tls_client",
232790792Sgshapiro				     macvalue(macid("{verify}"), e),
2328102528Sgshapiro				     "STARTTLS", e,
2329102528Sgshapiro				     RSF_RMCOMM|RSF_COUNT,
2330285229Sgshapiro				     5, NULL, NOQID, NULL, NULL) != EX_OK ||
233190792Sgshapiro			    Errors > 0)
233264562Sgshapiro			{
233364562Sgshapiro				extern char MsgBuf[];
233464562Sgshapiro
233564562Sgshapiro				if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
233664562Sgshapiro					nullserver = newstr(MsgBuf);
233764562Sgshapiro				else
233864562Sgshapiro					nullserver = "503 5.7.0 Authentication required.";
233964562Sgshapiro			}
234064562Sgshapiro			QuickAbort = saveQuickAbort;
234164562Sgshapiro			SuprErrs = saveSuprErrs;
234264562Sgshapiro
234390792Sgshapiro			tls_ok_srv = false;	/* don't offer STARTTLS again */
234464562Sgshapiro			n_helo = 0;
234590792Sgshapiro# if SASL
234664562Sgshapiro			if (sasl_ok)
234764562Sgshapiro			{
2348132943Sgshapiro				int cipher_bits;
2349132943Sgshapiro				bool verified;
2350132943Sgshapiro				char *s, *v, *c;
235164562Sgshapiro
235290792Sgshapiro				s = macvalue(macid("{cipher_bits}"), e);
2353132943Sgshapiro				v = macvalue(macid("{verify}"), e);
2354132943Sgshapiro				c = macvalue(macid("{cert_subject}"), e);
2355132943Sgshapiro				verified = (v != NULL && strcmp(v, "OK") == 0);
2356132943Sgshapiro				if (s != NULL && (cipher_bits = atoi(s)) > 0)
2357132943Sgshapiro				{
235898121Sgshapiro#  if SASL >= 20000
2359132943Sgshapiro					ext_ssf = cipher_bits;
2360132943Sgshapiro					auth_id = verified ? c : NULL;
2361132943Sgshapiro					sasl_ok = ((sasl_setprop(conn,
2362132943Sgshapiro							SASL_SSF_EXTERNAL,
2363132943Sgshapiro							&ext_ssf) == SASL_OK) &&
2364132943Sgshapiro						   (sasl_setprop(conn,
2365132943Sgshapiro							SASL_AUTH_EXTERNAL,
2366132943Sgshapiro							auth_id) == SASL_OK));
236798121Sgshapiro#  else /* SASL >= 20000 */
2368132943Sgshapiro					ext_ssf.ssf = cipher_bits;
2369132943Sgshapiro					ext_ssf.auth_id = verified ? c : NULL;
2370132943Sgshapiro					sasl_ok = sasl_setprop(conn,
2371132943Sgshapiro							SASL_SSF_EXTERNAL,
2372132943Sgshapiro							&ext_ssf) == SASL_OK;
237398121Sgshapiro#  endif /* SASL >= 20000 */
237464562Sgshapiro					mechlist = NULL;
237564562Sgshapiro					if (sasl_ok)
237664562Sgshapiro						n_mechs = saslmechs(conn,
237764562Sgshapiro								    &mechlist);
237864562Sgshapiro				}
237964562Sgshapiro			}
238090792Sgshapiro# endif /* SASL */
238164562Sgshapiro
238264562Sgshapiro			/* switch to secure connection */
238390792Sgshapiro			if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
238490792Sgshapiro			{
238590792Sgshapiro				tls_active = true;
238690792Sgshapiro# if PIPELINING
238790792Sgshapiro				(void) sm_io_autoflush(InChannel, OutChannel);
2388363466Sgshapiro# endif
238990792Sgshapiro			}
239064562Sgshapiro			else
239164562Sgshapiro			{
239264562Sgshapiro				/*
239364562Sgshapiro				**  XXX this is an internal error
239464562Sgshapiro				**  how to deal with it?
239564562Sgshapiro				**  we can't generate an error message
239664562Sgshapiro				**  since the other side switched to an
239764562Sgshapiro				**  encrypted layer, but we could not...
239864562Sgshapiro				**  just "hang up"?
239964562Sgshapiro				*/
240090792Sgshapiro
240164562Sgshapiro				nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
240290792Sgshapiro				syserr("STARTTLS: can't switch to encrypted layer");
240364562Sgshapiro			}
240490792Sgshapiro		  tls_done:
240590792Sgshapiro			if (smtps)
240690792Sgshapiro			{
240790792Sgshapiro				if (tls_active)
240890792Sgshapiro					goto greeting;
240990792Sgshapiro				else
241090792Sgshapiro					goto doquit;
241190792Sgshapiro			}
241264562Sgshapiro			break;
241390792Sgshapiro#endif /* STARTTLS */
241464562Sgshapiro
241538032Speter		  case CMDHELO:		/* hello -- introduce yourself */
241638032Speter		  case CMDEHLO:		/* extended hello */
241790792Sgshapiro			DELAY_CONN("EHLO");
241864562Sgshapiro			if (c->cmd_code == CMDEHLO)
241938032Speter			{
2420285229Sgshapiro				protocol = GET_PROTOCOL();
242138032Speter				SmtpPhase = "server EHLO";
242238032Speter			}
242338032Speter			else
242438032Speter			{
242538032Speter				protocol = "SMTP";
242638032Speter				SmtpPhase = "server HELO";
242738032Speter			}
242838032Speter
242938032Speter			/* avoid denial-of-service */
2430132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
2431132943Sgshapiro							true, "HELO/EHLO", e));
243238032Speter
243390792Sgshapiro#if 0
243490792Sgshapiro			/* RFC2821 4.1.4 allows duplicate HELO/EHLO */
243538032Speter			/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
243638032Speter			if (gothello)
243738032Speter			{
243838032Speter				usrerr("503 %s Duplicate HELO/EHLO",
243973188Sgshapiro				       MyHostName);
244038032Speter				break;
244138032Speter			}
244290792Sgshapiro#endif /* 0 */
244338032Speter
244438032Speter			/* check for valid domain name (re 1123 5.2.5) */
244538032Speter			if (*p == '\0' && !AllowBogusHELO)
244638032Speter			{
244738032Speter				usrerr("501 %s requires domain address",
244838032Speter					cmdbuf);
244938032Speter				break;
245038032Speter			}
245138032Speter
245238032Speter			/* check for long domain name (hides Received: info) */
245338032Speter			if (strlen(p) > MAXNAME)
245438032Speter			{
245538032Speter				usrerr("501 Invalid domain name");
245664562Sgshapiro				if (LogLevel > 9)
245764562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
2458110560Sgshapiro						  "invalid domain name (too long) from %s",
245964562Sgshapiro						  CurSmtpClient);
246038032Speter				break;
246138032Speter			}
246238032Speter
246398121Sgshapiro			ok = true;
246438032Speter			for (q = p; *q != '\0'; q++)
246538032Speter			{
246638032Speter				if (!isascii(*q))
246738032Speter					break;
246838032Speter				if (isalnum(*q))
246938032Speter					continue;
247038032Speter				if (isspace(*q))
247138032Speter				{
247238032Speter					*q = '\0';
247398121Sgshapiro
247498121Sgshapiro					/* only complain if strict check */
247598121Sgshapiro					ok = AllowBogusHELO;
2476141858Sgshapiro
2477141858Sgshapiro					/* allow trailing whitespace */
2478141858Sgshapiro					while (!ok && *++q != '\0' &&
2479141858Sgshapiro					       isspace(*q))
2480141858Sgshapiro						;
2481141858Sgshapiro					if (*q == '\0')
2482141858Sgshapiro						ok = true;
248338032Speter					break;
248438032Speter				}
2485120256Sgshapiro				if (strchr("[].-_#:", *q) == NULL)
248638032Speter					break;
248738032Speter			}
248864562Sgshapiro
248998121Sgshapiro			if (*q == '\0' && ok)
249038032Speter			{
249138032Speter				q = "pleased to meet you";
249290792Sgshapiro				sendinghost = sm_strdup_x(p);
249338032Speter			}
249438032Speter			else if (!AllowBogusHELO)
249538032Speter			{
249638032Speter				usrerr("501 Invalid domain name");
249764562Sgshapiro				if (LogLevel > 9)
249864562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
2499110560Sgshapiro						  "invalid domain name (%s) from %.100s",
250064562Sgshapiro						  p, CurSmtpClient);
250138032Speter				break;
250238032Speter			}
250338032Speter			else
250438032Speter			{
250538032Speter				q = "accepting invalid domain name";
250638032Speter			}
250738032Speter
2508157001Sgshapiro			if (gothello || smtp.sm_gotmail)
250990792Sgshapiro				CLEAR_STATE(cmdbuf);
251090792Sgshapiro
251190792Sgshapiro#if MILTER
251290792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
251390792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
251464562Sgshapiro			{
251564562Sgshapiro				char state;
251664562Sgshapiro				char *response;
251764562Sgshapiro
251864562Sgshapiro				response = milter_helo(p, e, &state);
251964562Sgshapiro				switch (state)
252064562Sgshapiro				{
252164562Sgshapiro				  case SMFIR_REJECT:
252290792Sgshapiro					if (MilterLogLevel > 3)
252390792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
252490792Sgshapiro							  "Milter: helo=%s, reject=Command rejected",
252590792Sgshapiro							  p);
252664562Sgshapiro					nullserver = "Command rejected";
252790792Sgshapiro					smtp.sm_milterize = false;
252864562Sgshapiro					break;
252964562Sgshapiro
253064562Sgshapiro				  case SMFIR_TEMPFAIL:
253190792Sgshapiro					if (MilterLogLevel > 3)
253290792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
253390792Sgshapiro							  "Milter: helo=%s, reject=%s",
253490792Sgshapiro							  p, MSG_TEMPFAIL);
253590792Sgshapiro					tempfail = true;
253690792Sgshapiro					smtp.sm_milterize = false;
253764562Sgshapiro					break;
2538157001Sgshapiro
2539168515Sgshapiro				  case SMFIR_REPLYCODE:
2540157001Sgshapiro					if (MilterLogLevel > 3)
2541157001Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
2542168515Sgshapiro							  "Milter: helo=%s, reject=%s",
2543168515Sgshapiro							  p, response);
2544168515Sgshapiro					if (strncmp(response, "421 ", 4) != 0
2545168515Sgshapiro					    && strncmp(response, "421-", 4) != 0)
2546168515Sgshapiro					{
2547168515Sgshapiro						nullserver = newstr(response);
2548168515Sgshapiro						smtp.sm_milterize = false;
2549168515Sgshapiro						break;
2550168515Sgshapiro					}
2551168515Sgshapiro					/* FALLTHROUGH */
2552168515Sgshapiro
2553168515Sgshapiro				  case SMFIR_SHUTDOWN:
2554168515Sgshapiro					if (MilterLogLevel > 3 &&
2555168515Sgshapiro					    response == NULL)
2556168515Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
2557159609Sgshapiro							  "Milter: helo=%s, reject=421 4.7.0 %s closing connection",
2558157001Sgshapiro							  p, MyHostName);
2559157001Sgshapiro					tempfail = true;
2560157001Sgshapiro					smtp.sm_milterize = false;
2561168515Sgshapiro					if (response != NULL)
2562363466Sgshapiro					{
2563363466Sgshapiro						/* Can't use ("%s", ...) due to usrerr() requirements */
2564168515Sgshapiro						usrerr(response);
2565363466Sgshapiro					}
2566168515Sgshapiro					else
2567363466Sgshapiro					{
2568168515Sgshapiro						message("421 4.7.0 %s closing connection",
2569168515Sgshapiro							MyHostName);
2570363466Sgshapiro					}
2571157001Sgshapiro					/* arrange to ignore send list */
2572157001Sgshapiro					e->e_sendqueue = NULL;
2573168515Sgshapiro					lognullconnection = false;
2574157001Sgshapiro					goto doquit;
257564562Sgshapiro				}
257690792Sgshapiro				if (response != NULL)
257790792Sgshapiro					sm_free(response);
257890792Sgshapiro
257990792Sgshapiro				/*
258090792Sgshapiro				**  If quarantining by a connect/ehlo action,
258190792Sgshapiro				**  save between messages
258290792Sgshapiro				*/
258390792Sgshapiro
258490792Sgshapiro				if (smtp.sm_quarmsg == NULL &&
258590792Sgshapiro				    e->e_quarmsg != NULL)
258690792Sgshapiro					smtp.sm_quarmsg = newstr(e->e_quarmsg);
258764562Sgshapiro			}
258890792Sgshapiro#endif /* MILTER */
258990792Sgshapiro			gothello = true;
259064562Sgshapiro
259138032Speter			/* print HELO response message */
259264562Sgshapiro			if (c->cmd_code != CMDEHLO)
259338032Speter			{
259438032Speter				message("250 %s Hello %s, %s",
259538032Speter					MyHostName, CurSmtpClient, q);
259638032Speter				break;
259738032Speter			}
259838032Speter
259938032Speter			message("250-%s Hello %s, %s",
260038032Speter				MyHostName, CurSmtpClient, q);
260138032Speter
260264562Sgshapiro			/* offer ENHSC even for nullserver */
260364562Sgshapiro			if (nullserver != NULL)
260464562Sgshapiro			{
260564562Sgshapiro				message("250 ENHANCEDSTATUSCODES");
260664562Sgshapiro				break;
260764562Sgshapiro			}
260864562Sgshapiro
260966494Sgshapiro			/*
261066494Sgshapiro			**  print EHLO features list
261166494Sgshapiro			**
261266494Sgshapiro			**  Note: If you change this list,
261390792Sgshapiro			**	  remember to update 'helpfile'
261466494Sgshapiro			*/
261566494Sgshapiro
261664562Sgshapiro			message("250-ENHANCEDSTATUSCODES");
261790792Sgshapiro#if PIPELINING
261890792Sgshapiro			if (bitset(SRV_OFFER_PIPE, features))
261990792Sgshapiro				message("250-PIPELINING");
2620363466Sgshapiro#endif
262190792Sgshapiro			if (bitset(SRV_OFFER_EXPN, features))
262238032Speter			{
262338032Speter				message("250-EXPN");
262490792Sgshapiro				if (bitset(SRV_OFFER_VERB, features))
262538032Speter					message("250-VERB");
262638032Speter			}
262790792Sgshapiro#if MIME8TO7
262838032Speter			message("250-8BITMIME");
2629363466Sgshapiro#endif
263038032Speter			if (MaxMessageSize > 0)
263138032Speter				message("250-SIZE %ld", MaxMessageSize);
263238032Speter			else
263338032Speter				message("250-SIZE");
263490792Sgshapiro#if DSN
263590792Sgshapiro			if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
263638032Speter				message("250-DSN");
2637363466Sgshapiro#endif
2638363466Sgshapiro#if _FFR_EAI
2639363466Sgshapiro			if (bitset(SRV_OFFER_EAI, features))
2640363466Sgshapiro				message("250-SMTPUTF8");
2641363466Sgshapiro#endif /* _FFR_EAI */
264290792Sgshapiro			if (bitset(SRV_OFFER_ETRN, features))
264338032Speter				message("250-ETRN");
264490792Sgshapiro#if SASL
264564562Sgshapiro			if (sasl_ok && mechlist != NULL && *mechlist != '\0')
264664562Sgshapiro				message("250-AUTH %s", mechlist);
2647363466Sgshapiro#endif
264890792Sgshapiro#if STARTTLS
2649223067Sgshapiro			if (tls_ok_srv && bitset(SRV_OFFER_TLS, features))
265064562Sgshapiro				message("250-STARTTLS");
2651363466Sgshapiro#endif
265290792Sgshapiro			if (DeliverByMin > 0)
265390792Sgshapiro				message("250-DELIVERBY %ld",
265490792Sgshapiro					(long) DeliverByMin);
265590792Sgshapiro			else if (DeliverByMin == 0)
265690792Sgshapiro				message("250-DELIVERBY");
265790792Sgshapiro
265890792Sgshapiro			/* < 0: no deliver-by */
265990792Sgshapiro
266038032Speter			message("250 HELP");
266138032Speter			break;
266238032Speter
266338032Speter		  case CMDMAIL:		/* mail -- designate sender */
266438032Speter			SmtpPhase = "server MAIL";
266590792Sgshapiro			DELAY_CONN("MAIL");
266638032Speter
266738032Speter			/* check for validity of this command */
266838032Speter			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
266938032Speter			{
267064562Sgshapiro				usrerr("503 5.0.0 Polite people say HELO first");
267138032Speter				break;
267238032Speter			}
267390792Sgshapiro			if (smtp.sm_gotmail)
267438032Speter			{
267564562Sgshapiro				usrerr("503 5.5.0 Sender already specified");
267638032Speter				break;
267738032Speter			}
267890792Sgshapiro#if SASL
267990792Sgshapiro			if (bitset(SRV_REQ_AUTH, features) &&
268064562Sgshapiro			    authenticating != SASL_IS_AUTH)
268164562Sgshapiro			{
268264562Sgshapiro				usrerr("530 5.7.0 Authentication required");
268364562Sgshapiro				break;
268464562Sgshapiro			}
268590792Sgshapiro#endif /* SASL */
268638032Speter
268764562Sgshapiro			p = skipword(p, "from");
268864562Sgshapiro			if (p == NULL)
268964562Sgshapiro				break;
269064562Sgshapiro			if (tempfail)
269164562Sgshapiro			{
269264562Sgshapiro				if (LogLevel > 9)
269364562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2694110560Sgshapiro						  "SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)",
269564562Sgshapiro						  p, CurSmtpClient);
2696363466Sgshapiro				/* Can't use ("%s", ...) due to usrerr() requirements */
269790792Sgshapiro				usrerr(MSG_TEMPFAIL);
269864562Sgshapiro				break;
269964562Sgshapiro			}
270064562Sgshapiro
270138032Speter			/* make sure we know who the sending host is */
270238032Speter			if (sendinghost == NULL)
270338032Speter				sendinghost = peerhostname;
270438032Speter
270538032Speter
270690792Sgshapiro#if SM_HEAP_CHECK
270790792Sgshapiro			if (sm_debug_active(&DebugLeakSmtp, 1))
270890792Sgshapiro			{
270990792Sgshapiro				sm_heap_newgroup();
271090792Sgshapiro				sm_dprintf("smtp() heap group #%d\n",
271190792Sgshapiro					sm_heap_group());
271290792Sgshapiro			}
271390792Sgshapiro#endif /* SM_HEAP_CHECK */
271464562Sgshapiro
271538032Speter			if (Errors > 0)
271690792Sgshapiro				goto undo_no_pm;
271738032Speter			if (!gothello)
271838032Speter			{
271990792Sgshapiro				auth_warning(e, "%s didn't use HELO protocol",
272090792Sgshapiro					     CurSmtpClient);
272138032Speter			}
272290792Sgshapiro#ifdef PICKY_HELO_CHECK
272390792Sgshapiro			if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
272490792Sgshapiro			    (sm_strcasecmp(peerhostname, "localhost") != 0 ||
272590792Sgshapiro			     sm_strcasecmp(sendinghost, MyHostName) != 0))
272638032Speter			{
272738032Speter				auth_warning(e, "Host %s claimed to be %s",
272890792Sgshapiro					     CurSmtpClient, sendinghost);
272938032Speter			}
273090792Sgshapiro#endif /* PICKY_HELO_CHECK */
273138032Speter
273238032Speter			if (protocol == NULL)
273338032Speter				protocol = "SMTP";
273490792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'r', protocol);
273590792Sgshapiro			macdefine(&e->e_macro, A_PERM, 's', sendinghost);
273664562Sgshapiro
273738032Speter			if (Errors > 0)
273890792Sgshapiro				goto undo_no_pm;
273990792Sgshapiro			smtp.sm_nrcpts = 0;
274090792Sgshapiro			n_badrcpts = 0;
274190792Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
274290792Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
2743132943Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"),
2744132943Sgshapiro				"0");
274564562Sgshapiro			e->e_flags |= EF_CLRQUEUE;
274690792Sgshapiro			sm_setproctitle(true, e, "%s %s: %.80s",
274764562Sgshapiro					qid_printname(e),
274864562Sgshapiro					CurSmtpClient, inp);
274938032Speter
275090792Sgshapiro			/* do the processing */
275190792Sgshapiro		    SM_TRY
275290792Sgshapiro		    {
275394334Sgshapiro			extern char *FullName;
275494334Sgshapiro
275590792Sgshapiro			QuickAbort = true;
2756363466Sgshapiro			SM_FREE(FullName);
275764562Sgshapiro
275838032Speter			/* must parse sender first */
275938032Speter			delimptr = NULL;
276090792Sgshapiro			setsender(p, e, &delimptr, ' ', false);
276138032Speter			if (delimptr != NULL && *delimptr != '\0')
276238032Speter				*delimptr++ = '\0';
276338032Speter			if (Errors > 0)
276490792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
276538032Speter
276664562Sgshapiro			/* Successfully set e_from, allow logging */
276764562Sgshapiro			e->e_flags |= EF_LOGSENDER;
276838032Speter
276964562Sgshapiro			/* put resulting triple from parseaddr() into macros */
277064562Sgshapiro			if (e->e_from.q_mailer != NULL)
277190792Sgshapiro				 macdefine(&e->e_macro, A_PERM,
277290792Sgshapiro					macid("{mail_mailer}"),
277390792Sgshapiro					e->e_from.q_mailer->m_name);
277464562Sgshapiro			else
277590792Sgshapiro				 macdefine(&e->e_macro, A_PERM,
277690792Sgshapiro					macid("{mail_mailer}"), NULL);
277764562Sgshapiro			if (e->e_from.q_host != NULL)
277890792Sgshapiro				macdefine(&e->e_macro, A_PERM,
277990792Sgshapiro					macid("{mail_host}"),
278090792Sgshapiro					e->e_from.q_host);
278164562Sgshapiro			else
278290792Sgshapiro				macdefine(&e->e_macro, A_PERM,
278390792Sgshapiro					macid("{mail_host}"), "localhost");
278464562Sgshapiro			if (e->e_from.q_user != NULL)
278590792Sgshapiro				macdefine(&e->e_macro, A_PERM,
278690792Sgshapiro					macid("{mail_addr}"),
278790792Sgshapiro					e->e_from.q_user);
278864562Sgshapiro			else
278990792Sgshapiro				macdefine(&e->e_macro, A_PERM,
279090792Sgshapiro					macid("{mail_addr}"), NULL);
279164562Sgshapiro			if (Errors > 0)
279290792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
279364562Sgshapiro
279438032Speter			/* check for possible spoofing */
279538032Speter			if (RealUid != 0 && OpMode == MD_SMTP &&
279638032Speter			    !wordinclass(RealUserName, 't') &&
279764562Sgshapiro			    (!bitnset(M_LOCALMAILER,
279864562Sgshapiro				      e->e_from.q_mailer->m_flags) ||
279964562Sgshapiro			     strcmp(e->e_from.q_user, RealUserName) != 0))
280038032Speter			{
280138032Speter				auth_warning(e, "%s owned process doing -bs",
280238032Speter					RealUserName);
280338032Speter			}
280438032Speter
2805120256Sgshapiro			/* reset to default value */
2806168515Sgshapiro			SevenBitInput = SevenBitInput_Saved;
2807120256Sgshapiro
280838032Speter			/* now parse ESMTP arguments */
280938032Speter			e->e_msgsize = 0;
281064562Sgshapiro			addr = p;
2811168515Sgshapiro			parse_esmtp_args(e, NULL, p, delimptr, "MAIL", args,
2812168515Sgshapiro					mail_esmtp_args);
281338032Speter			if (Errors > 0)
281490792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
281538032Speter
2816363466Sgshapiro#if _FFR_EAI
2817363466Sgshapiro			if (e->e_smtputf8)
2818363466Sgshapiro			{
2819363466Sgshapiro				protocol = GET_PROTOCOL();
2820363466Sgshapiro				macdefine(&e->e_macro, A_PERM, 'r', protocol);
2821363466Sgshapiro			}
2822363466Sgshapiro
2823363466Sgshapiro			/* UTF8 addresses are only legal with SMTPUTF8 */
2824363466Sgshapiro			if (!e->e_smtputf8 && !addr_is_ascii(e->e_from.q_paddr))
2825363466Sgshapiro			{
2826363466Sgshapiro				usrerr("553 5.6.7 That address requires SMTPUTF8");
2827363466Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2828363466Sgshapiro			}
2829363466Sgshapiro#endif
2830363466Sgshapiro
283190792Sgshapiro#if SASL
283290792Sgshapiro# if _FFR_AUTH_PASSING
283390792Sgshapiro			/* set the default AUTH= if the sender didn't */
283490792Sgshapiro			if (e->e_auth_param == NULL)
283590792Sgshapiro			{
283690792Sgshapiro				/* XXX only do this for an MSA? */
283790792Sgshapiro				e->e_auth_param = macvalue(macid("{auth_authen}"),
283890792Sgshapiro							   e);
283990792Sgshapiro				if (e->e_auth_param == NULL)
284090792Sgshapiro					e->e_auth_param = "<>";
284190792Sgshapiro
284290792Sgshapiro				/*
284390792Sgshapiro				**  XXX should we invoke Strust_auth now?
284490792Sgshapiro				**  authorizing as the client that just
284590792Sgshapiro				**  authenticated, so we'll trust implicitly
284690792Sgshapiro				*/
284790792Sgshapiro			}
284890792Sgshapiro# endif /* _FFR_AUTH_PASSING */
284990792Sgshapiro#endif /* SASL */
285090792Sgshapiro
285164562Sgshapiro			/* do config file checking of the sender */
285290792Sgshapiro			macdefine(&e->e_macro, A_PERM,
285390792Sgshapiro				macid("{addr_type}"), "e s");
285490792Sgshapiro#if _FFR_MAIL_MACRO
285590792Sgshapiro			/* make the "real" sender address available */
285690792Sgshapiro			macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
285790792Sgshapiro				  e->e_from.q_paddr);
2858363466Sgshapiro#endif
285964562Sgshapiro			if (rscheck("check_mail", addr,
2860102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
2861285229Sgshapiro				    NULL, e->e_id, NULL, NULL) != EX_OK ||
286264562Sgshapiro			    Errors > 0)
286390792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
286490792Sgshapiro			macdefine(&e->e_macro, A_PERM,
286590792Sgshapiro				  macid("{addr_type}"), NULL);
286664562Sgshapiro
286766494Sgshapiro			if (MaxMessageSize > 0 &&
286877349Sgshapiro			    (e->e_msgsize > MaxMessageSize ||
286977349Sgshapiro			     e->e_msgsize < 0))
287038032Speter			{
287164562Sgshapiro				usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
287238032Speter					MaxMessageSize);
287390792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
287438032Speter			}
287564562Sgshapiro
287690792Sgshapiro			/*
287790792Sgshapiro			**  XXX always check whether there is at least one fs
287890792Sgshapiro			**  with enough space?
287990792Sgshapiro			**  However, this may not help much: the queue group
288090792Sgshapiro			**  selection may later on select a FS that hasn't
288190792Sgshapiro			**  enough space.
288290792Sgshapiro			*/
288390792Sgshapiro
288490792Sgshapiro			if ((NumFileSys == 1 || NumQueue == 1) &&
288590792Sgshapiro			    !enoughdiskspace(e->e_msgsize, e)
288690792Sgshapiro#if _FFR_ANY_FREE_FS
288790792Sgshapiro			    && !filesys_free(e->e_msgsize)
2888363466Sgshapiro#endif
288990792Sgshapiro			   )
289038032Speter			{
289190792Sgshapiro				/*
289290792Sgshapiro				**  We perform this test again when the
289390792Sgshapiro				**  queue directory is selected, in collect.
289490792Sgshapiro				*/
289590792Sgshapiro
289664562Sgshapiro				usrerr("452 4.4.5 Insufficient disk space; try again later");
289790792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
289838032Speter			}
289938032Speter			if (Errors > 0)
290090792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
290164562Sgshapiro
290290792Sgshapiro			LogUsrErrs = true;
290390792Sgshapiro#if MILTER
290490792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
290590792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
290664562Sgshapiro			{
290764562Sgshapiro				char state;
290864562Sgshapiro				char *response;
290964562Sgshapiro
291064562Sgshapiro				response = milter_envfrom(args, e, &state);
291190792Sgshapiro				MILTER_REPLY("from");
291264562Sgshapiro			}
291390792Sgshapiro#endif /* MILTER */
291464562Sgshapiro			if (Errors > 0)
291590792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
291664562Sgshapiro
291764562Sgshapiro			message("250 2.1.0 Sender ok");
291890792Sgshapiro			smtp.sm_gotmail = true;
291990792Sgshapiro		    }
292090792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
292190792Sgshapiro		    {
292290792Sgshapiro			/*
292390792Sgshapiro			**  An error occurred while processing a MAIL command.
292490792Sgshapiro			**  Jump to the common error handling code.
292590792Sgshapiro			*/
292690792Sgshapiro
292790792Sgshapiro			sm_exc_free(exc);
292890792Sgshapiro			goto undo_no_pm;
292990792Sgshapiro		    }
293090792Sgshapiro		    SM_END_TRY
293138032Speter			break;
293238032Speter
293390792Sgshapiro		  undo_no_pm:
293490792Sgshapiro			e->e_flags &= ~EF_PM_NOTIFY;
293590792Sgshapiro		  undo:
293690792Sgshapiro			break;
293790792Sgshapiro
293838032Speter		  case CMDRCPT:		/* rcpt -- designate recipient */
293990792Sgshapiro			DELAY_CONN("RCPT");
2940168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2941168515Sgshapiro				macid("{rcpt_mailer}"), NULL);
2942168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2943168515Sgshapiro				macid("{rcpt_host}"), NULL);
2944168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2945168515Sgshapiro				macid("{rcpt_addr}"), NULL);
2946168515Sgshapiro#if MILTER
2947168515Sgshapiro			(void) memset(&addr_st, '\0', sizeof(addr_st));
2948168515Sgshapiro			a = NULL;
2949168515Sgshapiro			milter_rcpt_added = false;
2950173340Sgshapiro			smtp.sm_e_nrcpts_orig = e->e_nrcpts;
2951168515Sgshapiro#endif
2952182352Sgshapiro#if _FFR_BADRCPT_SHUTDOWN
2953182352Sgshapiro			/*
2954182352Sgshapiro			**  hack to deal with hack, see below:
2955203004Sgshapiro			**  n_badrcpts is increased if limit is reached.
2956182352Sgshapiro			*/
2957182352Sgshapiro
2958182352Sgshapiro			n_badrcpts_adj = (BadRcptThrottle > 0 &&
2959182352Sgshapiro					  n_badrcpts > BadRcptThrottle &&
2960182352Sgshapiro					  LogLevel > 5)
2961182352Sgshapiro					  ? n_badrcpts - 1 : n_badrcpts;
2962182352Sgshapiro			if (BadRcptShutdown > 0 &&
2963182352Sgshapiro			    n_badrcpts_adj >= BadRcptShutdown &&
2964182352Sgshapiro			    (BadRcptShutdownGood == 0 ||
2965182352Sgshapiro			     smtp.sm_nrcpts == 0 ||
2966182352Sgshapiro			     (n_badrcpts_adj * 100 /
2967182352Sgshapiro			      (smtp.sm_nrcpts + n_badrcpts) >=
2968182352Sgshapiro			      BadRcptShutdownGood)))
2969182352Sgshapiro			{
2970182352Sgshapiro				if (LogLevel > 5)
2971182352Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2972182352Sgshapiro						  "%s: Possible SMTP RCPT flood, shutting down connection.",
2973182352Sgshapiro						  CurSmtpClient);
2974182352Sgshapiro				message("421 4.7.0 %s Too many bad recipients; closing connection",
2975182352Sgshapiro				MyHostName);
2976182352Sgshapiro
2977182352Sgshapiro				/* arrange to ignore any current send list */
2978182352Sgshapiro				e->e_sendqueue = NULL;
2979182352Sgshapiro				goto doquit;
2980182352Sgshapiro			}
2981182352Sgshapiro#endif /* _FFR_BADRCPT_SHUTDOWN */
2982125820Sgshapiro			if (BadRcptThrottle > 0 &&
2983125820Sgshapiro			    n_badrcpts >= BadRcptThrottle)
2984125820Sgshapiro			{
2985125820Sgshapiro				if (LogLevel > 5 &&
2986125820Sgshapiro				    n_badrcpts == BadRcptThrottle)
2987125820Sgshapiro				{
2988125820Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2989125820Sgshapiro						  "%s: Possible SMTP RCPT flood, throttling.",
2990125820Sgshapiro						  CurSmtpClient);
2991125820Sgshapiro
2992125820Sgshapiro					/* To avoid duplicated message */
2993125820Sgshapiro					n_badrcpts++;
2994125820Sgshapiro				}
2995132943Sgshapiro				NBADRCPTS;
2996125820Sgshapiro
2997125820Sgshapiro				/*
2998125820Sgshapiro				**  Don't use exponential backoff for now.
2999203004Sgshapiro				**  Some systems will open more connections
3000125820Sgshapiro				**  and actually overload the receiver even
3001125820Sgshapiro				**  more.
3002125820Sgshapiro				*/
3003125820Sgshapiro
3004203004Sgshapiro				(void) sleep(BadRcptThrottleDelay);
3005125820Sgshapiro			}
300690792Sgshapiro			if (!smtp.sm_gotmail)
300738032Speter			{
300864562Sgshapiro				usrerr("503 5.0.0 Need MAIL before RCPT");
300938032Speter				break;
301038032Speter			}
301138032Speter			SmtpPhase = "server RCPT";
301290792Sgshapiro		    SM_TRY
301390792Sgshapiro		    {
301490792Sgshapiro			QuickAbort = true;
301590792Sgshapiro			LogUsrErrs = true;
301638032Speter
301738032Speter			/* limit flooding of our machine */
301890792Sgshapiro			if (MaxRcptPerMsg > 0 &&
301990792Sgshapiro			    smtp.sm_nrcpts >= MaxRcptPerMsg)
302038032Speter			{
302190792Sgshapiro				/* sleep(1); / * slow down? */
302264562Sgshapiro				usrerr("452 4.5.3 Too many recipients");
302390792Sgshapiro				goto rcpt_done;
302438032Speter			}
302538032Speter
3026223067Sgshapiro			if (!SM_IS_INTERACTIVE(e->e_sendmode)
3027157001Sgshapiro#if _FFR_DM_ONE
3028157001Sgshapiro			    && (NotFirstDelivery || SM_DM_ONE != e->e_sendmode)
3029363466Sgshapiro#endif
3030157001Sgshapiro			   )
303138032Speter				e->e_flags |= EF_VRFYONLY;
303238032Speter
303390792Sgshapiro#if MILTER
303464562Sgshapiro			/*
3035168515Sgshapiro			**  Do not expand recipients at RCPT time (in the call
3036173340Sgshapiro			**  to recipient()) if a milter can delete or reject
3037173340Sgshapiro			**  a RCPT.  If they are expanded, it is impossible
3038173340Sgshapiro			**  for removefromlist() to figure out the expanded
3039173340Sgshapiro			**  members of the original recipient and mark them
3040173340Sgshapiro			**  as QS_DONTSEND.
304164562Sgshapiro			*/
304264562Sgshapiro
3043363466Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
3044363466Sgshapiro			      !bitset(EF_DISCARD, e->e_flags) &&
3045173340Sgshapiro			    (smtp.sm_milters.mis_flags &
3046173340Sgshapiro			     (MIS_FL_DEL_RCPT|MIS_FL_REJ_RCPT)) != 0)
3047173340Sgshapiro				e->e_flags |= EF_VRFYONLY;
3048168515Sgshapiro			milter_cmd_done = false;
3049168515Sgshapiro			milter_cmd_safe = false;
305090792Sgshapiro#endif /* MILTER */
305164562Sgshapiro
305238032Speter			p = skipword(p, "to");
305338032Speter			if (p == NULL)
305490792Sgshapiro				goto rcpt_done;
305590792Sgshapiro			macdefine(&e->e_macro, A_PERM,
305690792Sgshapiro				macid("{addr_type}"), "e r");
305790792Sgshapiro			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
305890792Sgshapiro				      e, true);
305990792Sgshapiro			macdefine(&e->e_macro, A_PERM,
306090792Sgshapiro				macid("{addr_type}"), NULL);
306142575Speter			if (Errors > 0)
306290792Sgshapiro				goto rcpt_done;
306342575Speter			if (a == NULL)
306442575Speter			{
306564562Sgshapiro				usrerr("501 5.0.0 Missing recipient");
306690792Sgshapiro				goto rcpt_done;
306742575Speter			}
3068363466Sgshapiro#if _FFR_EAI
3069363466Sgshapiro			if (!e->e_smtputf8 && !addr_is_ascii(a->q_paddr))
3070363466Sgshapiro			{
3071363466Sgshapiro				usrerr("553 5.6.7 Address requires SMTPUTF8");
3072363466Sgshapiro				goto rcpt_done;
3073363466Sgshapiro			}
3074363466Sgshapiro#endif
307542575Speter
307638032Speter			if (delimptr != NULL && *delimptr != '\0')
307738032Speter				*delimptr++ = '\0';
307838032Speter
307964562Sgshapiro			/* put resulting triple from parseaddr() into macros */
308064562Sgshapiro			if (a->q_mailer != NULL)
308190792Sgshapiro				macdefine(&e->e_macro, A_PERM,
308290792Sgshapiro					macid("{rcpt_mailer}"),
308390792Sgshapiro					a->q_mailer->m_name);
308464562Sgshapiro			else
308590792Sgshapiro				macdefine(&e->e_macro, A_PERM,
308690792Sgshapiro					macid("{rcpt_mailer}"), NULL);
308764562Sgshapiro			if (a->q_host != NULL)
308890792Sgshapiro				macdefine(&e->e_macro, A_PERM,
308990792Sgshapiro					macid("{rcpt_host}"), a->q_host);
309064562Sgshapiro			else
309190792Sgshapiro				macdefine(&e->e_macro, A_PERM,
309290792Sgshapiro					macid("{rcpt_host}"), "localhost");
309364562Sgshapiro			if (a->q_user != NULL)
309490792Sgshapiro				macdefine(&e->e_macro, A_PERM,
309590792Sgshapiro					macid("{rcpt_addr}"), a->q_user);
309664562Sgshapiro			else
309790792Sgshapiro				macdefine(&e->e_macro, A_PERM,
309890792Sgshapiro					macid("{rcpt_addr}"), NULL);
309964562Sgshapiro			if (Errors > 0)
310090792Sgshapiro				goto rcpt_done;
310138032Speter
310238032Speter			/* now parse ESMTP arguments */
310364562Sgshapiro			addr = p;
3104168515Sgshapiro			parse_esmtp_args(e, a, p, delimptr, "RCPT", args,
3105168515Sgshapiro					rcpt_esmtp_args);
310638032Speter			if (Errors > 0)
310790792Sgshapiro				goto rcpt_done;
310838032Speter
3109168515Sgshapiro#if MILTER
3110168515Sgshapiro			/*
3111168515Sgshapiro			**  rscheck() can trigger an "exception"
3112168515Sgshapiro			**  in which case the execution continues at
3113168515Sgshapiro			**  SM_EXCEPT(exc, "[!F]*")
3114168515Sgshapiro			**  This means milter_cmd_safe is not set
3115168515Sgshapiro			**  and hence milter is not invoked.
3116168515Sgshapiro			**  Would it be "safe" to change that, i.e., use
3117168515Sgshapiro			**  milter_cmd_safe = true;
3118168515Sgshapiro			**  here so a milter is informed (if requested)
3119168515Sgshapiro			**  about RCPTs that are rejected by check_rcpt?
3120168515Sgshapiro			*/
3121168515Sgshapiro# if _FFR_MILTER_CHECK_REJECTIONS_TOO
3122168515Sgshapiro			milter_cmd_safe = true;
3123168515Sgshapiro# endif
3124168515Sgshapiro#endif
3125168515Sgshapiro
312664562Sgshapiro			/* do config file checking of the recipient */
312790792Sgshapiro			macdefine(&e->e_macro, A_PERM,
312890792Sgshapiro				macid("{addr_type}"), "e r");
312964562Sgshapiro			if (rscheck("check_rcpt", addr,
3130102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
3131285229Sgshapiro				    NULL, e->e_id, p_addr_st, NULL) != EX_OK ||
313264562Sgshapiro			    Errors > 0)
313390792Sgshapiro				goto rcpt_done;
313490792Sgshapiro			macdefine(&e->e_macro, A_PERM,
313590792Sgshapiro				macid("{addr_type}"), NULL);
313664562Sgshapiro
3137102528Sgshapiro			/* If discarding, don't bother to verify user */
3138102528Sgshapiro			if (bitset(EF_DISCARD, e->e_flags))
3139102528Sgshapiro				a->q_state = QS_VERIFIED;
3140168515Sgshapiro#if MILTER
3141168515Sgshapiro			milter_cmd_safe = true;
3142168515Sgshapiro#endif
3143102528Sgshapiro
3144285229Sgshapiro			addbcc(a, e);
3145285229Sgshapiro			rcptmods(a, e);
3146285229Sgshapiro
3147168515Sgshapiro			/* save in recipient list after ESMTP mods */
3148168515Sgshapiro			a = recipient(a, &e->e_sendqueue, 0, e);
3149168515Sgshapiro			/* may trigger exception... */
3150168515Sgshapiro
315190792Sgshapiro#if MILTER
3152168515Sgshapiro			milter_rcpt_added = true;
3153168515Sgshapiro#endif
3154168515Sgshapiro
3155168515Sgshapiro			if(!(Errors > 0) && QS_IS_BADADDR(a->q_state))
3156168515Sgshapiro			{
3157168515Sgshapiro				/* punt -- should keep message in ADDRESS.... */
3158168515Sgshapiro				usrerr("550 5.1.1 Addressee unknown");
3159168515Sgshapiro			}
3160168515Sgshapiro
3161168515Sgshapiro#if MILTER
3162168515Sgshapiro		rcpt_done:
316390792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
316490792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
316564562Sgshapiro			{
316664562Sgshapiro				char state;
316764562Sgshapiro				char *response;
316864562Sgshapiro
3169168515Sgshapiro				/* how to get the error codes? */
3170168515Sgshapiro				if (Errors > 0)
3171168515Sgshapiro				{
3172168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
3173168515Sgshapiro						macid("{rcpt_mailer}"),
3174168515Sgshapiro						"error");
3175168515Sgshapiro					if (a != NULL &&
3176168515Sgshapiro					    a->q_status != NULL &&
3177168515Sgshapiro					    a->q_rstatus != NULL)
3178168515Sgshapiro					{
3179168515Sgshapiro						macdefine(&e->e_macro, A_PERM,
3180168515Sgshapiro							macid("{rcpt_host}"),
3181168515Sgshapiro							a->q_status);
3182168515Sgshapiro						macdefine(&e->e_macro, A_PERM,
3183168515Sgshapiro							macid("{rcpt_addr}"),
3184168515Sgshapiro							a->q_rstatus);
3185168515Sgshapiro					}
3186168515Sgshapiro					else
3187168515Sgshapiro					{
3188168515Sgshapiro						if (addr_st.q_host != NULL)
3189168515Sgshapiro							macdefine(&e->e_macro,
3190168515Sgshapiro								A_PERM,
3191168515Sgshapiro								macid("{rcpt_host}"),
3192168515Sgshapiro								addr_st.q_host);
3193168515Sgshapiro						if (addr_st.q_user != NULL)
3194168515Sgshapiro							macdefine(&e->e_macro,
3195168515Sgshapiro								A_PERM,
3196168515Sgshapiro								macid("{rcpt_addr}"),
3197168515Sgshapiro								addr_st.q_user);
3198168515Sgshapiro					}
3199168515Sgshapiro				}
3200168515Sgshapiro
3201168515Sgshapiro				response = milter_envrcpt(args, e, &state,
3202168515Sgshapiro							Errors > 0);
3203168515Sgshapiro				milter_cmd_done = true;
320490792Sgshapiro				MILTER_REPLY("to");
320564562Sgshapiro			}
320690792Sgshapiro#endif /* MILTER */
320764562Sgshapiro
320838032Speter			/* no errors during parsing, but might be a duplicate */
320938032Speter			e->e_to = a->q_paddr;
3210168515Sgshapiro			if (!(Errors > 0) && !QS_IS_BADADDR(a->q_state))
321138032Speter			{
321290792Sgshapiro				if (smtp.sm_nrcpts == 0)
321364562Sgshapiro					initsys(e);
321464562Sgshapiro				message("250 2.1.5 Recipient ok%s",
321564562Sgshapiro					QS_IS_QUEUEUP(a->q_state) ?
321638032Speter						" (will queue)" : "");
321790792Sgshapiro				smtp.sm_nrcpts++;
321838032Speter			}
3219168515Sgshapiro
3220168515Sgshapiro			/* Is this needed? */
3221168515Sgshapiro#if !MILTER
3222168515Sgshapiro		rcpt_done:
3223363466Sgshapiro#endif
3224363466Sgshapiro
3225168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
3226168515Sgshapiro				macid("{rcpt_mailer}"), NULL);
3227168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
3228168515Sgshapiro				macid("{rcpt_host}"), NULL);
3229168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
3230168515Sgshapiro				macid("{rcpt_addr}"), NULL);
3231168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
3232168515Sgshapiro				macid("{dsn_notify}"), NULL);
3233168515Sgshapiro
323490792Sgshapiro			if (Errors > 0)
3235132943Sgshapiro			{
323690792Sgshapiro				++n_badrcpts;
3237132943Sgshapiro				NBADRCPTS;
3238132943Sgshapiro			}
323990792Sgshapiro		    }
324090792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
324190792Sgshapiro		    {
324290792Sgshapiro			/* An exception occurred while processing RCPT */
324390792Sgshapiro			e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
324490792Sgshapiro			++n_badrcpts;
3245132943Sgshapiro			NBADRCPTS;
3246168515Sgshapiro#if MILTER
3247168515Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
3248168515Sgshapiro			    !bitset(EF_DISCARD, e->e_flags) &&
3249168515Sgshapiro			    !milter_cmd_done && milter_cmd_safe)
3250168515Sgshapiro			{
3251168515Sgshapiro				char state;
3252168515Sgshapiro				char *response;
3253168515Sgshapiro
3254168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
3255168515Sgshapiro					macid("{rcpt_mailer}"), "error");
3256168515Sgshapiro
3257168515Sgshapiro				/* how to get the error codes? */
3258168515Sgshapiro				if (addr_st.q_host != NULL)
3259168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
3260168515Sgshapiro						macid("{rcpt_host}"),
3261168515Sgshapiro						addr_st.q_host);
3262168515Sgshapiro				else if (a != NULL && a->q_status != NULL)
3263168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
3264168515Sgshapiro						macid("{rcpt_host}"),
3265168515Sgshapiro						a->q_status);
3266168515Sgshapiro
3267168515Sgshapiro				if (addr_st.q_user != NULL)
3268168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
3269168515Sgshapiro						macid("{rcpt_addr}"),
3270168515Sgshapiro						addr_st.q_user);
3271168515Sgshapiro				else if (a != NULL && a->q_rstatus != NULL)
3272168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
3273168515Sgshapiro						macid("{rcpt_addr}"),
3274168515Sgshapiro						a->q_rstatus);
3275168515Sgshapiro
3276168515Sgshapiro				response = milter_envrcpt(args, e, &state,
3277168515Sgshapiro							true);
3278168515Sgshapiro				milter_cmd_done = true;
3279168515Sgshapiro				MILTER_REPLY("to");
3280168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
3281168515Sgshapiro					macid("{rcpt_mailer}"), NULL);
3282168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
3283168515Sgshapiro					macid("{rcpt_host}"), NULL);
3284168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
3285168515Sgshapiro					macid("{rcpt_addr}"), NULL);
3286168515Sgshapiro			}
3287168515Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
3288168515Sgshapiro			    milter_rcpt_added && milter_cmd_done &&
3289168515Sgshapiro			    milter_cmd_fail)
3290168515Sgshapiro			{
3291168515Sgshapiro				(void) removefromlist(addr, &e->e_sendqueue, e);
3292168515Sgshapiro				milter_cmd_fail = false;
3293173340Sgshapiro				if (smtp.sm_e_nrcpts_orig < e->e_nrcpts)
3294173340Sgshapiro					e->e_nrcpts = smtp.sm_e_nrcpts_orig;
3295168515Sgshapiro			}
3296168515Sgshapiro#endif /* MILTER */
329790792Sgshapiro		    }
329890792Sgshapiro		    SM_END_TRY
329938032Speter			break;
330038032Speter
330138032Speter		  case CMDDATA:		/* data -- text of mail */
330290792Sgshapiro			DELAY_CONN("DATA");
3303132943Sgshapiro			if (!smtp_data(&smtp, e))
3304132943Sgshapiro				goto doquit;
330538032Speter			break;
330638032Speter
330738032Speter		  case CMDRSET:		/* rset -- reset state */
330838032Speter			if (tTd(94, 100))
330964562Sgshapiro				message("451 4.0.0 Test failure");
331038032Speter			else
331164562Sgshapiro				message("250 2.0.0 Reset state");
331290792Sgshapiro			CLEAR_STATE(cmdbuf);
331338032Speter			break;
331438032Speter
331538032Speter		  case CMDVRFY:		/* vrfy -- verify address */
331638032Speter		  case CMDEXPN:		/* expn -- expand address */
331790792Sgshapiro			vrfy = c->cmd_code == CMDVRFY;
331890792Sgshapiro			DELAY_CONN(vrfy ? "VRFY" : "EXPN");
331964562Sgshapiro			if (tempfail)
332064562Sgshapiro			{
332164562Sgshapiro				if (LogLevel > 9)
332264562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
3323110560Sgshapiro						  "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)",
332490792Sgshapiro						  vrfy ? "VRFY" : "EXPN",
332564562Sgshapiro						  p, CurSmtpClient);
332690792Sgshapiro
332790792Sgshapiro				/* RFC 821 doesn't allow 4xy reply code */
332864562Sgshapiro				usrerr("550 5.7.1 Please try again later");
332964562Sgshapiro				break;
333064562Sgshapiro			}
333190792Sgshapiro			wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
333290792Sgshapiro					     false, vrfy ? "VRFY" : "EXPN", e);
3333132943Sgshapiro			STOP_IF_ATTACK(wt);
333464562Sgshapiro			previous = curtime();
3335110560Sgshapiro			if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) ||
3336110560Sgshapiro			    (!vrfy && !bitset(SRV_OFFER_EXPN, features)))
333738032Speter			{
333838032Speter				if (vrfy)
333964562Sgshapiro					message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
334038032Speter				else
334164562Sgshapiro					message("502 5.7.0 Sorry, we do not allow this operation");
334238032Speter				if (LogLevel > 5)
334338032Speter					sm_syslog(LOG_INFO, e->e_id,
3344110560Sgshapiro						  "%s: %s [rejected]",
334564562Sgshapiro						  CurSmtpClient,
334664562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
334738032Speter				break;
334838032Speter			}
334938032Speter			else if (!gothello &&
335038032Speter				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
335138032Speter						PrivacyFlags))
335238032Speter			{
335364562Sgshapiro				usrerr("503 5.0.0 I demand that you introduce yourself first");
335438032Speter				break;
335538032Speter			}
335690792Sgshapiro			if (Errors > 0)
335738032Speter				break;
335838032Speter			if (LogLevel > 5)
3359110560Sgshapiro				sm_syslog(LOG_INFO, e->e_id, "%s: %s",
336064562Sgshapiro					  CurSmtpClient,
336164562Sgshapiro					  shortenstring(inp, MAXSHORTSTR));
336290792Sgshapiro		    SM_TRY
336390792Sgshapiro		    {
336490792Sgshapiro			QuickAbort = true;
336538032Speter			vrfyqueue = NULL;
336638032Speter			if (vrfy)
336738032Speter				e->e_flags |= EF_VRFYONLY;
3368363466Sgshapiro			while (*p != '\0' && SM_ISSPACE(*p))
336938032Speter				p++;
337038032Speter			if (*p == '\0')
337138032Speter			{
337264562Sgshapiro				usrerr("501 5.5.2 Argument required");
337338032Speter			}
337438032Speter			else
337538032Speter			{
337664562Sgshapiro				/* do config file checking of the address */
337764562Sgshapiro				if (rscheck(vrfy ? "check_vrfy" : "check_expn",
3378285229Sgshapiro					    p, NULL, e, RSF_RMCOMM, 3, NULL,
3379285229Sgshapiro					    NOQID, NULL, NULL) != EX_OK ||
338090792Sgshapiro				    Errors > 0)
338190792Sgshapiro					sm_exc_raisenew_x(&EtypeQuickAbort, 1);
338238032Speter				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
338338032Speter			}
338464562Sgshapiro			if (wt > 0)
338571345Sgshapiro			{
338671345Sgshapiro				time_t t;
338771345Sgshapiro
338871345Sgshapiro				t = wt - (curtime() - previous);
338971345Sgshapiro				if (t > 0)
339071345Sgshapiro					(void) sleep(t);
339171345Sgshapiro			}
339238032Speter			if (Errors > 0)
339390792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
339438032Speter			if (vrfyqueue == NULL)
339538032Speter			{
339664562Sgshapiro				usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
339738032Speter			}
339838032Speter			while (vrfyqueue != NULL)
339938032Speter			{
340064562Sgshapiro				if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
340164562Sgshapiro				{
340264562Sgshapiro					vrfyqueue = vrfyqueue->q_next;
340364562Sgshapiro					continue;
340464562Sgshapiro				}
340538032Speter
340664562Sgshapiro				/* see if there is more in the vrfy list */
340738032Speter				a = vrfyqueue;
340838032Speter				while ((a = a->q_next) != NULL &&
340966494Sgshapiro				       (!QS_IS_UNDELIVERED(a->q_state)))
341038032Speter					continue;
341164562Sgshapiro				printvrfyaddr(vrfyqueue, a == NULL, vrfy);
341264562Sgshapiro				vrfyqueue = a;
341338032Speter			}
341490792Sgshapiro		    }
341590792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
341690792Sgshapiro		    {
341790792Sgshapiro			/*
341890792Sgshapiro			**  An exception occurred while processing VRFY/EXPN
341990792Sgshapiro			*/
342090792Sgshapiro
342190792Sgshapiro			sm_exc_free(exc);
342290792Sgshapiro			goto undo;
342390792Sgshapiro		    }
342490792Sgshapiro		    SM_END_TRY
342538032Speter			break;
342638032Speter
342738032Speter		  case CMDETRN:		/* etrn -- force queue flush */
342890792Sgshapiro			DELAY_CONN("ETRN");
342990792Sgshapiro
343090792Sgshapiro			/* Don't leak queue information via debug flags */
343190792Sgshapiro			if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
343290792Sgshapiro			    (RealUid != 0 && RealUid != TrustedUid &&
343390792Sgshapiro			     OpMode == MD_SMTP))
343438032Speter			{
343564562Sgshapiro				/* different message for MSA ? */
343664562Sgshapiro				message("502 5.7.0 Sorry, we do not allow this operation");
343738032Speter				if (LogLevel > 5)
343838032Speter					sm_syslog(LOG_INFO, e->e_id,
3439110560Sgshapiro						  "%s: %s [rejected]",
344064562Sgshapiro						  CurSmtpClient,
344164562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
344238032Speter				break;
344338032Speter			}
344464562Sgshapiro			if (tempfail)
344564562Sgshapiro			{
344664562Sgshapiro				if (LogLevel > 9)
344764562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
3448110560Sgshapiro						  "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)",
344964562Sgshapiro						  p, CurSmtpClient);
3450363466Sgshapiro				/* Can't use ("%s", ...) due to usrerr() requirements */
345190792Sgshapiro				usrerr(MSG_TEMPFAIL);
345264562Sgshapiro				break;
345364562Sgshapiro			}
345438032Speter
345538032Speter			if (strlen(p) <= 0)
345638032Speter			{
345764562Sgshapiro				usrerr("500 5.5.2 Parameter required");
345838032Speter				break;
345938032Speter			}
346038032Speter
346138032Speter			/* crude way to avoid denial-of-service attacks */
3462132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS,
3463132943Sgshapiro							true, "ETRN", e));
346438032Speter
346590792Sgshapiro			/*
346690792Sgshapiro			**  Do config file checking of the parameter.
346790792Sgshapiro			**  Even though we have srv_features now, we still
346890792Sgshapiro			**  need this ruleset because the former is called
346990792Sgshapiro			**  when the connection has been established, while
347090792Sgshapiro			**  this ruleset is called when the command is
347190792Sgshapiro			**  actually issued and therefore has all information
347290792Sgshapiro			**  available to make a decision.
347390792Sgshapiro			*/
347490792Sgshapiro
3475285229Sgshapiro			if (rscheck("check_etrn", p, NULL, e, RSF_RMCOMM, 3,
3476285229Sgshapiro				    NULL, NOQID, NULL, NULL) != EX_OK ||
3477102528Sgshapiro			    Errors > 0)
347864562Sgshapiro				break;
347964562Sgshapiro
348038032Speter			if (LogLevel > 5)
348138032Speter				sm_syslog(LOG_INFO, e->e_id,
3482110560Sgshapiro					  "%s: ETRN %s", CurSmtpClient,
348364562Sgshapiro					  shortenstring(p, MAXSHORTSTR));
348438032Speter
348538032Speter			id = p;
348690792Sgshapiro			if (*id == '#')
348790792Sgshapiro			{
3488111823Sgshapiro				int i, qgrp;
348990792Sgshapiro
349090792Sgshapiro				id++;
3491111823Sgshapiro				qgrp = name2qid(id);
3492111823Sgshapiro				if (!ISVALIDQGRP(qgrp))
349390792Sgshapiro				{
349490792Sgshapiro					usrerr("459 4.5.4 Queue %s unknown",
349590792Sgshapiro					       id);
349690792Sgshapiro					break;
349790792Sgshapiro				}
3498111823Sgshapiro				for (i = 0; i < NumQueue && Queue[i] != NULL;
3499111823Sgshapiro				     i++)
3500111823Sgshapiro					Queue[i]->qg_nextrun = (time_t) -1;
3501111823Sgshapiro				Queue[qgrp]->qg_nextrun = 0;
3502111823Sgshapiro				ok = run_work_group(Queue[qgrp]->qg_wgrp,
3503111823Sgshapiro						    RWG_FORK|RWG_FORCE);
350490792Sgshapiro				if (ok && Errors == 0)
350590792Sgshapiro					message("250 2.0.0 Queuing for queue group %s started", id);
350690792Sgshapiro				break;
350790792Sgshapiro			}
350890792Sgshapiro
350938032Speter			if (*id == '@')
351038032Speter				id++;
351138032Speter			else
351238032Speter				*--id = '@';
351364562Sgshapiro
351490792Sgshapiro			new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
351590792Sgshapiro			if (new == NULL)
351690792Sgshapiro			{
351790792Sgshapiro				syserr("500 5.5.0 ETRN out of memory");
351890792Sgshapiro				break;
351990792Sgshapiro			}
352038032Speter			new->queue_match = id;
352190792Sgshapiro			new->queue_negate = false;
352238032Speter			new->queue_next = NULL;
352338032Speter			QueueLimitRecipient = new;
352490792Sgshapiro			ok = runqueue(true, false, false, true);
352590792Sgshapiro			sm_free(QueueLimitRecipient); /* XXX */
352638032Speter			QueueLimitRecipient = NULL;
352738032Speter			if (ok && Errors == 0)
352864562Sgshapiro				message("250 2.0.0 Queuing for node %s started", p);
352938032Speter			break;
353038032Speter
353138032Speter		  case CMDHELP:		/* help -- give user info */
353290792Sgshapiro			DELAY_CONN("HELP");
353364562Sgshapiro			help(p, e);
353438032Speter			break;
353538032Speter
353638032Speter		  case CMDNOOP:		/* noop -- do nothing */
353790792Sgshapiro			DELAY_CONN("NOOP");
3538157001Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
3539132943Sgshapiro							true, "NOOP", e));
354064562Sgshapiro			message("250 2.0.0 OK");
354138032Speter			break;
354238032Speter
354338032Speter		  case CMDQUIT:		/* quit -- leave mail */
354464562Sgshapiro			message("221 2.0.0 %s closing connection", MyHostName);
354590792Sgshapiro#if PIPELINING
354690792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
3547363466Sgshapiro#endif
354838032Speter
354990792Sgshapiro			if (smtp.sm_nrcpts > 0)
355090792Sgshapiro				logundelrcpts(e, "aborted by sender", 9, false);
355190792Sgshapiro
355238032Speter			/* arrange to ignore any current send list */
355338032Speter			e->e_sendqueue = NULL;
355438032Speter
355590792Sgshapiro#if STARTTLS
355664562Sgshapiro			/* shutdown TLS connection */
355764562Sgshapiro			if (tls_active)
355864562Sgshapiro			{
3559363466Sgshapiro				(void) endtls(&srv_ssl, "server");
356090792Sgshapiro				tls_active = false;
356164562Sgshapiro			}
356290792Sgshapiro#endif /* STARTTLS */
356390792Sgshapiro#if SASL
356464562Sgshapiro			if (authenticating == SASL_IS_AUTH)
356564562Sgshapiro			{
356664562Sgshapiro				sasl_dispose(&conn);
356764562Sgshapiro				authenticating = SASL_NOT_AUTH;
356890792Sgshapiro				/* XXX sasl_done(); this is a child */
356964562Sgshapiro			}
357090792Sgshapiro#endif /* SASL */
357164562Sgshapiro
357264562Sgshapirodoquit:
357338032Speter			/* avoid future 050 messages */
357438032Speter			disconnect(1, e);
357538032Speter
357690792Sgshapiro#if MILTER
357764562Sgshapiro			/* close out milter filters */
357864562Sgshapiro			milter_quit(e);
3579363466Sgshapiro#endif
358064562Sgshapiro
3581203004Sgshapiro			if (tTd(92, 2))
3582203004Sgshapiro				sm_dprintf("QUIT: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",
3583203004Sgshapiro					e->e_id,
3584203004Sgshapiro					bitset(EF_LOGSENDER, e->e_flags),
3585203004Sgshapiro					LogLevel);
358664562Sgshapiro			if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
358764562Sgshapiro				logsender(e, NULL);
358864562Sgshapiro			e->e_flags &= ~EF_LOGSENDER;
358964562Sgshapiro
359098121Sgshapiro			if (lognullconnection && LogLevel > 5 &&
359198121Sgshapiro			    nullserver == NULL)
359264562Sgshapiro			{
359364562Sgshapiro				char *d;
359464562Sgshapiro
359590792Sgshapiro				d = macvalue(macid("{daemon_name}"), e);
359664562Sgshapiro				if (d == NULL)
359764562Sgshapiro					d = "stdin";
359890792Sgshapiro
359990792Sgshapiro				/*
360090792Sgshapiro				**  even though this id is "bogus", it makes
360190792Sgshapiro				**  it simpler to "grep" related events, e.g.,
360290792Sgshapiro				**  timeouts for the same connection.
360390792Sgshapiro				*/
360490792Sgshapiro
360590792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3606110560Sgshapiro					  "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
360764562Sgshapiro					  CurSmtpClient, d);
360864562Sgshapiro			}
3609110560Sgshapiro			if (tTd(93, 100))
3610110560Sgshapiro			{
3611110560Sgshapiro				/* return to handle next connection */
3612110560Sgshapiro				return;
3613110560Sgshapiro			}
361490792Sgshapiro			finis(true, true, ExitStat);
361564562Sgshapiro			/* NOTREACHED */
361638032Speter
3617157001Sgshapiro			/* just to avoid bogus warning from some compilers */
3618157001Sgshapiro			exit(EX_OSERR);
3619157001Sgshapiro
362038032Speter		  case CMDVERB:		/* set verbose mode */
362190792Sgshapiro			DELAY_CONN("VERB");
3622110560Sgshapiro			if (!bitset(SRV_OFFER_EXPN, features) ||
3623110560Sgshapiro			    !bitset(SRV_OFFER_VERB, features))
362438032Speter			{
362538032Speter				/* this would give out the same info */
362664562Sgshapiro				message("502 5.7.0 Verbose unavailable");
362738032Speter				break;
362838032Speter			}
3629157001Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
3630132943Sgshapiro							true, "VERB", e));
363138032Speter			Verbose = 1;
363264562Sgshapiro			set_delivery_mode(SM_DELIVER, e);
363364562Sgshapiro			message("250 2.0.0 Verbose mode");
363438032Speter			break;
363538032Speter
363690792Sgshapiro#if SMTPDEBUG
363738032Speter		  case CMDDBGQSHOW:	/* show queues */
363890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
363990792Sgshapiro					     "Send Queue=");
3640132943Sgshapiro			printaddr(smioout, e->e_sendqueue, true);
364138032Speter			break;
364238032Speter
364338032Speter		  case CMDDBGDEBUG:	/* set debug mode */
3644168515Sgshapiro			tTsetup(tTdvect, sizeof(tTdvect), "0-99.1");
364538032Speter			tTflag(p);
364664562Sgshapiro			message("200 2.0.0 Debug set");
364738032Speter			break;
364838032Speter
364990792Sgshapiro#else /* SMTPDEBUG */
365038032Speter		  case CMDDBGQSHOW:	/* show queues */
365138032Speter		  case CMDDBGDEBUG:	/* set debug mode */
365290792Sgshapiro#endif /* SMTPDEBUG */
365338032Speter		  case CMDLOGBOGUS:	/* bogus command */
365490792Sgshapiro			DELAY_CONN("Bogus");
365538032Speter			if (LogLevel > 0)
365638032Speter				sm_syslog(LOG_CRIT, e->e_id,
3657110560Sgshapiro					  "\"%s\" command from %s (%.100s)",
365864562Sgshapiro					  c->cmd_name, CurSmtpClient,
365964562Sgshapiro					  anynet_ntoa(&RealHostAddr));
366064562Sgshapiro			/* FALLTHROUGH */
366138032Speter
366238032Speter		  case CMDERROR:	/* unknown command */
366390792Sgshapiro#if MAXBADCOMMANDS > 0
366490792Sgshapiro			if (++n_badcmds > MAXBADCOMMANDS)
366538032Speter			{
3666132943Sgshapiro  stopattack:
366764562Sgshapiro				message("421 4.7.0 %s Too many bad commands; closing connection",
366838032Speter					MyHostName);
366964562Sgshapiro
367064562Sgshapiro				/* arrange to ignore any current send list */
367164562Sgshapiro				e->e_sendqueue = NULL;
367238032Speter				goto doquit;
367338032Speter			}
367490792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
367538032Speter
3676132943Sgshapiro#if MILTER && SMFI_VERSION > 2
3677132943Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
3678132943Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
3679132943Sgshapiro			{
3680132943Sgshapiro				char state;
3681132943Sgshapiro				char *response;
3682132943Sgshapiro
3683132943Sgshapiro				if (MilterLogLevel > 9)
3684132943Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
3685132943Sgshapiro						"Sending \"%s\" to Milter", inp);
3686132943Sgshapiro				response = milter_unknown(inp, e, &state);
3687132943Sgshapiro				MILTER_REPLY("unknown");
3688132943Sgshapiro				if (state == SMFIR_REPLYCODE ||
3689132943Sgshapiro				    state == SMFIR_REJECT ||
3690157001Sgshapiro				    state == SMFIR_TEMPFAIL ||
3691157001Sgshapiro				    state == SMFIR_SHUTDOWN)
3692132943Sgshapiro				{
3693132943Sgshapiro					/* MILTER_REPLY already gave an error */
3694132943Sgshapiro					break;
3695132943Sgshapiro				}
3696132943Sgshapiro			}
3697132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 2 */
3698132943Sgshapiro
369964562Sgshapiro			usrerr("500 5.5.1 Command unrecognized: \"%s\"",
370064562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
370138032Speter			break;
370238032Speter
370364562Sgshapiro		  case CMDUNIMPL:
370490792Sgshapiro			DELAY_CONN("Unimpl");
370564562Sgshapiro			usrerr("502 5.5.1 Command not implemented: \"%s\"",
370664562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
370764562Sgshapiro			break;
370864562Sgshapiro
370938032Speter		  default:
371090792Sgshapiro			DELAY_CONN("default");
371138032Speter			errno = 0;
371264562Sgshapiro			syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
371338032Speter			break;
371438032Speter		}
371590792Sgshapiro#if SASL
371664562Sgshapiro		}
3717363466Sgshapiro#endif
371890792Sgshapiro	    }
371990792Sgshapiro	    SM_EXCEPT(exc, "[!F]*")
372090792Sgshapiro	    {
372190792Sgshapiro		/*
372290792Sgshapiro		**  The only possible exception is "E:mta.quickabort".
372390792Sgshapiro		**  There is nothing to do except fall through and loop.
372490792Sgshapiro		*/
372590792Sgshapiro	    }
372690792Sgshapiro	    SM_END_TRY
372738032Speter	}
372890792Sgshapiro}
372990792Sgshapiro/*
373090792Sgshapiro**  SMTP_DATA -- implement the SMTP DATA command.
373190792Sgshapiro**
373290792Sgshapiro**	Parameters:
373390792Sgshapiro**		smtp -- status of SMTP connection.
373490792Sgshapiro**		e -- envelope.
373590792Sgshapiro**
373690792Sgshapiro**	Returns:
3737132943Sgshapiro**		true iff SMTP session can continue.
373890792Sgshapiro**
373990792Sgshapiro**	Side Effects:
374090792Sgshapiro**		possibly sends message.
374190792Sgshapiro*/
374264562Sgshapiro
3743132943Sgshapirostatic bool
374490792Sgshapirosmtp_data(smtp, e)
374590792Sgshapiro	SMTP_T *smtp;
374690792Sgshapiro	ENVELOPE *e;
374790792Sgshapiro{
374890792Sgshapiro#if MILTER
374990792Sgshapiro	bool milteraccept;
3750363466Sgshapiro#endif
375190792Sgshapiro	bool aborting;
375290792Sgshapiro	bool doublequeue;
3753168515Sgshapiro	bool rv = true;
375490792Sgshapiro	ADDRESS *a;
375590792Sgshapiro	ENVELOPE *ee;
375690792Sgshapiro	char *id;
375798121Sgshapiro	char *oldid;
3758168515Sgshapiro	unsigned int features;
375990792Sgshapiro	char buf[32];
376090792Sgshapiro
376190792Sgshapiro	SmtpPhase = "server DATA";
376290792Sgshapiro	if (!smtp->sm_gotmail)
376390792Sgshapiro	{
376490792Sgshapiro		usrerr("503 5.0.0 Need MAIL command");
3765132943Sgshapiro		return true;
376690792Sgshapiro	}
376790792Sgshapiro	else if (smtp->sm_nrcpts <= 0)
376890792Sgshapiro	{
376990792Sgshapiro		usrerr("503 5.0.0 Need RCPT (recipient)");
3770132943Sgshapiro		return true;
377190792Sgshapiro	}
3772168515Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "%u", smtp->sm_nrcpts);
377390792Sgshapiro	if (rscheck("check_data", buf, NULL, e,
3774102528Sgshapiro		    RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
3775285229Sgshapiro		    e->e_id, NULL, NULL) != EX_OK)
3776132943Sgshapiro		return true;
377790792Sgshapiro
3778132943Sgshapiro#if MILTER && SMFI_VERSION > 3
3779132943Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize &&
3780132943Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
3781132943Sgshapiro	{
3782132943Sgshapiro		char state;
3783132943Sgshapiro		char *response;
3784132943Sgshapiro		int savelogusrerrs = LogUsrErrs;
3785132943Sgshapiro
3786132943Sgshapiro		response = milter_data_cmd(e, &state);
3787132943Sgshapiro		switch (state)
3788132943Sgshapiro		{
3789132943Sgshapiro		  case SMFIR_REPLYCODE:
3790132943Sgshapiro			if (MilterLogLevel > 3)
3791132943Sgshapiro			{
3792132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3793132943Sgshapiro					  "Milter: cmd=data, reject=%s",
3794132943Sgshapiro					  response);
3795132943Sgshapiro				LogUsrErrs = false;
3796132943Sgshapiro			}
3797203004Sgshapiro#if _FFR_MILTER_ENHSC
3798203004Sgshapiro			if (ISSMTPCODE(response))
3799203004Sgshapiro				(void) extenhsc(response + 4, ' ', e->e_enhsc);
3800363466Sgshapiro#endif
3801203004Sgshapiro
3802363466Sgshapiro			/* Can't use ("%s", ...) due to usrerr() requirements */
3803132943Sgshapiro			usrerr(response);
3804157001Sgshapiro			if (strncmp(response, "421 ", 4) == 0
3805157001Sgshapiro			    || strncmp(response, "421-", 4) == 0)
3806132943Sgshapiro			{
3807132943Sgshapiro				e->e_sendqueue = NULL;
3808132943Sgshapiro				return false;
3809132943Sgshapiro			}
3810132943Sgshapiro			return true;
3811132943Sgshapiro
3812132943Sgshapiro		  case SMFIR_REJECT:
3813132943Sgshapiro			if (MilterLogLevel > 3)
3814132943Sgshapiro			{
3815132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3816132943Sgshapiro					  "Milter: cmd=data, reject=550 5.7.1 Command rejected");
3817132943Sgshapiro				LogUsrErrs = false;
3818132943Sgshapiro			}
3819203004Sgshapiro#if _FFR_MILTER_ENHSC
3820203004Sgshapiro			(void) sm_strlcpy(e->e_enhsc, "5.7.1",
3821203004Sgshapiro					 sizeof(e->e_enhsc));
3822363466Sgshapiro#endif
3823132943Sgshapiro			usrerr("550 5.7.1 Command rejected");
3824132943Sgshapiro			return true;
3825132943Sgshapiro
3826132943Sgshapiro		  case SMFIR_DISCARD:
3827132943Sgshapiro			if (MilterLogLevel > 3)
3828132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3829132943Sgshapiro					  "Milter: cmd=data, discard");
3830132943Sgshapiro			e->e_flags |= EF_DISCARD;
3831132943Sgshapiro			break;
3832132943Sgshapiro
3833132943Sgshapiro		  case SMFIR_TEMPFAIL:
3834132943Sgshapiro			if (MilterLogLevel > 3)
3835132943Sgshapiro			{
3836132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3837132943Sgshapiro					  "Milter: cmd=data, reject=%s",
3838132943Sgshapiro					  MSG_TEMPFAIL);
3839132943Sgshapiro				LogUsrErrs = false;
3840132943Sgshapiro			}
3841203004Sgshapiro#if _FFR_MILTER_ENHSC
3842203004Sgshapiro			(void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
3843363466Sgshapiro#endif
3844363466Sgshapiro			/* Can't use ("%s", ...) due to usrerr() requirements */
3845132943Sgshapiro			usrerr(MSG_TEMPFAIL);
3846132943Sgshapiro			return true;
3847157001Sgshapiro
3848157001Sgshapiro		  case SMFIR_SHUTDOWN:
3849157001Sgshapiro			if (MilterLogLevel > 3)
3850157001Sgshapiro			{
3851157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3852157001Sgshapiro					  "Milter: cmd=data, reject=421 4.7.0 %s closing connection",
3853157001Sgshapiro					  MyHostName);
3854157001Sgshapiro				LogUsrErrs = false;
3855157001Sgshapiro			}
3856157001Sgshapiro			usrerr("421 4.7.0 %s closing connection", MyHostName);
3857157001Sgshapiro			e->e_sendqueue = NULL;
3858157001Sgshapiro			return false;
3859132943Sgshapiro		}
3860132943Sgshapiro		LogUsrErrs = savelogusrerrs;
3861132943Sgshapiro		if (response != NULL)
3862132943Sgshapiro			sm_free(response); /* XXX */
3863132943Sgshapiro	}
3864132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 3 */
3865132943Sgshapiro
386690792Sgshapiro	/* put back discard bit */
386790792Sgshapiro	if (smtp->sm_discard)
386890792Sgshapiro		e->e_flags |= EF_DISCARD;
386990792Sgshapiro
387090792Sgshapiro	/* check to see if we need to re-expand aliases */
387190792Sgshapiro	/* also reset QS_BADADDR on already-diagnosted addrs */
387290792Sgshapiro	doublequeue = false;
387390792Sgshapiro	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
387490792Sgshapiro	{
387590792Sgshapiro		if (QS_IS_VERIFIED(a->q_state) &&
387690792Sgshapiro		    !bitset(EF_DISCARD, e->e_flags))
387790792Sgshapiro		{
387890792Sgshapiro			/* need to re-expand aliases */
387990792Sgshapiro			doublequeue = true;
388090792Sgshapiro		}
388190792Sgshapiro		if (QS_IS_BADADDR(a->q_state))
388290792Sgshapiro		{
388390792Sgshapiro			/* make this "go away" */
388490792Sgshapiro			a->q_state = QS_DONTSEND;
388590792Sgshapiro		}
388690792Sgshapiro	}
388790792Sgshapiro
388890792Sgshapiro	/* collect the text of the message */
388990792Sgshapiro	SmtpPhase = "collect";
389090792Sgshapiro	buffer_errors();
389190792Sgshapiro
3892120256Sgshapiro	collect(InChannel, true, NULL, e, true);
389390792Sgshapiro
389490792Sgshapiro	/* redefine message size */
3895244833Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "%ld", PRT_NONNEGL(e->e_msgsize));
389690792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
389790792Sgshapiro
389890792Sgshapiro	/* rscheck() will set Errors or EF_DISCARD if it trips */
3899102528Sgshapiro	(void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT,
3900285229Sgshapiro		       3, NULL, e->e_id, NULL, NULL);
390190792Sgshapiro
390290792Sgshapiro#if MILTER
390390792Sgshapiro	milteraccept = true;
390490792Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize &&
390590792Sgshapiro	    Errors <= 0 &&
390690792Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
390790792Sgshapiro	{
390890792Sgshapiro		char state;
390990792Sgshapiro		char *response;
391090792Sgshapiro
391190792Sgshapiro		response = milter_data(e, &state);
391290792Sgshapiro		switch (state)
391390792Sgshapiro		{
391490792Sgshapiro		  case SMFIR_REPLYCODE:
391590792Sgshapiro			if (MilterLogLevel > 3)
391690792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
391790792Sgshapiro					  "Milter: data, reject=%s",
391890792Sgshapiro					  response);
391990792Sgshapiro			milteraccept = false;
3920203004Sgshapiro#if _FFR_MILTER_ENHSC
3921203004Sgshapiro			if (ISSMTPCODE(response))
3922203004Sgshapiro				(void) extenhsc(response + 4, ' ', e->e_enhsc);
3923363466Sgshapiro#endif
3924363466Sgshapiro			/* Can't use ("%s", ...) due to usrerr() requirements */
392590792Sgshapiro			usrerr(response);
3926203004Sgshapiro			if (strncmp(response, "421 ", 4) == 0
3927203004Sgshapiro			    || strncmp(response, "421-", 4) == 0)
3928203004Sgshapiro				rv = false;
392990792Sgshapiro			break;
393090792Sgshapiro
393190792Sgshapiro		  case SMFIR_REJECT:
393290792Sgshapiro			milteraccept = false;
393390792Sgshapiro			if (MilterLogLevel > 3)
393490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
393590792Sgshapiro					  "Milter: data, reject=554 5.7.1 Command rejected");
393690792Sgshapiro			usrerr("554 5.7.1 Command rejected");
393790792Sgshapiro			break;
393890792Sgshapiro
393990792Sgshapiro		  case SMFIR_DISCARD:
394090792Sgshapiro			if (MilterLogLevel > 3)
394190792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
394290792Sgshapiro					  "Milter: data, discard");
394390792Sgshapiro			milteraccept = false;
394490792Sgshapiro			e->e_flags |= EF_DISCARD;
394590792Sgshapiro			break;
394690792Sgshapiro
394790792Sgshapiro		  case SMFIR_TEMPFAIL:
394890792Sgshapiro			if (MilterLogLevel > 3)
394990792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
395090792Sgshapiro					  "Milter: data, reject=%s",
395190792Sgshapiro					  MSG_TEMPFAIL);
395290792Sgshapiro			milteraccept = false;
3953203004Sgshapiro#if _FFR_MILTER_ENHSC
3954203004Sgshapiro			(void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
3955363466Sgshapiro#endif
3956363466Sgshapiro			/* Can't use ("%s", ...) due to usrerr() requirements */
395790792Sgshapiro			usrerr(MSG_TEMPFAIL);
395890792Sgshapiro			break;
3959157001Sgshapiro
3960157001Sgshapiro		  case SMFIR_SHUTDOWN:
3961157001Sgshapiro			if (MilterLogLevel > 3)
3962157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3963157001Sgshapiro					  "Milter: data, reject=421 4.7.0 %s closing connection",
3964157001Sgshapiro					  MyHostName);
3965157001Sgshapiro			milteraccept = false;
3966157001Sgshapiro			usrerr("421 4.7.0 %s closing connection", MyHostName);
3967157001Sgshapiro			rv = false;
3968157001Sgshapiro			break;
396990792Sgshapiro		}
397090792Sgshapiro		if (response != NULL)
397190792Sgshapiro			sm_free(response);
397290792Sgshapiro	}
397390792Sgshapiro
397490792Sgshapiro	/* Milter may have changed message size */
3975244833Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "%ld", PRT_NONNEGL(e->e_msgsize));
397690792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
397790792Sgshapiro
397890792Sgshapiro	/* abort message filters that didn't get the body & log msg is OK */
397990792Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize)
398090792Sgshapiro	{
398190792Sgshapiro		milter_abort(e);
398290792Sgshapiro		if (milteraccept && MilterLogLevel > 9)
398390792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
398490792Sgshapiro	}
3985132943Sgshapiro
3986132943Sgshapiro	/*
3987132943Sgshapiro	**  If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or
3988132943Sgshapiro	**  milter accepted message, sync it now
3989132943Sgshapiro	**
3990132943Sgshapiro	**  XXX This is almost a copy of the code in collect(): put it into
3991132943Sgshapiro	**	a function that is called from both places?
3992132943Sgshapiro	*/
3993132943Sgshapiro
3994132943Sgshapiro	if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER)
3995132943Sgshapiro	{
3996132943Sgshapiro		int afd;
3997132943Sgshapiro		SM_FILE_T *volatile df;
3998132943Sgshapiro		char *dfname;
3999132943Sgshapiro
4000132943Sgshapiro		df = e->e_dfp;
4001132943Sgshapiro		dfname = queuename(e, DATAFL_LETTER);
4002132943Sgshapiro		if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0
4003132943Sgshapiro		    && errno != EINVAL)
4004132943Sgshapiro		{
4005132943Sgshapiro			int save_errno;
4006132943Sgshapiro
4007132943Sgshapiro			save_errno = errno;
4008132943Sgshapiro			if (save_errno == EEXIST)
4009132943Sgshapiro			{
4010132943Sgshapiro				struct stat st;
4011132943Sgshapiro				int dfd;
4012132943Sgshapiro
4013132943Sgshapiro				if (stat(dfname, &st) < 0)
4014132943Sgshapiro					st.st_size = -1;
4015132943Sgshapiro				errno = EEXIST;
4016132943Sgshapiro				syserr("@collect: bfcommit(%s): already on disk, size=%ld",
4017132943Sgshapiro				       dfname, (long) st.st_size);
4018132943Sgshapiro				dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
4019132943Sgshapiro				if (dfd >= 0)
4020132943Sgshapiro					dumpfd(dfd, true, true);
4021132943Sgshapiro			}
4022132943Sgshapiro			errno = save_errno;
4023132943Sgshapiro			dferror(df, "bfcommit", e);
4024132943Sgshapiro			flush_errors(true);
4025132943Sgshapiro			finis(save_errno != EEXIST, true, ExitStat);
4026132943Sgshapiro		}
4027132943Sgshapiro		else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
4028132943Sgshapiro		{
4029132943Sgshapiro			dferror(df, "sm_io_getinfo", e);
4030132943Sgshapiro			flush_errors(true);
4031132943Sgshapiro			finis(true, true, ExitStat);
4032132943Sgshapiro			/* NOTREACHED */
4033132943Sgshapiro		}
4034132943Sgshapiro		else if (fsync(afd) < 0)
4035132943Sgshapiro		{
4036132943Sgshapiro			dferror(df, "fsync", e);
4037132943Sgshapiro			flush_errors(true);
4038132943Sgshapiro			finis(true, true, ExitStat);
4039132943Sgshapiro			/* NOTREACHED */
4040132943Sgshapiro		}
4041132943Sgshapiro		else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
4042132943Sgshapiro		{
4043132943Sgshapiro			dferror(df, "sm_io_close", e);
4044132943Sgshapiro			flush_errors(true);
4045132943Sgshapiro			finis(true, true, ExitStat);
4046132943Sgshapiro			/* NOTREACHED */
4047132943Sgshapiro		}
4048132943Sgshapiro
4049132943Sgshapiro		/* Now reopen the df file */
4050132943Sgshapiro		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
4051132943Sgshapiro					SM_IO_RDONLY, NULL);
4052132943Sgshapiro		if (e->e_dfp == NULL)
4053132943Sgshapiro		{
4054132943Sgshapiro			/* we haven't acked receipt yet, so just chuck this */
4055132943Sgshapiro			syserr("@Cannot reopen %s", dfname);
4056132943Sgshapiro			finis(true, true, ExitStat);
4057132943Sgshapiro			/* NOTREACHED */
4058132943Sgshapiro		}
4059132943Sgshapiro	}
406090792Sgshapiro#endif /* MILTER */
406190792Sgshapiro
406290792Sgshapiro	/* Check if quarantining stats should be updated */
406390792Sgshapiro	if (e->e_quarmsg != NULL)
406490792Sgshapiro		markstats(e, NULL, STATS_QUARANTINE);
406590792Sgshapiro
406690792Sgshapiro	/*
406790792Sgshapiro	**  If a header/body check (header checks or milter)
406890792Sgshapiro	**  set EF_DISCARD, don't queueup the message --
406990792Sgshapiro	**  that would lose the EF_DISCARD bit and deliver
407090792Sgshapiro	**  the message.
407190792Sgshapiro	*/
407290792Sgshapiro
407390792Sgshapiro	if (bitset(EF_DISCARD, e->e_flags))
407490792Sgshapiro		doublequeue = false;
407590792Sgshapiro
407690792Sgshapiro	aborting = Errors > 0;
4077125820Sgshapiro	if (!(aborting || bitset(EF_DISCARD, e->e_flags)) &&
407890792Sgshapiro	    (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
407990792Sgshapiro	    !split_by_recipient(e))
408090792Sgshapiro		aborting = bitset(EF_FATALERRS, e->e_flags);
408190792Sgshapiro
408290792Sgshapiro	if (aborting)
408390792Sgshapiro	{
4084173340Sgshapiro		ADDRESS *q;
4085173340Sgshapiro
408690792Sgshapiro		/* Log who the mail would have gone to */
408790792Sgshapiro		logundelrcpts(e, e->e_message, 8, false);
4088173340Sgshapiro
4089173340Sgshapiro		/*
4090173340Sgshapiro		**  If something above refused the message, we still haven't
4091173340Sgshapiro		**  accepted responsibility for it.  Don't send DSNs.
4092173340Sgshapiro		*/
4093173340Sgshapiro
4094173340Sgshapiro		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
4095173340Sgshapiro			q->q_flags &= ~Q_PINGFLAGS;
4096173340Sgshapiro
409790792Sgshapiro		flush_errors(true);
409890792Sgshapiro		buffer_errors();
409990792Sgshapiro		goto abortmessage;
410090792Sgshapiro	}
410190792Sgshapiro
410290792Sgshapiro	/* from now on, we have to operate silently */
410390792Sgshapiro	buffer_errors();
410490792Sgshapiro
410590792Sgshapiro#if 0
410690792Sgshapiro	/*
410790792Sgshapiro	**  Clear message, it may contain an error from the SMTP dialogue.
410890792Sgshapiro	**  This error must not show up in the queue.
410990792Sgshapiro	**	Some error message should show up, e.g., alias database
411090792Sgshapiro	**	not available, but others shouldn't, e.g., from check_rcpt.
411190792Sgshapiro	*/
411290792Sgshapiro
411390792Sgshapiro	e->e_message = NULL;
411490792Sgshapiro#endif /* 0 */
411590792Sgshapiro
411690792Sgshapiro	/*
411790792Sgshapiro	**  Arrange to send to everyone.
411890792Sgshapiro	**	If sending to multiple people, mail back
411990792Sgshapiro	**		errors rather than reporting directly.
412090792Sgshapiro	**	In any case, don't mail back errors for
412190792Sgshapiro	**		anything that has happened up to
412290792Sgshapiro	**		now (the other end will do this).
412390792Sgshapiro	**	Truncate our transcript -- the mail has gotten
412490792Sgshapiro	**		to us successfully, and if we have
412590792Sgshapiro	**		to mail this back, it will be easier
412690792Sgshapiro	**		on the reader.
412790792Sgshapiro	**	Then send to everyone.
412890792Sgshapiro	**	Finally give a reply code.  If an error has
412990792Sgshapiro	**		already been given, don't mail a
413090792Sgshapiro	**		message back.
413190792Sgshapiro	**	We goose error returns by clearing error bit.
413290792Sgshapiro	*/
413390792Sgshapiro
413490792Sgshapiro	SmtpPhase = "delivery";
413590792Sgshapiro	(void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
413690792Sgshapiro	id = e->e_id;
413790792Sgshapiro
413890792Sgshapiro#if NAMED_BIND
413990792Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
414090792Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
4141363466Sgshapiro#endif
414290792Sgshapiro
4143285229Sgshapiro#if _FFR_PROXY
4144285229Sgshapiro	if (SM_PROXY_REQ == e->e_sendmode)
4145285229Sgshapiro	{
4146285229Sgshapiro		/* is proxy mode possible? */
4147285229Sgshapiro		if (e->e_sibling == NULL && e->e_nrcpts == 1
4148285229Sgshapiro		    && smtp->sm_nrcpts == 1
4149285229Sgshapiro		    && (a = e->e_sendqueue) != NULL && a->q_next == NULL)
4150285229Sgshapiro		{
4151285229Sgshapiro			a->q_flags &= ~(QPINGONFAILURE|QPINGONSUCCESS|
4152285229Sgshapiro					QPINGONDELAY);
4153285229Sgshapiro			e->e_errormode = EM_QUIET;
4154285229Sgshapiro			e->e_sendmode = SM_PROXY;
4155285229Sgshapiro		}
4156285229Sgshapiro		else
4157285229Sgshapiro		{
4158285229Sgshapiro			if (tTd(87, 2))
4159285229Sgshapiro			{
4160285229Sgshapiro				a = e->e_sendqueue;
4161285229Sgshapiro				sm_dprintf("srv: mode=%c, e=%p, sibling=%p, nrcpts=%d, sm_nrcpts=%d, sendqueue=%p, next=%p\n",
4162285229Sgshapiro				e->e_sendmode, e, e->e_sibling, e->e_nrcpts,
4163285229Sgshapiro				smtp->sm_nrcpts, a,
4164285229Sgshapiro				(a == NULL) ? (void *)0 : a->q_next);
4165285229Sgshapiro			}
4166223067Sgshapiro
4167285229Sgshapiro			/* switch to interactive mode */
4168285229Sgshapiro			e->e_sendmode = SM_DELIVER;
4169285229Sgshapiro			if (LogLevel > 9)
4170285229Sgshapiro				sm_syslog(LOG_DEBUG, e->e_id,
4171285229Sgshapiro					  "proxy mode requested but not possible");
4172285229Sgshapiro		}
4173285229Sgshapiro	}
4174285229Sgshapiro#endif /* _FFR_PROXY */
4175285229Sgshapiro
417690792Sgshapiro	for (ee = e; ee != NULL; ee = ee->e_sibling)
417790792Sgshapiro	{
417890792Sgshapiro		/* make sure we actually do delivery */
417990792Sgshapiro		ee->e_flags &= ~EF_CLRQUEUE;
418090792Sgshapiro
418190792Sgshapiro		/* from now on, operate silently */
418290792Sgshapiro		ee->e_errormode = EM_MAIL;
418390792Sgshapiro
418490792Sgshapiro		if (doublequeue)
418590792Sgshapiro		{
418690792Sgshapiro			/* make sure it is in the queue */
418790792Sgshapiro			queueup(ee, false, true);
418890792Sgshapiro		}
418990792Sgshapiro		else
419090792Sgshapiro		{
4191157001Sgshapiro			int mode;
4192157001Sgshapiro
419390792Sgshapiro			/* send to all recipients */
4194157001Sgshapiro			mode = SM_DEFAULT;
4195157001Sgshapiro#if _FFR_DM_ONE
4196157001Sgshapiro			if (SM_DM_ONE == e->e_sendmode)
4197157001Sgshapiro			{
4198157001Sgshapiro				if (NotFirstDelivery)
4199157001Sgshapiro				{
4200157001Sgshapiro					mode = SM_QUEUE;
4201157001Sgshapiro					e->e_sendmode = SM_QUEUE;
4202157001Sgshapiro				}
4203157001Sgshapiro				else
4204157001Sgshapiro				{
4205157001Sgshapiro					mode = SM_FORK;
4206157001Sgshapiro					NotFirstDelivery = true;
4207157001Sgshapiro				}
4208157001Sgshapiro			}
4209157001Sgshapiro#endif /* _FFR_DM_ONE */
4210157001Sgshapiro			sendall(ee, mode);
421190792Sgshapiro		}
421290792Sgshapiro		ee->e_to = NULL;
421390792Sgshapiro	}
421490792Sgshapiro
421598121Sgshapiro	/* put back id for SMTP logging in putoutmsg() */
421698121Sgshapiro	oldid = CurEnv->e_id;
421798121Sgshapiro	CurEnv->e_id = id;
421898121Sgshapiro
4219285229Sgshapiro#if _FFR_PROXY
4220285229Sgshapiro	a = e->e_sendqueue;
4221285229Sgshapiro	if (tTd(87, 1))
4222285229Sgshapiro	{
4223285229Sgshapiro		sm_dprintf("srv: mode=%c, e=%p, sibling=%p, nrcpts=%d, msg=%s, sendqueue=%p, next=%p, state=%d, SmtpError=%s, rcode=%d, renhsc=%s, text=%s\n",
4224285229Sgshapiro		e->e_sendmode, e, e->e_sibling, e->e_nrcpts, e->e_message, a,
4225285229Sgshapiro		(a == NULL) ? (void *)0 : a->q_next,
4226285229Sgshapiro		(a == NULL) ? -1 : a->q_state, SmtpError, e->e_rcode,
4227285229Sgshapiro		e->e_renhsc, e->e_text);
4228285229Sgshapiro	}
4229285229Sgshapiro
4230285229Sgshapiro	if (SM_PROXY == e->e_sendmode && a->q_state != QS_SENT &&
4231285229Sgshapiro	    a->q_state != QS_VERIFIED) /* discarded! */
4232285229Sgshapiro	{
4233285229Sgshapiro		char *m, *errtext;
4234285229Sgshapiro		char replycode[4];
4235285229Sgshapiro		char enhsc[10];
4236285229Sgshapiro		int offset;
4237285229Sgshapiro
4238285229Sgshapiro#define NN_MSG(e)	(((e)->e_message != NULL) ? (e)->e_message : "")
4239285229Sgshapiro		m = e->e_message;
4240285229Sgshapiro#define SM_MSG_DEFERRED "Deferred: "
4241285229Sgshapiro		if (m != NULL && strncmp(SM_MSG_DEFERRED, m,
4242285229Sgshapiro					 sizeof(SM_MSG_DEFERRED) - 1) == 0)
4243285229Sgshapiro			m += sizeof(SM_MSG_DEFERRED) - 1;
4244285229Sgshapiro		offset = extsc(m, ' ', replycode, enhsc);
4245285229Sgshapiro
4246285229Sgshapiro		if (tTd(87, 2))
4247285229Sgshapiro		{
4248285229Sgshapiro			sm_dprintf("srv: SmtpError=%s, rcode=%d, renhsc=%s, replycode=%s, enhsc=%s, offset=%d\n",
4249285229Sgshapiro				SmtpError, e->e_rcode, e->e_renhsc,
4250285229Sgshapiro				replycode, enhsc, offset);
4251285229Sgshapiro		}
4252285229Sgshapiro
4253285229Sgshapiro#define DIG2CHAR(d)	((d) + '0')
4254285229Sgshapiro		if (e->e_rcode != 0 && (replycode[0] == '\0' ||
4255285229Sgshapiro		    replycode[0] == DIG2CHAR(REPLYTYPE(e->e_rcode))))
4256285229Sgshapiro		{
4257285229Sgshapiro			replycode[0] = DIG2CHAR(REPLYTYPE(e->e_rcode));
4258285229Sgshapiro			replycode[1] = DIG2CHAR(REPLYCLASS(e->e_rcode));
4259285229Sgshapiro			replycode[2] = DIG2CHAR(REPLYMINOR(e->e_rcode));
4260285229Sgshapiro			replycode[3] = '\0';
4261285229Sgshapiro			if (e->e_renhsc[0] == replycode[0])
4262285229Sgshapiro				sm_strlcpy(enhsc, e->e_renhsc, sizeof(enhsc));
4263285229Sgshapiro			if (offset < 0)
4264285229Sgshapiro				offset = 0;
4265285229Sgshapiro		}
4266285229Sgshapiro		if (e->e_text != NULL)
4267285229Sgshapiro		{
4268285229Sgshapiro			(void) strreplnonprt(e->e_text, '_');
4269285229Sgshapiro			errtext = e->e_text;
4270285229Sgshapiro		}
4271285229Sgshapiro		else
4272285229Sgshapiro			errtext = m + offset;
4273285229Sgshapiro
4274285229Sgshapiro		if (replycode[0] != '\0' && enhsc[0] != '\0')
4275285229Sgshapiro			emessage(replycode, enhsc, "%s", errtext);
4276285229Sgshapiro		else if (replycode[0] != '\0')
4277285229Sgshapiro			emessage(replycode, smtptodsn(atoi(replycode)),
4278285229Sgshapiro				 "%s", errtext);
4279285229Sgshapiro		else if (QS_IS_TEMPFAIL(a->q_state))
4280285229Sgshapiro		{
4281285229Sgshapiro			if (m != NULL)
4282285229Sgshapiro				message("450 4.5.1 %s", m);
4283285229Sgshapiro			else
4284285229Sgshapiro				message("450 4.5.1 Temporary error");
4285285229Sgshapiro		}
4286285229Sgshapiro		else
4287285229Sgshapiro		{
4288285229Sgshapiro			if (m != NULL)
4289285229Sgshapiro				message("550 5.5.1 %s", m);
4290285229Sgshapiro			else
4291285229Sgshapiro				message("550 5.0.0 Permanent error");
4292285229Sgshapiro		}
4293285229Sgshapiro	}
4294285229Sgshapiro	else
4295285229Sgshapiro	{
4296285229Sgshapiro#endif /* _FFR_PROXY */
4297223067Sgshapiro		/* issue success message */
4298157001Sgshapiro#if _FFR_MSG_ACCEPT
4299223067Sgshapiro		if (MessageAccept != NULL && *MessageAccept != '\0')
4300223067Sgshapiro		{
4301223067Sgshapiro			char msg[MAXLINE];
4302157001Sgshapiro
4303223067Sgshapiro			expand(MessageAccept, msg, sizeof(msg), e);
4304223067Sgshapiro			message("250 2.0.0 %s", msg);
4305223067Sgshapiro		}
4306223067Sgshapiro		else
4307157001Sgshapiro#endif /* _FFR_MSG_ACCEPT */
4308223067Sgshapiro		message("250 2.0.0 %s Message accepted for delivery", id);
4309285229Sgshapiro#if _FFR_PROXY
4310285229Sgshapiro	}
4311363466Sgshapiro#endif
431298121Sgshapiro	CurEnv->e_id = oldid;
431390792Sgshapiro
431490792Sgshapiro	/* if we just queued, poke it */
431590792Sgshapiro	if (doublequeue)
431690792Sgshapiro	{
431790792Sgshapiro		bool anything_to_send = false;
431890792Sgshapiro
431990792Sgshapiro		sm_getla();
432090792Sgshapiro		for (ee = e; ee != NULL; ee = ee->e_sibling)
432190792Sgshapiro		{
432290792Sgshapiro			if (WILL_BE_QUEUED(ee->e_sendmode))
432390792Sgshapiro				continue;
432490792Sgshapiro			if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
432590792Sgshapiro			{
432690792Sgshapiro				ee->e_sendmode = SM_QUEUE;
432790792Sgshapiro				continue;
432890792Sgshapiro			}
432990792Sgshapiro			else if (QueueMode != QM_QUARANTINE &&
433090792Sgshapiro				 ee->e_quarmsg != NULL)
433190792Sgshapiro			{
433290792Sgshapiro				ee->e_sendmode = SM_QUEUE;
433390792Sgshapiro				continue;
433490792Sgshapiro			}
433590792Sgshapiro			anything_to_send = true;
433690792Sgshapiro
433790792Sgshapiro			/* close all the queue files */
433890792Sgshapiro			closexscript(ee);
433990792Sgshapiro			if (ee->e_dfp != NULL)
434090792Sgshapiro			{
434190792Sgshapiro				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
434290792Sgshapiro				ee->e_dfp = NULL;
434390792Sgshapiro			}
434490792Sgshapiro			unlockqueue(ee);
434590792Sgshapiro		}
434690792Sgshapiro		if (anything_to_send)
434790792Sgshapiro		{
434890792Sgshapiro#if PIPELINING
434990792Sgshapiro			/*
435090792Sgshapiro			**  XXX if we don't do this, we get 250 twice
435190792Sgshapiro			**	because it is also flushed in the child.
435290792Sgshapiro			*/
435390792Sgshapiro
435490792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
435590792Sgshapiro#endif /* PIPELINING */
435690792Sgshapiro			(void) doworklist(e, true, true);
435790792Sgshapiro		}
435890792Sgshapiro	}
435990792Sgshapiro
436090792Sgshapiro  abortmessage:
4361203004Sgshapiro	if (tTd(92, 2))
4362203004Sgshapiro		sm_dprintf("abortmessage: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",
4363203004Sgshapiro			e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel);
436490792Sgshapiro	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
436590792Sgshapiro		logsender(e, NULL);
436690792Sgshapiro	e->e_flags &= ~EF_LOGSENDER;
436790792Sgshapiro
436890792Sgshapiro	/* clean up a bit */
436990792Sgshapiro	smtp->sm_gotmail = false;
437090792Sgshapiro
437190792Sgshapiro	/*
437290792Sgshapiro	**  Call dropenvelope if and only if the envelope is *not*
437390792Sgshapiro	**  being processed by the child process forked by doworklist().
437490792Sgshapiro	*/
437590792Sgshapiro
437690792Sgshapiro	if (aborting || bitset(EF_DISCARD, e->e_flags))
4377203004Sgshapiro		(void) dropenvelope(e, true, false);
437890792Sgshapiro	else
437990792Sgshapiro	{
438090792Sgshapiro		for (ee = e; ee != NULL; ee = ee->e_sibling)
438190792Sgshapiro		{
438290792Sgshapiro			if (!doublequeue &&
438390792Sgshapiro			    QueueMode != QM_QUARANTINE &&
438490792Sgshapiro			    ee->e_quarmsg != NULL)
438590792Sgshapiro			{
4386203004Sgshapiro				(void) dropenvelope(ee, true, false);
438790792Sgshapiro				continue;
438890792Sgshapiro			}
438990792Sgshapiro			if (WILL_BE_QUEUED(ee->e_sendmode))
4390203004Sgshapiro				(void) dropenvelope(ee, true, false);
439190792Sgshapiro		}
439290792Sgshapiro	}
439390792Sgshapiro
439490792Sgshapiro	CurEnv = e;
4395168515Sgshapiro	features = e->e_features;
4396182352Sgshapiro	sm_rpool_free(e->e_rpool);
439790792Sgshapiro	newenvelope(e, e, sm_rpool_new_x(NULL));
439890792Sgshapiro	e->e_flags = BlankEnvelope.e_flags;
4399168515Sgshapiro	e->e_features = features;
440090792Sgshapiro
440190792Sgshapiro	/* restore connection quarantining */
440290792Sgshapiro	if (smtp->sm_quarmsg == NULL)
440390792Sgshapiro	{
440490792Sgshapiro		e->e_quarmsg = NULL;
440590792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
440690792Sgshapiro	}
440790792Sgshapiro	else
440890792Sgshapiro	{
440990792Sgshapiro		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
441090792Sgshapiro		macdefine(&e->e_macro, A_PERM,
441190792Sgshapiro			  macid("{quarantine}"), e->e_quarmsg);
441290792Sgshapiro	}
4413157001Sgshapiro	return rv;
441438032Speter}
441590792Sgshapiro/*
441690792Sgshapiro**  LOGUNDELRCPTS -- log undelivered (or all) recipients.
441790792Sgshapiro**
441890792Sgshapiro**	Parameters:
441990792Sgshapiro**		e -- envelope.
442090792Sgshapiro**		msg -- message for Stat=
442190792Sgshapiro**		level -- log level.
442290792Sgshapiro**		all -- log all recipients.
442390792Sgshapiro**
442490792Sgshapiro**	Returns:
442590792Sgshapiro**		none.
442690792Sgshapiro**
442790792Sgshapiro**	Side Effects:
442890792Sgshapiro**		logs undelivered (or all) recipients
442990792Sgshapiro*/
443090792Sgshapiro
443190792Sgshapirovoid
443290792Sgshapirologundelrcpts(e, msg, level, all)
443390792Sgshapiro	ENVELOPE *e;
443490792Sgshapiro	char *msg;
443590792Sgshapiro	int level;
443690792Sgshapiro	bool all;
443790792Sgshapiro{
443890792Sgshapiro	ADDRESS *a;
443990792Sgshapiro
444090792Sgshapiro	if (LogLevel <= level || msg == NULL || *msg == '\0')
444190792Sgshapiro		return;
444290792Sgshapiro
444390792Sgshapiro	/* Clear $h so relay= doesn't get mislogged by logdelivery() */
444490792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', NULL);
444590792Sgshapiro
444690792Sgshapiro	/* Log who the mail would have gone to */
444790792Sgshapiro	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
444890792Sgshapiro	{
444990792Sgshapiro		if (!QS_IS_UNDELIVERED(a->q_state) && !all)
445090792Sgshapiro			continue;
445190792Sgshapiro		e->e_to = a->q_paddr;
4452203004Sgshapiro		logdelivery(NULL, NULL,
4453203004Sgshapiro#if _FFR_MILTER_ENHSC
4454203004Sgshapiro			    (a->q_status == NULL && e->e_enhsc[0] != '\0')
4455203004Sgshapiro			    ? e->e_enhsc :
4456363466Sgshapiro#endif
4457203004Sgshapiro			    a->q_status,
4458285229Sgshapiro			    msg, NULL, (time_t) 0, e, a, EX_OK /* ??? */);
445990792Sgshapiro	}
446090792Sgshapiro	e->e_to = NULL;
446190792Sgshapiro}
446290792Sgshapiro/*
446338032Speter**  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
446438032Speter**
446538032Speter**	Parameters:
446638032Speter**		pcounter -- pointer to a counter for this command.
446738032Speter**		maxcount -- maximum value for this counter before we
446838032Speter**			slow down.
446964562Sgshapiro**		waitnow -- sleep now (in this routine)?
447038032Speter**		cname -- command name for logging.
447138032Speter**		e -- the current envelope.
447238032Speter**
447338032Speter**	Returns:
4474132943Sgshapiro**		time to wait,
4475132943Sgshapiro**		STOP_ATTACK if twice as many commands as allowed and
4476132943Sgshapiro**			MaxChildren > 0.
447738032Speter**
447838032Speter**	Side Effects:
447938032Speter**		Slows down if we seem to be under attack.
448038032Speter*/
448138032Speter
448264562Sgshapirostatic time_t
448364562Sgshapirochecksmtpattack(pcounter, maxcount, waitnow, cname, e)
448490792Sgshapiro	volatile unsigned int *pcounter;
4485132943Sgshapiro	unsigned int maxcount;
448664562Sgshapiro	bool waitnow;
448738032Speter	char *cname;
448838032Speter	ENVELOPE *e;
448938032Speter{
449090792Sgshapiro	if (maxcount <= 0)	/* no limit */
449190792Sgshapiro		return (time_t) 0;
449290792Sgshapiro
449338032Speter	if (++(*pcounter) >= maxcount)
449438032Speter	{
4495132943Sgshapiro		unsigned int shift;
449664562Sgshapiro		time_t s;
449764562Sgshapiro
449838032Speter		if (*pcounter == maxcount && LogLevel > 5)
449938032Speter		{
450038032Speter			sm_syslog(LOG_INFO, e->e_id,
4501110560Sgshapiro				  "%s: possible SMTP attack: command=%.40s, count=%u",
450277349Sgshapiro				  CurSmtpClient, cname, *pcounter);
450338032Speter		}
4504132943Sgshapiro		shift = *pcounter - maxcount;
4505132943Sgshapiro		s = 1 << shift;
4506132943Sgshapiro		if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0)
450764562Sgshapiro			s = MAXTIMEOUT;
450890792Sgshapiro
4509132943Sgshapiro#define IS_ATTACK(s)	((MaxChildren > 0 && *pcounter >= maxcount * 2)	\
4510132943Sgshapiro				? STOP_ATTACK : (time_t) s)
4511132943Sgshapiro
451264562Sgshapiro		/* sleep at least 1 second before returning */
451364562Sgshapiro		(void) sleep(*pcounter / maxcount);
451464562Sgshapiro		s -= *pcounter / maxcount;
4515132943Sgshapiro		if (s >= MAXTIMEOUT || s < 0)
4516132943Sgshapiro			s = MAXTIMEOUT;
4517132943Sgshapiro		if (waitnow && s > 0)
451864562Sgshapiro		{
451964562Sgshapiro			(void) sleep(s);
4520132943Sgshapiro			return IS_ATTACK(0);
452164562Sgshapiro		}
4522132943Sgshapiro		return IS_ATTACK(s);
452338032Speter	}
452490792Sgshapiro	return (time_t) 0;
452538032Speter}
452690792Sgshapiro/*
452790792Sgshapiro**  SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
452890792Sgshapiro**
452990792Sgshapiro**	Parameters:
453090792Sgshapiro**		none.
453190792Sgshapiro**
453290792Sgshapiro**	Returns:
453390792Sgshapiro**		nothing.
453490792Sgshapiro**
453590792Sgshapiro**	Side Effects:
453690792Sgshapiro**		may change I/O fd.
453790792Sgshapiro*/
453890792Sgshapiro
453990792Sgshapirostatic void
454090792Sgshapirosetup_smtpd_io()
454190792Sgshapiro{
454290792Sgshapiro	int inchfd, outchfd, outfd;
454390792Sgshapiro
454490792Sgshapiro	inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
454590792Sgshapiro	outchfd  = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
454690792Sgshapiro	outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
454790792Sgshapiro	if (outchfd != outfd)
454890792Sgshapiro	{
454990792Sgshapiro		/* arrange for debugging output to go to remote host */
455090792Sgshapiro		(void) dup2(outchfd, outfd);
455190792Sgshapiro	}
455290792Sgshapiro
455390792Sgshapiro	/*
455490792Sgshapiro	**  if InChannel and OutChannel are stdin/stdout
455590792Sgshapiro	**  and connected to ttys
455690792Sgshapiro	**  and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
455790792Sgshapiro	**  then "chain" them together.
455890792Sgshapiro	*/
455990792Sgshapiro
456090792Sgshapiro	if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
456190792Sgshapiro	    isatty(inchfd) && isatty(outchfd))
456290792Sgshapiro	{
456390792Sgshapiro		int inmode, outmode;
456490792Sgshapiro
456590792Sgshapiro		inmode = fcntl(inchfd, F_GETFL, 0);
456690792Sgshapiro		if (inmode == -1)
456790792Sgshapiro		{
456890792Sgshapiro			if (LogLevel > 11)
456990792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
457090792Sgshapiro					"fcntl(inchfd, F_GETFL) failed: %s",
457190792Sgshapiro					sm_errstring(errno));
457290792Sgshapiro			return;
457390792Sgshapiro		}
457490792Sgshapiro		outmode = fcntl(outchfd, F_GETFL, 0);
457590792Sgshapiro		if (outmode == -1)
457690792Sgshapiro		{
457790792Sgshapiro			if (LogLevel > 11)
457890792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
457990792Sgshapiro					"fcntl(outchfd, F_GETFL) failed: %s",
458090792Sgshapiro					sm_errstring(errno));
458190792Sgshapiro			return;
458290792Sgshapiro		}
458390792Sgshapiro		if (bitset(O_NONBLOCK, inmode) ||
458490792Sgshapiro		    bitset(O_NONBLOCK, outmode) ||
458590792Sgshapiro		    fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
458690792Sgshapiro			return;
458790792Sgshapiro		outmode = fcntl(outchfd, F_GETFL, 0);
458890792Sgshapiro		if (outmode != -1 && bitset(O_NONBLOCK, outmode))
458990792Sgshapiro		{
459090792Sgshapiro			/* changing InChannel also changes OutChannel */
459190792Sgshapiro			sm_io_automode(OutChannel, InChannel);
459290792Sgshapiro			if (tTd(97, 4) && LogLevel > 9)
459390792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
459490792Sgshapiro					  "set automode for I (%d)/O (%d) in SMTP server",
459590792Sgshapiro					  inchfd, outchfd);
459690792Sgshapiro		}
459790792Sgshapiro
459890792Sgshapiro		/* undo change of inchfd */
459990792Sgshapiro		(void) fcntl(inchfd, F_SETFL, inmode);
460090792Sgshapiro	}
460190792Sgshapiro}
460290792Sgshapiro/*
460338032Speter**  SKIPWORD -- skip a fixed word.
460438032Speter**
460538032Speter**	Parameters:
460638032Speter**		p -- place to start looking.
460738032Speter**		w -- word to skip.
460838032Speter**
460938032Speter**	Returns:
461038032Speter**		p following w.
461138032Speter**		NULL on error.
461238032Speter**
461338032Speter**	Side Effects:
461438032Speter**		clobbers the p data area.
461538032Speter*/
461638032Speter
461738032Speterstatic char *
461838032Speterskipword(p, w)
461938032Speter	register char *volatile p;
462038032Speter	char *w;
462138032Speter{
462238032Speter	register char *q;
462338032Speter	char *firstp = p;
462438032Speter
462538032Speter	/* find beginning of word */
462690792Sgshapiro	SKIP_SPACE(p);
462738032Speter	q = p;
462838032Speter
462938032Speter	/* find end of word */
4630363466Sgshapiro	while (*p != '\0' && *p != ':' && !(SM_ISSPACE(*p)))
463138032Speter		p++;
4632363466Sgshapiro	while (SM_ISSPACE(*p))
463338032Speter		*p++ = '\0';
463438032Speter	if (*p != ':')
463538032Speter	{
463638032Speter	  syntax:
463764562Sgshapiro		usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
463838032Speter			shortenstring(firstp, MAXSHORTSTR));
463964562Sgshapiro		return NULL;
464038032Speter	}
464138032Speter	*p++ = '\0';
464290792Sgshapiro	SKIP_SPACE(p);
464338032Speter
464438032Speter	if (*p == '\0')
464538032Speter		goto syntax;
464638032Speter
464738032Speter	/* see if the input word matches desired word */
464890792Sgshapiro	if (sm_strcasecmp(q, w))
464938032Speter		goto syntax;
465038032Speter
465164562Sgshapiro	return p;
465238032Speter}
4653159609Sgshapiro
465490792Sgshapiro/*
4655168515Sgshapiro**  RESET_MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
4656168515Sgshapiro**
4657168515Sgshapiro**	Parameters:
4658168515Sgshapiro**		e -- the envelope.
4659168515Sgshapiro**
4660168515Sgshapiro**	Returns:
4661168515Sgshapiro**		none.
4662168515Sgshapiro*/
4663168515Sgshapiro
4664168515Sgshapirovoid
4665168515Sgshapiroreset_mail_esmtp_args(e)
4666168515Sgshapiro	ENVELOPE *e;
4667168515Sgshapiro{
4668168515Sgshapiro	/* "size": no reset */
4669168515Sgshapiro
4670168515Sgshapiro	/* "body" */
4671168515Sgshapiro	SevenBitInput = SevenBitInput_Saved;
4672168515Sgshapiro	e->e_bodytype = NULL;
4673168515Sgshapiro
4674168515Sgshapiro	/* "envid" */
4675168515Sgshapiro	e->e_envid = NULL;
4676168515Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{dsn_envid}"), NULL);
4677168515Sgshapiro
4678168515Sgshapiro	/* "ret" */
4679173340Sgshapiro	e->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
4680168515Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), NULL);
4681168515Sgshapiro
4682168515Sgshapiro#if SASL
4683168515Sgshapiro	/* "auth" */
4684168515Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), NULL);
4685168515Sgshapiro	e->e_auth_param = "";
4686168515Sgshapiro# if _FFR_AUTH_PASSING
4687168515Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_PERM,
4688168515Sgshapiro				  macid("{auth_author}"), NULL);
4689363466Sgshapiro# endif
4690168515Sgshapiro#endif /* SASL */
4691168515Sgshapiro
4692168515Sgshapiro	/* "by" */
4693168515Sgshapiro	e->e_deliver_by = 0;
4694168515Sgshapiro	e->e_dlvr_flag = 0;
4695168515Sgshapiro}
4696168515Sgshapiro
4697168515Sgshapiro/*
469838032Speter**  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
469938032Speter**
470038032Speter**	Parameters:
4701168515Sgshapiro**		a -- address (unused, for compatibility with rcpt_esmtp_args)
470238032Speter**		kp -- the parameter key.
470338032Speter**		vp -- the value of that parameter.
470438032Speter**		e -- the envelope.
470538032Speter**
470638032Speter**	Returns:
470738032Speter**		none.
470838032Speter*/
470938032Speter
4710168515Sgshapirovoid
4711168515Sgshapiromail_esmtp_args(a, kp, vp, e)
4712168515Sgshapiro	ADDRESS *a;
471338032Speter	char *kp;
471438032Speter	char *vp;
471538032Speter	ENVELOPE *e;
471638032Speter{
471790792Sgshapiro	if (sm_strcasecmp(kp, "size") == 0)
471838032Speter	{
471938032Speter		if (vp == NULL)
472038032Speter		{
472164562Sgshapiro			usrerr("501 5.5.2 SIZE requires a value");
472238032Speter			/* NOTREACHED */
472338032Speter		}
472490792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
472590792Sgshapiro		errno = 0;
472671345Sgshapiro		e->e_msgsize = strtol(vp, (char **) NULL, 10);
472766494Sgshapiro		if (e->e_msgsize == LONG_MAX && errno == ERANGE)
472866494Sgshapiro		{
472966494Sgshapiro			usrerr("552 5.2.3 Message size exceeds maximum value");
473066494Sgshapiro			/* NOTREACHED */
473166494Sgshapiro		}
473290792Sgshapiro		if (e->e_msgsize < 0)
473390792Sgshapiro		{
473490792Sgshapiro			usrerr("552 5.2.3 Message size invalid");
473590792Sgshapiro			/* NOTREACHED */
473690792Sgshapiro		}
473738032Speter	}
473890792Sgshapiro	else if (sm_strcasecmp(kp, "body") == 0)
473938032Speter	{
474038032Speter		if (vp == NULL)
474138032Speter		{
474264562Sgshapiro			usrerr("501 5.5.2 BODY requires a value");
474338032Speter			/* NOTREACHED */
474438032Speter		}
474590792Sgshapiro		else if (sm_strcasecmp(vp, "8bitmime") == 0)
474638032Speter		{
474790792Sgshapiro			SevenBitInput = false;
474838032Speter		}
474990792Sgshapiro		else if (sm_strcasecmp(vp, "7bit") == 0)
475038032Speter		{
475190792Sgshapiro			SevenBitInput = true;
475238032Speter		}
475338032Speter		else
475438032Speter		{
475590792Sgshapiro			usrerr("501 5.5.4 Unknown BODY type %s", vp);
475638032Speter			/* NOTREACHED */
475738032Speter		}
475890792Sgshapiro		e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
475938032Speter	}
476090792Sgshapiro	else if (sm_strcasecmp(kp, "envid") == 0)
476138032Speter	{
4762168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
476364562Sgshapiro		{
476464562Sgshapiro			usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
476564562Sgshapiro			/* NOTREACHED */
476664562Sgshapiro		}
476738032Speter		if (vp == NULL)
476838032Speter		{
476964562Sgshapiro			usrerr("501 5.5.2 ENVID requires a value");
477038032Speter			/* NOTREACHED */
477138032Speter		}
477238032Speter		if (!xtextok(vp))
477338032Speter		{
477464562Sgshapiro			usrerr("501 5.5.4 Syntax error in ENVID parameter value");
477538032Speter			/* NOTREACHED */
477638032Speter		}
477738032Speter		if (e->e_envid != NULL)
477838032Speter		{
477964562Sgshapiro			usrerr("501 5.5.0 Duplicate ENVID parameter");
478038032Speter			/* NOTREACHED */
478138032Speter		}
478290792Sgshapiro		e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
478390792Sgshapiro		macdefine(&e->e_macro, A_PERM,
478490792Sgshapiro			macid("{dsn_envid}"), e->e_envid);
478538032Speter	}
478690792Sgshapiro	else if (sm_strcasecmp(kp, "ret") == 0)
478738032Speter	{
4788168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
478964562Sgshapiro		{
479064562Sgshapiro			usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
479164562Sgshapiro			/* NOTREACHED */
479264562Sgshapiro		}
479338032Speter		if (vp == NULL)
479438032Speter		{
479564562Sgshapiro			usrerr("501 5.5.2 RET requires a value");
479638032Speter			/* NOTREACHED */
479738032Speter		}
479838032Speter		if (bitset(EF_RET_PARAM, e->e_flags))
479938032Speter		{
480064562Sgshapiro			usrerr("501 5.5.0 Duplicate RET parameter");
480138032Speter			/* NOTREACHED */
480238032Speter		}
480338032Speter		e->e_flags |= EF_RET_PARAM;
480490792Sgshapiro		if (sm_strcasecmp(vp, "hdrs") == 0)
480538032Speter			e->e_flags |= EF_NO_BODY_RETN;
480690792Sgshapiro		else if (sm_strcasecmp(vp, "full") != 0)
480738032Speter		{
480864562Sgshapiro			usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
480938032Speter			/* NOTREACHED */
481038032Speter		}
481190792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
481238032Speter	}
481390792Sgshapiro#if SASL
481490792Sgshapiro	else if (sm_strcasecmp(kp, "auth") == 0)
481564562Sgshapiro	{
481664562Sgshapiro		int len;
481764562Sgshapiro		char *q;
481864562Sgshapiro		char *auth_param;	/* the value of the AUTH=x */
481964562Sgshapiro		bool saveQuickAbort = QuickAbort;
482064562Sgshapiro		bool saveSuprErrs = SuprErrs;
482190792Sgshapiro		bool saveExitStat = ExitStat;
482264562Sgshapiro
482364562Sgshapiro		if (vp == NULL)
482464562Sgshapiro		{
482564562Sgshapiro			usrerr("501 5.5.2 AUTH= requires a value");
482664562Sgshapiro			/* NOTREACHED */
482764562Sgshapiro		}
482864562Sgshapiro		if (e->e_auth_param != NULL)
482964562Sgshapiro		{
483064562Sgshapiro			usrerr("501 5.5.0 Duplicate AUTH parameter");
483164562Sgshapiro			/* NOTREACHED */
483264562Sgshapiro		}
483364562Sgshapiro		if ((q = strchr(vp, ' ')) != NULL)
483464562Sgshapiro			len = q - vp + 1;
483564562Sgshapiro		else
483664562Sgshapiro			len = strlen(vp) + 1;
483764562Sgshapiro		auth_param = xalloc(len);
483890792Sgshapiro		(void) sm_strlcpy(auth_param, vp, len);
483964562Sgshapiro		if (!xtextok(auth_param))
484064562Sgshapiro		{
484164562Sgshapiro			usrerr("501 5.5.4 Syntax error in AUTH parameter value");
484264562Sgshapiro			/* just a warning? */
484364562Sgshapiro			/* NOTREACHED */
484464562Sgshapiro		}
484564562Sgshapiro
484664562Sgshapiro		/* XXX define this always or only if trusted? */
4847132943Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"),
4848132943Sgshapiro			  auth_param);
484964562Sgshapiro
485064562Sgshapiro		/*
485164562Sgshapiro		**  call Strust_auth to find out whether
485264562Sgshapiro		**  auth_param is acceptable (trusted)
485364562Sgshapiro		**  we shouldn't trust it if not authenticated
485464562Sgshapiro		**  (required by RFC, leave it to ruleset?)
485564562Sgshapiro		*/
485664562Sgshapiro
485790792Sgshapiro		SuprErrs = true;
485890792Sgshapiro		QuickAbort = false;
485964562Sgshapiro		if (strcmp(auth_param, "<>") != 0 &&
4860285229Sgshapiro		     (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM, 9,
4861285229Sgshapiro			      NULL, NOQID, NULL, NULL) != EX_OK || Errors > 0))
486264562Sgshapiro		{
486364562Sgshapiro			if (tTd(95, 8))
486464562Sgshapiro			{
486564562Sgshapiro				q = e->e_auth_param;
486690792Sgshapiro				sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
4867132943Sgshapiro					auth_param, (q == NULL) ? "" : q);
486864562Sgshapiro			}
486990792Sgshapiro
487064562Sgshapiro			/* not trusted */
487190792Sgshapiro			e->e_auth_param = "<>";
487290792Sgshapiro# if _FFR_AUTH_PASSING
487390792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
487490792Sgshapiro				  macid("{auth_author}"), NULL);
4875363466Sgshapiro# endif
487664562Sgshapiro		}
487764562Sgshapiro		else
487864562Sgshapiro		{
487964562Sgshapiro			if (tTd(95, 8))
4880132943Sgshapiro				sm_dprintf("auth=\"%.100s\" trusted\n", auth_param);
488190792Sgshapiro			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
488290792Sgshapiro							    auth_param);
488364562Sgshapiro		}
488490792Sgshapiro		sm_free(auth_param); /* XXX */
488577349Sgshapiro
488664562Sgshapiro		/* reset values */
488764562Sgshapiro		Errors = 0;
488864562Sgshapiro		QuickAbort = saveQuickAbort;
488964562Sgshapiro		SuprErrs = saveSuprErrs;
489090792Sgshapiro		ExitStat = saveExitStat;
489164562Sgshapiro	}
489290792Sgshapiro#endif /* SASL */
489390792Sgshapiro#define PRTCHAR(c)	((isascii(c) && isprint(c)) ? (c) : '?')
489490792Sgshapiro
489590792Sgshapiro	/*
489690792Sgshapiro	**  "by" is only accepted if DeliverByMin >= 0.
489790792Sgshapiro	**  We maybe could add this to the list of server_features.
489890792Sgshapiro	*/
489990792Sgshapiro
490090792Sgshapiro	else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
490190792Sgshapiro	{
490290792Sgshapiro		char *s;
490390792Sgshapiro
490490792Sgshapiro		if (vp == NULL)
490590792Sgshapiro		{
490690792Sgshapiro			usrerr("501 5.5.2 BY= requires a value");
490790792Sgshapiro			/* NOTREACHED */
490890792Sgshapiro		}
490990792Sgshapiro		errno = 0;
491090792Sgshapiro		e->e_deliver_by = strtol(vp, &s, 10);
491190792Sgshapiro		if (e->e_deliver_by == LONG_MIN ||
491290792Sgshapiro		    e->e_deliver_by == LONG_MAX ||
491390792Sgshapiro		    e->e_deliver_by > 999999999l ||
491490792Sgshapiro		    e->e_deliver_by < -999999999l)
491590792Sgshapiro		{
491690792Sgshapiro			usrerr("501 5.5.2 BY=%s out of range", vp);
491790792Sgshapiro			/* NOTREACHED */
491890792Sgshapiro		}
491990792Sgshapiro		if (s == NULL || *s != ';')
492090792Sgshapiro		{
492190792Sgshapiro			usrerr("501 5.5.2 BY= missing ';'");
492290792Sgshapiro			/* NOTREACHED */
492390792Sgshapiro		}
492490792Sgshapiro		e->e_dlvr_flag = 0;
492590792Sgshapiro		++s;	/* XXX: spaces allowed? */
492690792Sgshapiro		SKIP_SPACE(s);
492790792Sgshapiro		switch (tolower(*s))
492890792Sgshapiro		{
492990792Sgshapiro		  case 'n':
493090792Sgshapiro			e->e_dlvr_flag = DLVR_NOTIFY;
493190792Sgshapiro			break;
493290792Sgshapiro		  case 'r':
493390792Sgshapiro			e->e_dlvr_flag = DLVR_RETURN;
493490792Sgshapiro			if (e->e_deliver_by <= 0)
493590792Sgshapiro			{
493690792Sgshapiro				usrerr("501 5.5.4 mode R requires BY time > 0");
493790792Sgshapiro				/* NOTREACHED */
493890792Sgshapiro			}
493990792Sgshapiro			if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
494090792Sgshapiro			    e->e_deliver_by < DeliverByMin)
494190792Sgshapiro			{
494290792Sgshapiro				usrerr("555 5.5.2 time %ld less than %ld",
494390792Sgshapiro					e->e_deliver_by, (long) DeliverByMin);
494490792Sgshapiro				/* NOTREACHED */
494590792Sgshapiro			}
494690792Sgshapiro			break;
494790792Sgshapiro		  default:
494890792Sgshapiro			usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
494990792Sgshapiro			/* NOTREACHED */
495090792Sgshapiro		}
495190792Sgshapiro		++s;	/* XXX: spaces allowed? */
495290792Sgshapiro		SKIP_SPACE(s);
495390792Sgshapiro		switch (tolower(*s))
495490792Sgshapiro		{
495590792Sgshapiro		  case 't':
495690792Sgshapiro			e->e_dlvr_flag |= DLVR_TRACE;
495790792Sgshapiro			break;
495890792Sgshapiro		  case '\0':
495990792Sgshapiro			break;
496090792Sgshapiro		  default:
496190792Sgshapiro			usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
496290792Sgshapiro			/* NOTREACHED */
496390792Sgshapiro		}
496490792Sgshapiro
496590792Sgshapiro		/* XXX: check whether more characters follow? */
496690792Sgshapiro	}
4967363466Sgshapiro#if _FFR_EAI
4968363466Sgshapiro	else if (sm_strcasecmp(kp, "smtputf8") == 0)
4969363466Sgshapiro	{
4970363466Sgshapiro		if (!bitset(SRV_OFFER_EAI, e->e_features))
4971363466Sgshapiro		{
4972363466Sgshapiro			usrerr("504 5.7.0 Sorry, SMTPUTF8 not supported/enabled");
4973363466Sgshapiro			/* NOTREACHED */
4974363466Sgshapiro		}
4975363466Sgshapiro		e->e_smtputf8 = true;
4976363466Sgshapiro	}
4977363466Sgshapiro#endif
497838032Speter	else
497938032Speter	{
498066494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
498138032Speter		/* NOTREACHED */
498238032Speter	}
498338032Speter}
4984168515Sgshapiro
498590792Sgshapiro/*
498638032Speter**  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
498738032Speter**
498838032Speter**	Parameters:
498938032Speter**		a -- the address corresponding to the To: parameter.
499038032Speter**		kp -- the parameter key.
499138032Speter**		vp -- the value of that parameter.
499238032Speter**		e -- the envelope.
499338032Speter**
499438032Speter**	Returns:
499538032Speter**		none.
499638032Speter*/
499738032Speter
4998168515Sgshapirovoid
4999168515Sgshapirorcpt_esmtp_args(a, kp, vp, e)
500038032Speter	ADDRESS *a;
500138032Speter	char *kp;
500238032Speter	char *vp;
500338032Speter	ENVELOPE *e;
500438032Speter{
500590792Sgshapiro	if (sm_strcasecmp(kp, "notify") == 0)
500638032Speter	{
500738032Speter		char *p;
500838032Speter
5009168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
501064562Sgshapiro		{
501164562Sgshapiro			usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
501264562Sgshapiro			/* NOTREACHED */
501364562Sgshapiro		}
501438032Speter		if (vp == NULL)
501538032Speter		{
501664562Sgshapiro			usrerr("501 5.5.2 NOTIFY requires a value");
501738032Speter			/* NOTREACHED */
501838032Speter		}
501938032Speter		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
502038032Speter		a->q_flags |= QHASNOTIFY;
502190792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
502264562Sgshapiro
502390792Sgshapiro		if (sm_strcasecmp(vp, "never") == 0)
502438032Speter			return;
502538032Speter		for (p = vp; p != NULL; vp = p)
502638032Speter		{
5027141858Sgshapiro			char *s;
5028141858Sgshapiro
5029141858Sgshapiro			s = p = strchr(p, ',');
503038032Speter			if (p != NULL)
503138032Speter				*p++ = '\0';
503290792Sgshapiro			if (sm_strcasecmp(vp, "success") == 0)
503338032Speter				a->q_flags |= QPINGONSUCCESS;
503490792Sgshapiro			else if (sm_strcasecmp(vp, "failure") == 0)
503538032Speter				a->q_flags |= QPINGONFAILURE;
503690792Sgshapiro			else if (sm_strcasecmp(vp, "delay") == 0)
503738032Speter				a->q_flags |= QPINGONDELAY;
503838032Speter			else
503938032Speter			{
504064562Sgshapiro				usrerr("501 5.5.4 Bad argument \"%s\"  to NOTIFY",
504138032Speter					vp);
504238032Speter				/* NOTREACHED */
504338032Speter			}
5044141858Sgshapiro			if (s != NULL)
5045141858Sgshapiro				*s = ',';
504638032Speter		}
504738032Speter	}
504890792Sgshapiro	else if (sm_strcasecmp(kp, "orcpt") == 0)
504938032Speter	{
5050249729Sgshapiro		char *p;
5051249729Sgshapiro
5052168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
505364562Sgshapiro		{
505464562Sgshapiro			usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
505564562Sgshapiro			/* NOTREACHED */
505664562Sgshapiro		}
505738032Speter		if (vp == NULL)
505838032Speter		{
505964562Sgshapiro			usrerr("501 5.5.2 ORCPT requires a value");
506038032Speter			/* NOTREACHED */
506138032Speter		}
5062249729Sgshapiro		if (a->q_orcpt != NULL)
506338032Speter		{
5064249729Sgshapiro			usrerr("501 5.5.0 Duplicate ORCPT parameter");
5065249729Sgshapiro			/* NOTREACHED */
5066249729Sgshapiro		}
5067249729Sgshapiro		p = strchr(vp, ';');
5068249729Sgshapiro		if (p == NULL)
5069249729Sgshapiro		{
507064562Sgshapiro			usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
507138032Speter			/* NOTREACHED */
507238032Speter		}
5073249729Sgshapiro		*p = '\0';
5074249729Sgshapiro		if (!isatom(vp) || !xtextok(p + 1))
507538032Speter		{
5076249729Sgshapiro			*p = ';';
5077249729Sgshapiro			usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
507838032Speter			/* NOTREACHED */
507938032Speter		}
5080249729Sgshapiro		*p = ';';
508190792Sgshapiro		a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
508238032Speter	}
508338032Speter	else
508438032Speter	{
508566494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
508638032Speter		/* NOTREACHED */
508738032Speter	}
508838032Speter}
508990792Sgshapiro/*
509038032Speter**  PRINTVRFYADDR -- print an entry in the verify queue
509138032Speter**
509238032Speter**	Parameters:
509390792Sgshapiro**		a -- the address to print.
509438032Speter**		last -- set if this is the last one.
509538032Speter**		vrfy -- set if this is a VRFY command.
509638032Speter**
509738032Speter**	Returns:
509838032Speter**		none.
509938032Speter**
510038032Speter**	Side Effects:
510138032Speter**		Prints the appropriate 250 codes.
510238032Speter*/
510364562Sgshapiro#define OFFF	(3 + 1 + 5 + 1)	/* offset in fmt: SMTP reply + enh. code */
510438032Speter
510564562Sgshapirostatic void
510638032Speterprintvrfyaddr(a, last, vrfy)
510738032Speter	register ADDRESS *a;
510838032Speter	bool last;
510938032Speter	bool vrfy;
511038032Speter{
511164562Sgshapiro	char fmtbuf[30];
511238032Speter
511338032Speter	if (vrfy && a->q_mailer != NULL &&
511438032Speter	    !bitnset(M_VRFY250, a->q_mailer->m_flags))
5115168515Sgshapiro		(void) sm_strlcpy(fmtbuf, "252", sizeof(fmtbuf));
511638032Speter	else
5117168515Sgshapiro		(void) sm_strlcpy(fmtbuf, "250", sizeof(fmtbuf));
511838032Speter	fmtbuf[3] = last ? ' ' : '-';
5119168515Sgshapiro	(void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof(fmtbuf) - 4);
512038032Speter	if (a->q_fullname == NULL)
512138032Speter	{
512264562Sgshapiro		if ((a->q_mailer == NULL ||
512364562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
512490792Sgshapiro		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
512564562Sgshapiro		    strchr(a->q_user, '@') == NULL)
512690792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
5127168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
512838032Speter		else
512990792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
5130168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
513138032Speter		message(fmtbuf, a->q_user, MyHostName);
513238032Speter	}
513338032Speter	else
513438032Speter	{
513564562Sgshapiro		if ((a->q_mailer == NULL ||
513664562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
513790792Sgshapiro		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
513864562Sgshapiro		    strchr(a->q_user, '@') == NULL)
513990792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
5140168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
514138032Speter		else
514290792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
5143168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
514438032Speter		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
514538032Speter	}
514638032Speter}
514738032Speter
514890792Sgshapiro#if SASL
514990792Sgshapiro/*
515064562Sgshapiro**  SASLMECHS -- get list of possible AUTH mechanisms
515164562Sgshapiro**
515264562Sgshapiro**	Parameters:
515390792Sgshapiro**		conn -- SASL connection info.
515490792Sgshapiro**		mechlist -- output parameter for list of mechanisms.
515564562Sgshapiro**
515664562Sgshapiro**	Returns:
515790792Sgshapiro**		number of mechs.
515864562Sgshapiro*/
515964562Sgshapiro
516064562Sgshapirostatic int
516164562Sgshapirosaslmechs(conn, mechlist)
516264562Sgshapiro	sasl_conn_t *conn;
516364562Sgshapiro	char **mechlist;
516464562Sgshapiro{
516564562Sgshapiro	int len, num, result;
516664562Sgshapiro
516764562Sgshapiro	/* "user" is currently unused */
516898121Sgshapiro# if SASL >= 20000
516998121Sgshapiro	result = sasl_listmech(conn, NULL,
517098121Sgshapiro			       "", " ", "", (const char **) mechlist,
5171120256Sgshapiro			       (unsigned int *)&len, &num);
517298121Sgshapiro# else /* SASL >= 20000 */
517364562Sgshapiro	result = sasl_listmech(conn, "user", /* XXX */
517464562Sgshapiro			       "", " ", "", mechlist,
517590792Sgshapiro			       (unsigned int *)&len, (unsigned int *)&num);
517698121Sgshapiro# endif /* SASL >= 20000 */
517790792Sgshapiro	if (result != SASL_OK)
517864562Sgshapiro	{
517990792Sgshapiro		if (LogLevel > 9)
518090792Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
518190792Sgshapiro				  "AUTH error: listmech=%d, num=%d",
518290792Sgshapiro				  result, num);
518390792Sgshapiro		num = 0;
518490792Sgshapiro	}
518590792Sgshapiro	if (num > 0)
518690792Sgshapiro	{
518764562Sgshapiro		if (LogLevel > 11)
518864562Sgshapiro			sm_syslog(LOG_INFO, NOQID,
518990792Sgshapiro				  "AUTH: available mech=%s, allowed mech=%s",
519064562Sgshapiro				  *mechlist, AuthMechanisms);
519190792Sgshapiro		*mechlist = intersect(AuthMechanisms, *mechlist, NULL);
519264562Sgshapiro	}
519364562Sgshapiro	else
519464562Sgshapiro	{
519590792Sgshapiro		*mechlist = NULL;	/* be paranoid... */
519690792Sgshapiro		if (result == SASL_OK && LogLevel > 9)
519764562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
519890792Sgshapiro				  "AUTH warning: no mechanisms");
519964562Sgshapiro	}
520064562Sgshapiro	return num;
520164562Sgshapiro}
520298121Sgshapiro
520398121Sgshapiro# if SASL >= 20000
520490792Sgshapiro/*
520564562Sgshapiro**  PROXY_POLICY -- define proxy policy for AUTH
520664562Sgshapiro**
520764562Sgshapiro**	Parameters:
520898121Sgshapiro**		conn -- unused.
520990792Sgshapiro**		context -- unused.
521098121Sgshapiro**		requested_user -- authorization identity.
521198121Sgshapiro**		rlen -- authorization identity length.
521290792Sgshapiro**		auth_identity -- authentication identity.
521398121Sgshapiro**		alen -- authentication identity length.
521498121Sgshapiro**		def_realm -- default user realm.
521598121Sgshapiro**		urlen -- user realm length.
521698121Sgshapiro**		propctx -- unused.
521798121Sgshapiro**
521898121Sgshapiro**	Returns:
521998121Sgshapiro**		ok?
522098121Sgshapiro**
522198121Sgshapiro**	Side Effects:
522298121Sgshapiro**		sets {auth_authen} macro.
522398121Sgshapiro*/
522498121Sgshapiro
522598121Sgshapiroint
522698121Sgshapiroproxy_policy(conn, context, requested_user, rlen, auth_identity, alen,
522798121Sgshapiro	     def_realm, urlen, propctx)
522898121Sgshapiro	sasl_conn_t *conn;
522998121Sgshapiro	void *context;
523098121Sgshapiro	const char *requested_user;
523198121Sgshapiro	unsigned rlen;
523298121Sgshapiro	const char *auth_identity;
523398121Sgshapiro	unsigned alen;
523498121Sgshapiro	const char *def_realm;
523598121Sgshapiro	unsigned urlen;
523698121Sgshapiro	struct propctx *propctx;
523798121Sgshapiro{
523898121Sgshapiro	if (auth_identity == NULL)
523998121Sgshapiro		return SASL_FAIL;
524098121Sgshapiro
524198121Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP,
5242223067Sgshapiro		  macid("{auth_authen}"),
5243223067Sgshapiro		  xtextify((char *) auth_identity, "=<>\")"));
524498121Sgshapiro
524598121Sgshapiro	return SASL_OK;
524698121Sgshapiro}
524798121Sgshapiro# else /* SASL >= 20000 */
524898121Sgshapiro
524998121Sgshapiro/*
525098121Sgshapiro**  PROXY_POLICY -- define proxy policy for AUTH
525198121Sgshapiro**
525298121Sgshapiro**	Parameters:
525398121Sgshapiro**		context -- unused.
525498121Sgshapiro**		auth_identity -- authentication identity.
525590792Sgshapiro**		requested_user -- authorization identity.
525690792Sgshapiro**		user -- allowed user (output).
525790792Sgshapiro**		errstr -- possible error string (output).
525864562Sgshapiro**
525964562Sgshapiro**	Returns:
526064562Sgshapiro**		ok?
526164562Sgshapiro*/
526264562Sgshapiro
526364562Sgshapiroint
526464562Sgshapiroproxy_policy(context, auth_identity, requested_user, user, errstr)
526564562Sgshapiro	void *context;
526664562Sgshapiro	const char *auth_identity;
526764562Sgshapiro	const char *requested_user;
526864562Sgshapiro	const char **user;
526964562Sgshapiro	const char **errstr;
527064562Sgshapiro{
527164562Sgshapiro	if (user == NULL || auth_identity == NULL)
527264562Sgshapiro		return SASL_FAIL;
527364562Sgshapiro	*user = newstr(auth_identity);
527464562Sgshapiro	return SASL_OK;
527564562Sgshapiro}
527698121Sgshapiro# endif /* SASL >= 20000 */
527790792Sgshapiro#endif /* SASL */
527864562Sgshapiro
527990792Sgshapiro#if STARTTLS
528090792Sgshapiro/*
528190792Sgshapiro**  INITSRVTLS -- initialize server side TLS
528264562Sgshapiro**
528364562Sgshapiro**	Parameters:
528490792Sgshapiro**		tls_ok -- should tls initialization be done?
528564562Sgshapiro**
528664562Sgshapiro**	Returns:
528790792Sgshapiro**		succeeded?
528864562Sgshapiro**
528964562Sgshapiro**	Side Effects:
529090792Sgshapiro**		sets tls_ok_srv which is a static variable in this module.
529190792Sgshapiro**		Do NOT remove assignments to it!
529264562Sgshapiro*/
529364562Sgshapiro
529466494Sgshapirobool
529590792Sgshapiroinitsrvtls(tls_ok)
529690792Sgshapiro	bool tls_ok;
529764562Sgshapiro{
529890792Sgshapiro	if (!tls_ok)
529990792Sgshapiro		return false;
530064562Sgshapiro
530190792Sgshapiro	/* do NOT remove assignment */
5302203004Sgshapiro	tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, Srv_SSL_Options, true,
5303203004Sgshapiro			     SrvCertFile, SrvKeyFile,
5304203004Sgshapiro			     CACertPath, CACertFile, DHParams);
530590792Sgshapiro	return tls_ok_srv;
530664562Sgshapiro}
530790792Sgshapiro#endif /* STARTTLS */
530864562Sgshapiro/*
530990792Sgshapiro**  SRVFEATURES -- get features for SMTP server
531064562Sgshapiro**
531164562Sgshapiro**	Parameters:
531290792Sgshapiro**		e -- envelope (should be session context).
531390792Sgshapiro**		clientname -- name of client.
531490792Sgshapiro**		features -- default features for this invocation.
531564562Sgshapiro**
531664562Sgshapiro**	Returns:
531790792Sgshapiro**		server features.
531864562Sgshapiro*/
531964562Sgshapiro
532090792Sgshapiro/* table with options: it uses just one character, how about strings? */
532190792Sgshapirostatic struct
532264562Sgshapiro{
532390792Sgshapiro	char		srvf_opt;
532490792Sgshapiro	unsigned int	srvf_flag;
532590792Sgshapiro} srv_feat_table[] =
532664562Sgshapiro{
532790792Sgshapiro	{ 'A',	SRV_OFFER_AUTH	},
5328132943Sgshapiro	{ 'B',	SRV_OFFER_VERB	},
5329132943Sgshapiro	{ 'C',	SRV_REQ_SEC	},
5330132943Sgshapiro	{ 'D',	SRV_OFFER_DSN	},
5331132943Sgshapiro	{ 'E',	SRV_OFFER_ETRN	},
5332363466Sgshapiro#if _FFR_EAI
5333363466Sgshapiro	{ 'I',	SRV_OFFER_EAI	},
5334363466Sgshapiro#endif
5335132943Sgshapiro	{ 'L',	SRV_REQ_AUTH	},
533690792Sgshapiro#if PIPELINING
533790792Sgshapiro# if _FFR_NO_PIPE
533890792Sgshapiro	{ 'N',	SRV_NO_PIPE	},
5339363466Sgshapiro# endif
534090792Sgshapiro	{ 'P',	SRV_OFFER_PIPE	},
534190792Sgshapiro#endif /* PIPELINING */
5342132943Sgshapiro	{ 'R',	SRV_VRFY_CLT	},	/* same as V; not documented */
534390792Sgshapiro	{ 'S',	SRV_OFFER_TLS	},
534490792Sgshapiro/*	{ 'T',	SRV_TMP_FAIL	},	*/
534590792Sgshapiro	{ 'V',	SRV_VRFY_CLT	},
5346132943Sgshapiro	{ 'X',	SRV_OFFER_EXPN	},
534790792Sgshapiro/*	{ 'Y',	SRV_OFFER_VRFY	},	*/
534890792Sgshapiro	{ '\0',	SRV_NONE	}
534990792Sgshapiro};
535064562Sgshapiro
535190792Sgshapirostatic unsigned int
535290792Sgshapirosrvfeatures(e, clientname, features)
535390792Sgshapiro	ENVELOPE *e;
535490792Sgshapiro	char *clientname;
535590792Sgshapiro	unsigned int features;
535677349Sgshapiro{
535790792Sgshapiro	int r, i, j;
535890792Sgshapiro	char **pvp, c, opt;
535990792Sgshapiro	char pvpbuf[PSBUFSIZE];
536077349Sgshapiro
536190792Sgshapiro	pvp = NULL;
536290792Sgshapiro	r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
536390792Sgshapiro		  sizeof(pvpbuf));
536490792Sgshapiro	if (r != EX_OK)
536590792Sgshapiro		return features;
536690792Sgshapiro	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
536790792Sgshapiro		return features;
536890792Sgshapiro	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
536990792Sgshapiro		return SRV_TMP_FAIL;
537077349Sgshapiro
537164562Sgshapiro	/*
537290792Sgshapiro	**  General rule (see sendmail.h, d_flags):
537390792Sgshapiro	**  lower case: required/offered, upper case: Not required/available
537490792Sgshapiro	**
537590792Sgshapiro	**  Since we can change some features per daemon, we have both
537690792Sgshapiro	**  cases here: turn on/off a feature.
537764562Sgshapiro	*/
537864562Sgshapiro
537990792Sgshapiro	for (i = 1; pvp[i] != NULL; i++)
538064562Sgshapiro	{
538190792Sgshapiro		c = pvp[i][0];
538290792Sgshapiro		j = 0;
538390792Sgshapiro		for (;;)
538464562Sgshapiro		{
538590792Sgshapiro			if ((opt = srv_feat_table[j].srvf_opt) == '\0')
538664562Sgshapiro			{
538790792Sgshapiro				if (LogLevel > 9)
538890792Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
538990792Sgshapiro						  "srvfeatures: unknown feature %s",
539090792Sgshapiro						  pvp[i]);
539190792Sgshapiro				break;
539264562Sgshapiro			}
539390792Sgshapiro			if (c == opt)
539464562Sgshapiro			{
539590792Sgshapiro				features &= ~(srv_feat_table[j].srvf_flag);
539690792Sgshapiro				break;
539764562Sgshapiro			}
539890792Sgshapiro			if (c == tolower(opt))
539964562Sgshapiro			{
540090792Sgshapiro				features |= srv_feat_table[j].srvf_flag;
540190792Sgshapiro				break;
540264562Sgshapiro			}
540390792Sgshapiro			++j;
540464562Sgshapiro		}
540564562Sgshapiro	}
540690792Sgshapiro	return features;
540764562Sgshapiro}
540864562Sgshapiro
540990792Sgshapiro/*
541038032Speter**  HELP -- implement the HELP command.
541138032Speter**
541238032Speter**	Parameters:
541338032Speter**		topic -- the topic we want help for.
541490792Sgshapiro**		e -- envelope.
541538032Speter**
541638032Speter**	Returns:
541738032Speter**		none.
541838032Speter**
541938032Speter**	Side Effects:
542038032Speter**		outputs the help file to message output.
542138032Speter*/
542264562Sgshapiro#define HELPVSTR	"#vers	"
542364562Sgshapiro#define HELPVERSION	2
542438032Speter
542538032Spetervoid
542664562Sgshapirohelp(topic, e)
542738032Speter	char *topic;
542864562Sgshapiro	ENVELOPE *e;
542938032Speter{
543090792Sgshapiro	register SM_FILE_T *hf;
543164562Sgshapiro	register char *p;
543238032Speter	int len;
543338032Speter	bool noinfo;
543490792Sgshapiro	bool first = true;
543564562Sgshapiro	long sff = SFF_OPENASROOT|SFF_REGONLY;
543638032Speter	char buf[MAXLINE];
543764562Sgshapiro	char inp[MAXLINE];
543864562Sgshapiro	static int foundvers = -1;
543938032Speter	extern char Version[];
544038032Speter
544138032Speter	if (DontLockReadFiles)
544238032Speter		sff |= SFF_NOLOCK;
544364562Sgshapiro	if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
544438032Speter		sff |= SFF_SAFEDIRPATH;
544538032Speter
544638032Speter	if (HelpFile == NULL ||
544738032Speter	    (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
544838032Speter	{
544938032Speter		/* no help */
545038032Speter		errno = 0;
545164562Sgshapiro		message("502 5.3.0 Sendmail %s -- HELP not implemented",
545264562Sgshapiro			Version);
545338032Speter		return;
545438032Speter	}
545538032Speter
545638032Speter	if (topic == NULL || *topic == '\0')
545738032Speter	{
545838032Speter		topic = "smtp";
545990792Sgshapiro		noinfo = false;
546038032Speter	}
546138032Speter	else
546238032Speter	{
546338032Speter		makelower(topic);
546490792Sgshapiro		noinfo = true;
546538032Speter	}
546638032Speter
546738032Speter	len = strlen(topic);
546838032Speter
5469249729Sgshapiro	while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
547038032Speter	{
547164562Sgshapiro		if (buf[0] == '#')
547264562Sgshapiro		{
547364562Sgshapiro			if (foundvers < 0 &&
547464562Sgshapiro			    strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
547564562Sgshapiro			{
547664562Sgshapiro				int h;
547764562Sgshapiro
547890792Sgshapiro				if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
547990792Sgshapiro						 &h) == 1)
548064562Sgshapiro					foundvers = h;
548164562Sgshapiro			}
548264562Sgshapiro			continue;
548364562Sgshapiro		}
548438032Speter		if (strncmp(buf, topic, len) == 0)
548538032Speter		{
548664562Sgshapiro			if (first)
548764562Sgshapiro			{
548890792Sgshapiro				first = false;
548938032Speter
549064562Sgshapiro				/* print version if no/old vers# in file */
549164562Sgshapiro				if (foundvers < 2 && !noinfo)
549264562Sgshapiro					message("214-2.0.0 This is Sendmail version %s", Version);
549364562Sgshapiro			}
549464562Sgshapiro			p = strpbrk(buf, " \t");
549538032Speter			if (p == NULL)
549664562Sgshapiro				p = buf + strlen(buf) - 1;
549738032Speter			else
549838032Speter				p++;
549990792Sgshapiro			fixcrlf(p, true);
550064562Sgshapiro			if (foundvers >= 2)
550164562Sgshapiro			{
5502168515Sgshapiro				char *lbp;
5503168515Sgshapiro				int lbs = sizeof(buf) - (p - buf);
5504168515Sgshapiro
5505168515Sgshapiro				lbp = translate_dollars(p, p, &lbs);
5506168515Sgshapiro				expand(lbp, inp, sizeof(inp), e);
5507168515Sgshapiro				if (p != lbp)
5508168515Sgshapiro					sm_free(lbp);
550964562Sgshapiro				p = inp;
551064562Sgshapiro			}
551164562Sgshapiro			message("214-2.0.0 %s", p);
551290792Sgshapiro			noinfo = false;
551338032Speter		}
551438032Speter	}
551538032Speter
551638032Speter	if (noinfo)
551764562Sgshapiro		message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
551838032Speter	else
551964562Sgshapiro		message("214 2.0.0 End of HELP info");
552064562Sgshapiro
552164562Sgshapiro	if (foundvers != 0 && foundvers < HELPVERSION)
552264562Sgshapiro	{
552364562Sgshapiro		if (LogLevel > 1)
552464562Sgshapiro			sm_syslog(LOG_WARNING, e->e_id,
552564562Sgshapiro				  "%s too old (require version %d)",
552664562Sgshapiro				  HelpFile, HELPVERSION);
552764562Sgshapiro
552864562Sgshapiro		/* avoid log next time */
552964562Sgshapiro		foundvers = 0;
553064562Sgshapiro	}
553164562Sgshapiro
553290792Sgshapiro	(void) sm_io_close(hf, SM_TIME_DEFAULT);
553338032Speter}
5534120256Sgshapiro
5535120256Sgshapiro#if SASL
5536120256Sgshapiro/*
5537120256Sgshapiro**  RESET_SASLCONN -- reset SASL connection data
5538120256Sgshapiro**
5539120256Sgshapiro**	Parameters:
5540120256Sgshapiro**		conn -- SASL connection context
5541120256Sgshapiro**		hostname -- host name
5542120256Sgshapiro**		various connection data
5543120256Sgshapiro**
5544120256Sgshapiro**	Returns:
5545120256Sgshapiro**		SASL result
5546120256Sgshapiro*/
5547120256Sgshapiro
5548120256Sgshapirostatic int
5549147078Sgshapiroreset_saslconn(sasl_conn_t **conn, char *hostname,
5550120256Sgshapiro# if SASL >= 20000
5551120256Sgshapiro	       char *remoteip, char *localip,
5552120256Sgshapiro	       char *auth_id, sasl_ssf_t * ext_ssf)
5553120256Sgshapiro# else /* SASL >= 20000 */
5554147078Sgshapiro	       struct sockaddr_in *saddr_r, struct sockaddr_in *saddr_l,
5555120256Sgshapiro	       sasl_external_properties_t * ext_ssf)
5556120256Sgshapiro# endif /* SASL >= 20000 */
5557120256Sgshapiro{
5558120256Sgshapiro	int result;
5559120256Sgshapiro
5560120256Sgshapiro	sasl_dispose(conn);
5561120256Sgshapiro# if SASL >= 20000
5562120256Sgshapiro	result = sasl_server_new("smtp", hostname, NULL, NULL, NULL,
5563120256Sgshapiro				 NULL, 0, conn);
5564120256Sgshapiro# elif SASL > 10505
5565120256Sgshapiro	/* use empty realm: only works in SASL > 1.5.5 */
5566120256Sgshapiro	result = sasl_server_new("smtp", hostname, "", NULL, 0, conn);
5567120256Sgshapiro# else /* SASL >= 20000 */
5568120256Sgshapiro	/* use no realm -> realm is set to hostname by SASL lib */
5569120256Sgshapiro	result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
5570120256Sgshapiro				 conn);
5571120256Sgshapiro# endif /* SASL >= 20000 */
5572120256Sgshapiro	if (result != SASL_OK)
5573120256Sgshapiro		return result;
5574120256Sgshapiro
5575120256Sgshapiro# if SASL >= 20000
5576120256Sgshapiro#  if NETINET || NETINET6
5577147078Sgshapiro	if (remoteip != NULL && *remoteip != '\0')
5578120256Sgshapiro		result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip);
5579120256Sgshapiro	if (result != SASL_OK)
5580120256Sgshapiro		return result;
5581120256Sgshapiro
5582147078Sgshapiro	if (localip != NULL && *localip != '\0')
5583120256Sgshapiro		result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip);
5584120256Sgshapiro	if (result != SASL_OK)
5585120256Sgshapiro		return result;
5586120256Sgshapiro#  endif /* NETINET || NETINET6 */
5587120256Sgshapiro
5588120256Sgshapiro	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
5589120256Sgshapiro	if (result != SASL_OK)
5590120256Sgshapiro		return result;
5591120256Sgshapiro
5592120256Sgshapiro	result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id);
5593120256Sgshapiro	if (result != SASL_OK)
5594120256Sgshapiro		return result;
5595120256Sgshapiro# else /* SASL >= 20000 */
5596120256Sgshapiro#  if NETINET
5597120256Sgshapiro	if (saddr_r != NULL)
5598120256Sgshapiro		result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r);
5599120256Sgshapiro	if (result != SASL_OK)
5600120256Sgshapiro		return result;
5601120256Sgshapiro
5602120256Sgshapiro	if (saddr_l != NULL)
5603120256Sgshapiro		result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l);
5604120256Sgshapiro	if (result != SASL_OK)
5605120256Sgshapiro		return result;
5606120256Sgshapiro#  endif /* NETINET */
5607120256Sgshapiro
5608120256Sgshapiro	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
5609120256Sgshapiro	if (result != SASL_OK)
5610120256Sgshapiro		return result;
5611120256Sgshapiro# endif /* SASL >= 20000 */
5612120256Sgshapiro	return SASL_OK;
5613120256Sgshapiro}
5614363466Sgshapiro
5615363466Sgshapiro/*
5616363466Sgshapiro**  GET_SASL_USER -- extract user part from SASL reply
5617363466Sgshapiro**
5618363466Sgshapiro**	Parameters:
5619363466Sgshapiro**		val -- sasl reply (may contain NUL)
5620363466Sgshapiro**		len -- length of val
5621363466Sgshapiro**		auth_type -- auth_type (can be NULL)
5622363466Sgshapiro**		user -- output buffer for extract user
5623363466Sgshapiro**		user_len -- length of output buffer (user)
5624363466Sgshapiro**
5625363466Sgshapiro**	Returns:
5626363466Sgshapiro**		none.
5627363466Sgshapiro**
5628363466Sgshapiro**	Note: val is supplied by the client and hence may contain "bad"
5629363466Sgshapiro**		(non-printable) characters, but the returned value (user)
5630363466Sgshapiro**		is only used for logging which converts those characters.
5631363466Sgshapiro*/
5632363466Sgshapiro
5633363466Sgshapirostatic void
5634363466Sgshapiroget_sasl_user(val, len, auth_type, user, user_len)
5635363466Sgshapiro	char *val;
5636363466Sgshapiro	unsigned int len;
5637363466Sgshapiro	const char *auth_type;
5638363466Sgshapiro	char *user;
5639363466Sgshapiro	size_t user_len;
5640363466Sgshapiro{
5641363466Sgshapiro	unsigned int u;
5642363466Sgshapiro
5643363466Sgshapiro	SM_ASSERT(val != NULL);
5644363466Sgshapiro	SM_ASSERT(user != NULL);
5645363466Sgshapiro	SM_ASSERT(user_len > 0);
5646363466Sgshapiro
5647363466Sgshapiro	*user = '\0';
5648363466Sgshapiro	if (NULL == auth_type || '\0' == *auth_type)
5649363466Sgshapiro		return;
5650363466Sgshapiro	if (0 == len)
5651363466Sgshapiro		return;
5652363466Sgshapiro
5653363466Sgshapiro# define DIGMD5U	"username=\""
5654363466Sgshapiro# define DIGMD5U_L	(sizeof(DIGMD5U) - 1)
5655363466Sgshapiro	if (sm_strcasecmp(auth_type, "digest-md5") == 0 &&
5656363466Sgshapiro	    strncmp(val, DIGMD5U, DIGMD5U_L) == 0)
5657363466Sgshapiro	{
5658363466Sgshapiro		char *s;
5659363466Sgshapiro
5660363466Sgshapiro		val += DIGMD5U_L;
5661363466Sgshapiro		if (len <= DIGMD5U_L)
5662363466Sgshapiro			return;
5663363466Sgshapiro		len -= DIGMD5U_L;
5664363466Sgshapiro
5665363466Sgshapiro		/* format? could there be a quoted '"'? */
5666363466Sgshapiro		for (s = val, u = 0; *s != '\0' && u < len; s++)
5667363466Sgshapiro		{
5668363466Sgshapiro			if ('"' == *s)
5669363466Sgshapiro			{
5670363466Sgshapiro				*s = '\0';
5671363466Sgshapiro				break;
5672363466Sgshapiro			}
5673363466Sgshapiro			if ('\\' == *s)
5674363466Sgshapiro			{
5675363466Sgshapiro				++s;
5676363466Sgshapiro				if ('\0' == *s)
5677363466Sgshapiro					break;
5678363466Sgshapiro			}
5679363466Sgshapiro		}
5680363466Sgshapiro	}
5681363466Sgshapiro	else if (sm_strcasecmp(auth_type, "cram-md5") == 0)
5682363466Sgshapiro	{
5683363466Sgshapiro		char *s;
5684363466Sgshapiro
5685363466Sgshapiro		for (s = val, u = 0; *s != '\0' && u < len; s++)
5686363466Sgshapiro		{
5687363466Sgshapiro			if (' ' == *s)
5688363466Sgshapiro			{
5689363466Sgshapiro				*s = '\0';
5690363466Sgshapiro				break;
5691363466Sgshapiro			}
5692363466Sgshapiro		}
5693363466Sgshapiro	}
5694363466Sgshapiro
5695363466Sgshapiro	else if (sm_strcasecmp(auth_type, "plain") == 0 ||
5696363466Sgshapiro		 sm_strcasecmp(auth_type, "login") == 0)
5697363466Sgshapiro	{
5698363466Sgshapiro		/*
5699363466Sgshapiro		**  RFC 4616: The PLAIN Simple Authentication and
5700363466Sgshapiro		**	Security Layer (SASL) Mechanism
5701363466Sgshapiro		**    message   = [authzid] UTF8NUL authcid UTF8NUL passwd
5702363466Sgshapiro		**  each part: 1*SAFE ; MUST accept up to 255 octets
5703363466Sgshapiro		**  UTF8NUL   = %x00 ; UTF-8 encoded NUL character
5704363466Sgshapiro		**
5705363466Sgshapiro		**  draft-murchison-sasl-login: it's just username by its own
5706363466Sgshapiro		*/
5707363466Sgshapiro
5708363466Sgshapiro		for (u = 0; u < len; u++)
5709363466Sgshapiro		{
5710363466Sgshapiro			if (val[u] == '\0')
5711363466Sgshapiro			{
5712363466Sgshapiro				val[u] = '/';
5713363466Sgshapiro				(void) sm_strlcpy(user,
5714363466Sgshapiro						val + ((0 == u) ? 1 : 0),
5715363466Sgshapiro						user_len);
5716363466Sgshapiro				return;
5717363466Sgshapiro			}
5718363466Sgshapiro		}
5719363466Sgshapiro	}
5720363466Sgshapiro	else
5721363466Sgshapiro	{
5722363466Sgshapiro		/*
5723363466Sgshapiro		**  Extracting the "user" from other mechanisms
5724363466Sgshapiro		**  is currently not supported.
5725363466Sgshapiro		*/
5726363466Sgshapiro
5727363466Sgshapiro		return;
5728363466Sgshapiro	}
5729363466Sgshapiro
5730363466Sgshapiro	/*
5731363466Sgshapiro	**  Does the input buffer has an NUL in it so it can be treated
5732363466Sgshapiro	**  as a C string?
5733363466Sgshapiro	*/
5734363466Sgshapiro
5735363466Sgshapiro	/* SM_ASSERT(len > 0); see above */
5736363466Sgshapiro	u = len - 1;
5737363466Sgshapiro	if (val[u] != '\0')
5738363466Sgshapiro	{
5739363466Sgshapiro		for (u = 0; u < len; u++)
5740363466Sgshapiro		{
5741363466Sgshapiro			if (val[u] == '\0')
5742363466Sgshapiro				break;
5743363466Sgshapiro		}
5744363466Sgshapiro	}
5745363466Sgshapiro	if (val[u] != '\0')
5746363466Sgshapiro		user_len = SM_MIN(len, user_len);
5747363466Sgshapiro
5748363466Sgshapiro	(void) sm_strlcpy(user, val, user_len);
5749363466Sgshapiro}
5750120256Sgshapiro#endif /* SASL */
5751