xref: /freebsd/contrib/sendmail/src/srvrsmtp.c (revision d39bd2c1388b520fcba9abed1932acacead60fba)
1c2aa98e2SPeter Wemm /*
2*d39bd2c1SGregory Neil Shapiro  * Copyright (c) 1998-2010, 2012-2014,2021-2024 Proofpoint, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro  *	All rights reserved.
4c2aa98e2SPeter Wemm  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5c2aa98e2SPeter Wemm  * Copyright (c) 1988, 1993
6c2aa98e2SPeter Wemm  *	The Regents of the University of California.  All rights reserved.
7c2aa98e2SPeter Wemm  *
8c2aa98e2SPeter Wemm  * By using this file, you agree to the terms and conditions set
9c2aa98e2SPeter Wemm  * forth in the LICENSE file which can be found at the top level of
10c2aa98e2SPeter Wemm  * the sendmail distribution.
11c2aa98e2SPeter Wemm  *
12c2aa98e2SPeter Wemm  */
13c2aa98e2SPeter Wemm 
1406f25ae9SGregory Neil Shapiro #include <sendmail.h>
1540266059SGregory Neil Shapiro #if MILTER
16e92d3f3fSGregory Neil Shapiro # include <libmilter/mfapi.h>
1740266059SGregory Neil Shapiro # include <libmilter/mfdef.h>
185b0945b5SGregory Neil Shapiro #endif
19c2aa98e2SPeter Wemm 
204313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.1016 2013-11-22 20:51:56 ca Exp $")
21e92d3f3fSGregory Neil Shapiro 
222fb4f839SGregory Neil Shapiro #include <sm/sendmail.h>
232fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
242fb4f839SGregory Neil Shapiro # include <sm/ixlen.h>
252fb4f839SGregory Neil Shapiro #endif
264e4196cbSGregory Neil Shapiro #include <sm/time.h>
27e92d3f3fSGregory Neil Shapiro #include <sm/fdset.h>
28c2aa98e2SPeter Wemm 
2942e5d165SGregory Neil Shapiro #if SASL || STARTTLS
305b0945b5SGregory Neil Shapiro # include <tls.h>
3106f25ae9SGregory Neil Shapiro # include "sfsasl.h"
325b0945b5SGregory Neil Shapiro #endif
3306f25ae9SGregory Neil Shapiro #if SASL
3406f25ae9SGregory Neil Shapiro # define ENC64LEN(l)	(((l) + 2) * 4 / 3 + 1)
3506f25ae9SGregory Neil Shapiro static int saslmechs __P((sasl_conn_t *, char **));
365b0945b5SGregory Neil Shapiro #endif
3706f25ae9SGregory Neil Shapiro #if STARTTLS
38ba00ec3dSGregory Neil Shapiro # include <openssl/err.h>
3906f25ae9SGregory Neil Shapiro 
4040266059SGregory Neil Shapiro static SSL_CTX	*srv_ctx = NULL;	/* TLS server context */
4140266059SGregory Neil Shapiro static SSL	*srv_ssl = NULL;	/* per connection context */
425b0945b5SGregory Neil Shapiro static tlsi_ctx_T tlsi_ctx;		/* TLS information context */
4340266059SGregory Neil Shapiro 
4440266059SGregory Neil Shapiro static bool	tls_ok_srv = false;
4540266059SGregory Neil Shapiro 
4640266059SGregory Neil Shapiro # define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
4740266059SGregory Neil Shapiro 				bitset(SRV_VRFY_CLT, features))
4806f25ae9SGregory Neil Shapiro #endif /* STARTTLS */
4906f25ae9SGregory Neil Shapiro 
50d0cef73dSGregory Neil Shapiro #if _FFR_DM_ONE
51d0cef73dSGregory Neil Shapiro static bool	NotFirstDelivery = false;
525b0945b5SGregory Neil Shapiro #endif
53d0cef73dSGregory Neil Shapiro 
5440266059SGregory Neil Shapiro /* server features */
55*d39bd2c1SGregory Neil Shapiro #define SRV_NONE	0x00000000	/* none... */
56*d39bd2c1SGregory Neil Shapiro #define SRV_OFFER_TLS	0x00000001	/* offer STARTTLS */
57*d39bd2c1SGregory Neil Shapiro #define SRV_VRFY_CLT	0x00000002	/* request a cert */
58*d39bd2c1SGregory Neil Shapiro #define SRV_OFFER_AUTH	0x00000004	/* offer AUTH */
59*d39bd2c1SGregory Neil Shapiro #define SRV_OFFER_ETRN	0x00000008	/* offer ETRN */
60*d39bd2c1SGregory Neil Shapiro #define SRV_OFFER_VRFY	0x00000010	/* offer VRFY (not yet used) */
61*d39bd2c1SGregory Neil Shapiro #define SRV_OFFER_EXPN	0x00000020	/* offer EXPN */
62*d39bd2c1SGregory Neil Shapiro #define SRV_OFFER_VERB	0x00000040	/* offer VERB */
63*d39bd2c1SGregory Neil Shapiro #define SRV_OFFER_DSN	0x00000080	/* offer DSN */
6440266059SGregory Neil Shapiro #if PIPELINING
65*d39bd2c1SGregory Neil Shapiro # define SRV_OFFER_PIPE	0x00000100	/* offer PIPELINING */
6640266059SGregory Neil Shapiro # if _FFR_NO_PIPE
67*d39bd2c1SGregory Neil Shapiro #  define SRV_NO_PIPE	0x00000200	/* disable PIPELINING, sleep if used */
685b0945b5SGregory Neil Shapiro # endif
6940266059SGregory Neil Shapiro #endif /* PIPELINING */
70*d39bd2c1SGregory Neil Shapiro #define SRV_REQ_AUTH	0x00000400	/* require AUTH */
71*d39bd2c1SGregory Neil Shapiro #define SRV_REQ_SEC	0x00000800	/* require security - equiv to AuthOptions=p */
72*d39bd2c1SGregory Neil Shapiro #define SRV_TMP_FAIL	0x00001000	/* ruleset caused a temporary failure */
732fb4f839SGregory Neil Shapiro #if USE_EAI
74*d39bd2c1SGregory Neil Shapiro # define SRV_OFFER_EAI	0x00002000	/* offer SMTPUTF8 */
755b0945b5SGregory Neil Shapiro #endif
76*d39bd2c1SGregory Neil Shapiro #define SRV_NO_HTTP_CMD	0x00004000	/* always reject HTTP commands */
77*d39bd2c1SGregory Neil Shapiro #define SRV_BAD_PIPELINE	0x00008000	/* reject bad pipelining (see comment below) */
78*d39bd2c1SGregory Neil Shapiro #define SRV_REQ_CRLF	0x00010000	/* require CRLF as EOL */
79*d39bd2c1SGregory Neil Shapiro #define SRV_BARE_LF_421	0x00020000	/* bare LF - drop connection */
80*d39bd2c1SGregory Neil Shapiro #define SRV_BARE_CR_421	0x00040000	/* bare CR - drop connection */
81*d39bd2c1SGregory Neil Shapiro #define SRV_BARE_LF_SP	0x00080000
82*d39bd2c1SGregory Neil Shapiro #define SRV_BARE_CR_SP	0x00100000
8340266059SGregory Neil Shapiro 
84*d39bd2c1SGregory Neil Shapiro static unsigned long	srvfeatures __P((ENVELOPE *, char *, unsigned long));
8540266059SGregory Neil Shapiro 
86e92d3f3fSGregory Neil Shapiro #define	STOP_ATTACK	((time_t) -1)
87e92d3f3fSGregory Neil Shapiro static time_t	checksmtpattack __P((volatile unsigned int *, unsigned int,
88e92d3f3fSGregory Neil Shapiro 				     bool, char *, ENVELOPE *));
8906f25ae9SGregory Neil Shapiro static void	printvrfyaddr __P((ADDRESS *, bool, bool));
9006f25ae9SGregory Neil Shapiro static char	*skipword __P((char *volatile, char *));
9140266059SGregory Neil Shapiro static void	setup_smtpd_io __P((void));
92*d39bd2c1SGregory Neil Shapiro static struct timeval	*channel_readable __P((SM_FILE_T *, int));
93a7ec597cSGregory Neil Shapiro 
94a7ec597cSGregory Neil Shapiro #if SASL
955b0945b5SGregory Neil Shapiro # ifndef MAX_AUTH_USER_LEN
965b0945b5SGregory Neil Shapiro #  define MAX_AUTH_USER_LEN 256
975b0945b5SGregory Neil Shapiro # endif
985b0945b5SGregory Neil Shapiro # ifndef MAX_AUTH_LOG_LEN
995b0945b5SGregory Neil Shapiro #  define MAX_AUTH_LOG_LEN 64
1005b0945b5SGregory Neil Shapiro # endif
1015b0945b5SGregory Neil Shapiro static void get_sasl_user __P((char *, unsigned int, const char *, char *out, size_t));
1025b0945b5SGregory Neil Shapiro # define RESET_AUTH_FAIL_LOG_USER	\
1035b0945b5SGregory Neil Shapiro 	do	\
1045b0945b5SGregory Neil Shapiro 	{	\
1055b0945b5SGregory Neil Shapiro 		(void) memset(auth_user, 0, sizeof(auth_user));	\
1065b0945b5SGregory Neil Shapiro 		(void) memset(auth_user_tmp, 0, sizeof(auth_user_tmp));	\
1075b0945b5SGregory Neil Shapiro 		auth_user_len = 0;	\
1085b0945b5SGregory Neil Shapiro 	} while (0)
1095b0945b5SGregory Neil Shapiro # define SET_AUTH_USER_TMP(s, len)	\
1105b0945b5SGregory Neil Shapiro 	do	\
1115b0945b5SGregory Neil Shapiro 	{	\
1125b0945b5SGregory Neil Shapiro 		auth_user_len = SM_MIN(len, MAX_AUTH_USER_LEN-1);	\
1135b0945b5SGregory Neil Shapiro 		(void) memcpy(auth_user_tmp, s, auth_user_len);	\
1145b0945b5SGregory Neil Shapiro 	} while (0)
1155b0945b5SGregory Neil Shapiro # define SET_AUTH_USER	\
1165b0945b5SGregory Neil Shapiro 	get_sasl_user(auth_user_tmp, auth_user_len, auth_type, auth_user, sizeof(auth_user))
1175b0945b5SGregory Neil Shapiro # define SET_AUTH_USER_CONDITIONALLY	\
1185b0945b5SGregory Neil Shapiro 		if ('\0' == auth_user[0])	\
1195b0945b5SGregory Neil Shapiro 			SET_AUTH_USER;
1205b0945b5SGregory Neil Shapiro # define LOG_AUTH_FAIL_USER ", user=", (int)MAX_AUTH_LOG_LEN, auth_user
121a7ec597cSGregory Neil Shapiro # if SASL >= 20000
122a7ec597cSGregory Neil Shapiro static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
123a7ec597cSGregory Neil Shapiro 				char *_remoteip, char *_localip,
124a7ec597cSGregory Neil Shapiro 				char *_auth_id, sasl_ssf_t *_ext_ssf));
125a7ec597cSGregory Neil Shapiro 
126a7ec597cSGregory Neil Shapiro # define RESET_SASLCONN	\
12713d88268SGregory Neil Shapiro 	do							\
128a7ec597cSGregory Neil Shapiro 	{							\
1295b0945b5SGregory Neil Shapiro 		RESET_AUTH_FAIL_LOG_USER;			\
13013d88268SGregory Neil Shapiro 		result = reset_saslconn(&conn, AuthRealm, remoteip, \
13113d88268SGregory Neil Shapiro 					localip, auth_id, &ext_ssf); \
13213d88268SGregory Neil Shapiro 		if (result != SASL_OK)				\
13313d88268SGregory Neil Shapiro 			sasl_ok = false;			\
13413d88268SGregory Neil Shapiro 	} while (0)
135a7ec597cSGregory Neil Shapiro 
136a7ec597cSGregory Neil Shapiro # else /* SASL >= 20000 */
137a7ec597cSGregory Neil Shapiro static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
138a7ec597cSGregory Neil Shapiro 				struct sockaddr_in *_saddr_r,
139a7ec597cSGregory Neil Shapiro 				struct sockaddr_in *_saddr_l,
140a7ec597cSGregory Neil Shapiro 				sasl_external_properties_t *_ext_ssf));
141a7ec597cSGregory Neil Shapiro # define RESET_SASLCONN	\
14213d88268SGregory Neil Shapiro 	do							\
143a7ec597cSGregory Neil Shapiro 	{							\
1445b0945b5SGregory Neil Shapiro 		RESET_AUTH_FAIL_LOG_USER;			\
14513d88268SGregory Neil Shapiro 		result = reset_saslconn(&conn, AuthRealm, &saddr_r, \
14613d88268SGregory Neil Shapiro 					&saddr_l, &ext_ssf);	\
14713d88268SGregory Neil Shapiro 		if (result != SASL_OK)				\
14813d88268SGregory Neil Shapiro 			sasl_ok = false;			\
14913d88268SGregory Neil Shapiro 	} while (0)
150a7ec597cSGregory Neil Shapiro 
151a7ec597cSGregory Neil Shapiro # endif /* SASL >= 20000 */
152a7ec597cSGregory Neil Shapiro #endif /* SASL */
153a7ec597cSGregory Neil Shapiro 
1545b0945b5SGregory Neil Shapiro #if !defined(RESET_AUTH_FAIL_LOG_USER)
1555b0945b5SGregory Neil Shapiro # define RESET_AUTH_FAIL_LOG_USER
1565b0945b5SGregory Neil Shapiro #endif
1575b0945b5SGregory Neil Shapiro 
15806f25ae9SGregory Neil Shapiro extern ENVELOPE	BlankEnvelope;
159c2aa98e2SPeter Wemm 
160e92d3f3fSGregory Neil Shapiro #define NBADRCPTS						\
161e92d3f3fSGregory Neil Shapiro 	do							\
162e92d3f3fSGregory Neil Shapiro 	{							\
163e92d3f3fSGregory Neil Shapiro 		char buf[16];					\
164d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(buf, sizeof(buf), "%d",	\
165e92d3f3fSGregory Neil Shapiro 			BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \
166e92d3f3fSGregory Neil Shapiro 				? n_badrcpts - 1 : n_badrcpts);	\
167e92d3f3fSGregory Neil Shapiro 		macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
168e92d3f3fSGregory Neil Shapiro 	} while (0)
169e92d3f3fSGregory Neil Shapiro 
1705b0945b5SGregory Neil Shapiro #define SKIP_SPACE(s)	while (SM_ISSPACE(*s))	\
17140266059SGregory Neil Shapiro 				(s)++
17240266059SGregory Neil Shapiro 
1732fb4f839SGregory Neil Shapiro #if USE_EAI
174c2aa98e2SPeter Wemm /*
175*d39bd2c1SGregory Neil Shapiro **  ADDR_IS_ASCII -- check whether a string (address) is ASCII
1765b0945b5SGregory Neil Shapiro **
1775b0945b5SGregory Neil Shapiro **	Parameters:
178*d39bd2c1SGregory Neil Shapiro **		str -- a string
1795b0945b5SGregory Neil Shapiro **
1805b0945b5SGregory Neil Shapiro **	Returns:
181*d39bd2c1SGregory Neil Shapiro **		TRUE iff str is non-NULL and points to only ASCII
1825b0945b5SGregory Neil Shapiro */
1835b0945b5SGregory Neil Shapiro 
1845b0945b5SGregory Neil Shapiro bool
addr_is_ascii(str)185*d39bd2c1SGregory Neil Shapiro addr_is_ascii(str)
186*d39bd2c1SGregory Neil Shapiro 	const char *str;
1875b0945b5SGregory Neil Shapiro {
188*d39bd2c1SGregory Neil Shapiro 	while (str != NULL && *str != '\0' && isascii((unsigned char)*str))
189*d39bd2c1SGregory Neil Shapiro 		str++;
190*d39bd2c1SGregory Neil Shapiro 	return (str != NULL && *str == '\0');
1915b0945b5SGregory Neil Shapiro }
1922fb4f839SGregory Neil Shapiro 
193*d39bd2c1SGregory Neil Shapiro /*
194*d39bd2c1SGregory Neil Shapiro **  STR_IS_PRINT -- check whether a string is printable ASCII
195*d39bd2c1SGregory Neil Shapiro **
196*d39bd2c1SGregory Neil Shapiro **	Parameters:
197*d39bd2c1SGregory Neil Shapiro **		str -- a string
198*d39bd2c1SGregory Neil Shapiro **
199*d39bd2c1SGregory Neil Shapiro **	Returns:
200*d39bd2c1SGregory Neil Shapiro **		TRUE iff str is non-NULL and points to only printable ASCII
201*d39bd2c1SGregory Neil Shapiro */
202*d39bd2c1SGregory Neil Shapiro 
203*d39bd2c1SGregory Neil Shapiro bool
str_is_print(str)204*d39bd2c1SGregory Neil Shapiro str_is_print(str)
205*d39bd2c1SGregory Neil Shapiro 	const char *str;
206*d39bd2c1SGregory Neil Shapiro {
207*d39bd2c1SGregory Neil Shapiro 	while (str != NULL && *str != '\0' && *str >= ' ' && (unsigned char)*str < 127)
208*d39bd2c1SGregory Neil Shapiro 		str++;
209*d39bd2c1SGregory Neil Shapiro 	return (str != NULL && *str == '\0');
210*d39bd2c1SGregory Neil Shapiro }
211*d39bd2c1SGregory Neil Shapiro 
212*d39bd2c1SGregory Neil Shapiro 
2132fb4f839SGregory Neil Shapiro # define CHECK_UTF8_ADDR(a, q)	\
2142fb4f839SGregory Neil Shapiro 	do	\
2152fb4f839SGregory Neil Shapiro 	{	\
2162fb4f839SGregory Neil Shapiro 		q = NULL;	\
2172fb4f839SGregory Neil Shapiro 		if (addr_is_ascii(a))	\
2182fb4f839SGregory Neil Shapiro 			break;	\
219*d39bd2c1SGregory Neil Shapiro 		if (!SMTP_UTF8)	\
2202fb4f839SGregory Neil Shapiro 			break;	\
2212fb4f839SGregory Neil Shapiro 		if (!e->e_smtputf8)	\
2222fb4f839SGregory Neil Shapiro 			q = "553 5.6.7 Address requires SMTPUTF8";	\
2232fb4f839SGregory Neil Shapiro 		else	\
2242fb4f839SGregory Neil Shapiro 		{	\
2252fb4f839SGregory Neil Shapiro 			char str[MAXNAME];	\
2262fb4f839SGregory Neil Shapiro 			dequote_internal_chars(a, str, sizeof(str));	\
227*d39bd2c1SGregory Neil Shapiro 			if (!utf8_valid(str, strlen(str)) && SMTP_UTF8 <= 1) \
2282fb4f839SGregory Neil Shapiro 				q = "553 5.6.7 Address not valid UTF8";	\
2292fb4f839SGregory Neil Shapiro 		}	\
2302fb4f839SGregory Neil Shapiro 	} while (0)
2312fb4f839SGregory Neil Shapiro #endif /* USE_EAI */
2325b0945b5SGregory Neil Shapiro 
2335b0945b5SGregory Neil Shapiro /*
2345b0945b5SGregory Neil Shapiro **  PARSE_ESMTP_ARGS -- parse ESMTP arguments (for MAIL, RCPT)
235d0cef73dSGregory Neil Shapiro **
236d0cef73dSGregory Neil Shapiro **	Parameters:
237d0cef73dSGregory Neil Shapiro **		e -- the envelope
238d0cef73dSGregory Neil Shapiro **		addr_st -- address (RCPT only)
239d0cef73dSGregory Neil Shapiro **		p -- read buffer
240d0cef73dSGregory Neil Shapiro **		delimptr -- current position in read buffer
241d0cef73dSGregory Neil Shapiro **		which -- MAIL/RCPT
242d0cef73dSGregory Neil Shapiro **		args -- arguments (output)
243d0cef73dSGregory Neil Shapiro **		esmtp_args -- function to process a single ESMTP argument
244d0cef73dSGregory Neil Shapiro **
245d0cef73dSGregory Neil Shapiro **	Returns:
246d0cef73dSGregory Neil Shapiro **		none
247d0cef73dSGregory Neil Shapiro */
248d0cef73dSGregory Neil Shapiro 
249d0cef73dSGregory Neil Shapiro void
parse_esmtp_args(e,addr_st,p,delimptr,which,args,esmtp_args)250d0cef73dSGregory Neil Shapiro parse_esmtp_args(e, addr_st, p, delimptr, which, args, esmtp_args)
251d0cef73dSGregory Neil Shapiro 	ENVELOPE *e;
252d0cef73dSGregory Neil Shapiro 	ADDRESS *addr_st;
253d0cef73dSGregory Neil Shapiro 	char *p;
254d0cef73dSGregory Neil Shapiro 	char *delimptr;
255d0cef73dSGregory Neil Shapiro 	char *which;
256d0cef73dSGregory Neil Shapiro 	char *args[];
257d0cef73dSGregory Neil Shapiro 	esmtp_args_F esmtp_args;
258d0cef73dSGregory Neil Shapiro {
259d0cef73dSGregory Neil Shapiro 	int argno;
260d0cef73dSGregory Neil Shapiro 
261d0cef73dSGregory Neil Shapiro 	argno = 0;
262d0cef73dSGregory Neil Shapiro 	if (args != NULL)
263d0cef73dSGregory Neil Shapiro 		args[argno++] = p;
264d0cef73dSGregory Neil Shapiro 	p = delimptr;
265d0cef73dSGregory Neil Shapiro 	while (p != NULL && *p != '\0')
266d0cef73dSGregory Neil Shapiro 	{
267d0cef73dSGregory Neil Shapiro 		char *kp;
268d0cef73dSGregory Neil Shapiro 		char *vp = NULL;
269d0cef73dSGregory Neil Shapiro 		char *equal = NULL;
270d0cef73dSGregory Neil Shapiro 
271d0cef73dSGregory Neil Shapiro 		/* locate the beginning of the keyword */
272d0cef73dSGregory Neil Shapiro 		SKIP_SPACE(p);
273d0cef73dSGregory Neil Shapiro 		if (*p == '\0')
274d0cef73dSGregory Neil Shapiro 			break;
275d0cef73dSGregory Neil Shapiro 		kp = p;
276d0cef73dSGregory Neil Shapiro 
277d0cef73dSGregory Neil Shapiro 		/* skip to the value portion */
278d0cef73dSGregory Neil Shapiro 		while ((isascii(*p) && isalnum(*p)) || *p == '-')
279d0cef73dSGregory Neil Shapiro 			p++;
280d0cef73dSGregory Neil Shapiro 		if (*p == '=')
281d0cef73dSGregory Neil Shapiro 		{
282d0cef73dSGregory Neil Shapiro 			equal = p;
283d0cef73dSGregory Neil Shapiro 			*p++ = '\0';
284d0cef73dSGregory Neil Shapiro 			vp = p;
285d0cef73dSGregory Neil Shapiro 
286d0cef73dSGregory Neil Shapiro 			/* skip to the end of the value */
287d0cef73dSGregory Neil Shapiro 			while (*p != '\0' && *p != ' ' &&
288d0cef73dSGregory Neil Shapiro 			       !(isascii(*p) && iscntrl(*p)) &&
289d0cef73dSGregory Neil Shapiro 			       *p != '=')
290d0cef73dSGregory Neil Shapiro 				p++;
291d0cef73dSGregory Neil Shapiro 		}
292d0cef73dSGregory Neil Shapiro 
293d0cef73dSGregory Neil Shapiro 		if (*p != '\0')
294d0cef73dSGregory Neil Shapiro 			*p++ = '\0';
295d0cef73dSGregory Neil Shapiro 
296d0cef73dSGregory Neil Shapiro 		if (tTd(19, 1))
297d0cef73dSGregory Neil Shapiro 			sm_dprintf("%s: got arg %s=\"%s\"\n", which, kp,
298d0cef73dSGregory Neil Shapiro 				vp == NULL ? "<null>" : vp);
299d0cef73dSGregory Neil Shapiro 
300d0cef73dSGregory Neil Shapiro 		esmtp_args(addr_st, kp, vp, e);
301d0cef73dSGregory Neil Shapiro 		if (equal != NULL)
302d0cef73dSGregory Neil Shapiro 			*equal = '=';
303d0cef73dSGregory Neil Shapiro 		if (args != NULL)
304d0cef73dSGregory Neil Shapiro 			args[argno] = kp;
305d0cef73dSGregory Neil Shapiro 		argno++;
306d0cef73dSGregory Neil Shapiro 		if (argno >= MAXSMTPARGS - 1)
307d0cef73dSGregory Neil Shapiro 			usrerr("501 5.5.4 Too many parameters");
308d0cef73dSGregory Neil Shapiro 		if (Errors > 0)
309d0cef73dSGregory Neil Shapiro 			break;
310d0cef73dSGregory Neil Shapiro 	}
311d0cef73dSGregory Neil Shapiro 	if (args != NULL)
312d0cef73dSGregory Neil Shapiro 		args[argno] = NULL;
313d0cef73dSGregory Neil Shapiro }
314d0cef73dSGregory Neil Shapiro 
315da7d7b9cSGregory Neil Shapiro #if _FFR_ADD_BCC
316da7d7b9cSGregory Neil Shapiro 
317da7d7b9cSGregory Neil Shapiro /*
318da7d7b9cSGregory Neil Shapiro **  ADDRCPT -- Add a rcpt to sendq list
319da7d7b9cSGregory Neil Shapiro **
320da7d7b9cSGregory Neil Shapiro **	Parameters:
3212fb4f839SGregory Neil Shapiro **		rcpt -- rcpt [i]
322da7d7b9cSGregory Neil Shapiro **		sendq -- a pointer to the head of a queue to put
323da7d7b9cSGregory Neil Shapiro **			these people into.
324da7d7b9cSGregory Neil Shapiro **		e -- the envelope in which to add these recipients.
325da7d7b9cSGregory Neil Shapiro **
326da7d7b9cSGregory Neil Shapiro **	Returns:
327da7d7b9cSGregory Neil Shapiro **		The number of addresses added to the list.
328da7d7b9cSGregory Neil Shapiro */
329da7d7b9cSGregory Neil Shapiro 
330da7d7b9cSGregory Neil Shapiro static int
addrcpt(rcpt,sendq,e)331da7d7b9cSGregory Neil Shapiro addrcpt(rcpt, sendq, e)
332da7d7b9cSGregory Neil Shapiro 	char *rcpt;
333da7d7b9cSGregory Neil Shapiro 	ADDRESS **sendq;
334da7d7b9cSGregory Neil Shapiro 	ENVELOPE *e;
335da7d7b9cSGregory Neil Shapiro {
336da7d7b9cSGregory Neil Shapiro 	int r;
337da7d7b9cSGregory Neil Shapiro 	char *oldto;
338da7d7b9cSGregory Neil Shapiro 	ADDRESS *a;
339da7d7b9cSGregory Neil Shapiro 
340da7d7b9cSGregory Neil Shapiro 	SM_REQUIRE(rcpt != NULL);
341da7d7b9cSGregory Neil Shapiro 	SM_REQUIRE(sendq != NULL);
342da7d7b9cSGregory Neil Shapiro 	SM_REQUIRE(e != NULL);
343da7d7b9cSGregory Neil Shapiro 	oldto = e->e_to;
344da7d7b9cSGregory Neil Shapiro 	if (tTd(25, 1))
345da7d7b9cSGregory Neil Shapiro 		sm_dprintf("addrcpt: rcpt=%s\n", rcpt);
346da7d7b9cSGregory Neil Shapiro 	r = Errors;
347da7d7b9cSGregory Neil Shapiro 	a = NULL;
348da7d7b9cSGregory Neil Shapiro 	SM_TRY
349da7d7b9cSGregory Neil Shapiro 	{
350da7d7b9cSGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), "e b");
3512fb4f839SGregory Neil Shapiro /* XXX rcpt must be [i] */
352da7d7b9cSGregory Neil Shapiro 		a = parseaddr(rcpt, NULLADDR, RF_COPYALL, ' ', NULL, e, true);
353da7d7b9cSGregory Neil Shapiro 		if (a == NULL)
354da7d7b9cSGregory Neil Shapiro 			return 0;
355da7d7b9cSGregory Neil Shapiro 
356da7d7b9cSGregory Neil Shapiro 		a->q_flags &= ~Q_PINGFLAGS;
357da7d7b9cSGregory Neil Shapiro 		a->q_flags |= QINTBCC;
358da7d7b9cSGregory Neil Shapiro 		a->q_owner = "<>";
359da7d7b9cSGregory Neil Shapiro 
360da7d7b9cSGregory Neil Shapiro 		/* disable alias expansion? */
361da7d7b9cSGregory Neil Shapiro 		a = recipient(a, sendq, 0, e);
362da7d7b9cSGregory Neil Shapiro 	}
363da7d7b9cSGregory Neil Shapiro 	SM_FINALLY
364da7d7b9cSGregory Neil Shapiro 	{
365da7d7b9cSGregory Neil Shapiro 		e->e_to = oldto;
366da7d7b9cSGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{addr_type}"), NULL);
367da7d7b9cSGregory Neil Shapiro 	}
368da7d7b9cSGregory Neil Shapiro 	SM_END_TRY
369da7d7b9cSGregory Neil Shapiro 	if (tTd(25, 1))
370da7d7b9cSGregory Neil Shapiro 		sm_dprintf("addrcpt: rcpt=%s, flags=%#lx\n", rcpt,
371da7d7b9cSGregory Neil Shapiro 			a != NULL ? a->q_flags : 0);
372da7d7b9cSGregory Neil Shapiro 	Errors = r;
373da7d7b9cSGregory Neil Shapiro 	return 1;
374da7d7b9cSGregory Neil Shapiro }
375da7d7b9cSGregory Neil Shapiro 
376da7d7b9cSGregory Neil Shapiro /*
377da7d7b9cSGregory Neil Shapiro **  ADDBCC -- Maybe create a copy of an e-mail
378da7d7b9cSGregory Neil Shapiro **
379da7d7b9cSGregory Neil Shapiro **	Parameters:
380da7d7b9cSGregory Neil Shapiro **		a -- current RCPT
381da7d7b9cSGregory Neil Shapiro **		e -- the envelope.
382da7d7b9cSGregory Neil Shapiro **
383da7d7b9cSGregory Neil Shapiro **	Returns:
384da7d7b9cSGregory Neil Shapiro **		nothing
385da7d7b9cSGregory Neil Shapiro **
386da7d7b9cSGregory Neil Shapiro **	Side Effects:
387da7d7b9cSGregory Neil Shapiro **		rscheck() can trigger an "exception"
388da7d7b9cSGregory Neil Shapiro */
389da7d7b9cSGregory Neil Shapiro 
390da7d7b9cSGregory Neil Shapiro static void
addbcc(a,e)391da7d7b9cSGregory Neil Shapiro addbcc(a, e)
392da7d7b9cSGregory Neil Shapiro 	ADDRESS *a;
393da7d7b9cSGregory Neil Shapiro 	ENVELOPE *e;
394da7d7b9cSGregory Neil Shapiro {
395da7d7b9cSGregory Neil Shapiro 	int nobcc;
396da7d7b9cSGregory Neil Shapiro 	char *newrcpt, empty[1];
397da7d7b9cSGregory Neil Shapiro 
398da7d7b9cSGregory Neil Shapiro 	if (!AddBcc)
399da7d7b9cSGregory Neil Shapiro 		return;
400da7d7b9cSGregory Neil Shapiro 
401da7d7b9cSGregory Neil Shapiro 	nobcc = false;
402da7d7b9cSGregory Neil Shapiro 	empty[0] = '\0';
403da7d7b9cSGregory Neil Shapiro 	newrcpt = empty;
404da7d7b9cSGregory Neil Shapiro 
405da7d7b9cSGregory Neil Shapiro 	nobcc = rscheck("bcc", a->q_paddr, NULL, e, RSF_ADDR, 12, NULL, NOQID,
406da7d7b9cSGregory Neil Shapiro 			NULL, &newrcpt);
407da7d7b9cSGregory Neil Shapiro 	if (tTd(25, 1))
408da7d7b9cSGregory Neil Shapiro 		sm_dprintf("addbcc: nobcc=%d, Errors=%d, newrcpt=<%s>\n", nobcc, Errors, newrcpt);
409da7d7b9cSGregory Neil Shapiro 	if (nobcc != EX_OK || Errors > 0 || *newrcpt == '\0')
410da7d7b9cSGregory Neil Shapiro 		return;
411da7d7b9cSGregory Neil Shapiro 
412da7d7b9cSGregory Neil Shapiro 	(void) addrcpt(newrcpt, &e->e_sendqueue, e);
413da7d7b9cSGregory Neil Shapiro 	return;
414da7d7b9cSGregory Neil Shapiro }
415da7d7b9cSGregory Neil Shapiro #else /* _FFR_ADD_BCC */
416da7d7b9cSGregory Neil Shapiro # define addbcc(a, e)
417da7d7b9cSGregory Neil Shapiro #endif /* _FFR_ADD_BCC */
418da7d7b9cSGregory Neil Shapiro 
419da7d7b9cSGregory Neil Shapiro #if _FFR_RCPTFLAGS
420da7d7b9cSGregory Neil Shapiro /*
421da7d7b9cSGregory Neil Shapiro **  RCPTMODS -- Perform rcpt modifications if requested
422da7d7b9cSGregory Neil Shapiro **
423da7d7b9cSGregory Neil Shapiro **	Parameters:
424da7d7b9cSGregory Neil Shapiro **		rcpt -- current RCPT
425da7d7b9cSGregory Neil Shapiro **		e -- the envelope.
426da7d7b9cSGregory Neil Shapiro **
427da7d7b9cSGregory Neil Shapiro **	Returns:
428da7d7b9cSGregory Neil Shapiro **		nothing.
429da7d7b9cSGregory Neil Shapiro */
430da7d7b9cSGregory Neil Shapiro 
431da7d7b9cSGregory Neil Shapiro void
rcptmods(rcpt,e)432da7d7b9cSGregory Neil Shapiro rcptmods(rcpt, e)
433da7d7b9cSGregory Neil Shapiro 	ADDRESS *rcpt;
434da7d7b9cSGregory Neil Shapiro 	ENVELOPE *e;
435da7d7b9cSGregory Neil Shapiro {
436da7d7b9cSGregory Neil Shapiro 	char *fl;
437da7d7b9cSGregory Neil Shapiro 
438da7d7b9cSGregory Neil Shapiro 	SM_REQUIRE(rcpt != NULL);
439da7d7b9cSGregory Neil Shapiro 	SM_REQUIRE(e != NULL);
440da7d7b9cSGregory Neil Shapiro 
441da7d7b9cSGregory Neil Shapiro 	fl = macvalue(macid("{rcpt_flags}"), e);
4422fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(fl))
443da7d7b9cSGregory Neil Shapiro 		return;
444da7d7b9cSGregory Neil Shapiro 	if (tTd(25, 1))
445da7d7b9cSGregory Neil Shapiro 		sm_dprintf("rcptmods: rcpt=%s, flags=%s\n", rcpt->q_paddr, fl);
446da7d7b9cSGregory Neil Shapiro 
447da7d7b9cSGregory Neil Shapiro 	/* parse flags */
448da7d7b9cSGregory Neil Shapiro 	for ( ; *fl != '\0'; ++fl)
449da7d7b9cSGregory Neil Shapiro 	{
450da7d7b9cSGregory Neil Shapiro 		switch (*fl)
451da7d7b9cSGregory Neil Shapiro 		{
452da7d7b9cSGregory Neil Shapiro 		  case 'n':
453da7d7b9cSGregory Neil Shapiro 			rcpt->q_flags &= ~Q_PINGFLAGS;
454da7d7b9cSGregory Neil Shapiro 			rcpt->q_flags |= QINTBCC;
455da7d7b9cSGregory Neil Shapiro 			rcpt->q_owner = "<>";
456da7d7b9cSGregory Neil Shapiro 			break;
457da7d7b9cSGregory Neil Shapiro 
458da7d7b9cSGregory Neil Shapiro 		  case 'N':
459da7d7b9cSGregory Neil Shapiro 			rcpt->q_flags &= ~Q_PINGFLAGS;
460da7d7b9cSGregory Neil Shapiro 			rcpt->q_owner = "<>";
461da7d7b9cSGregory Neil Shapiro 			break;
462da7d7b9cSGregory Neil Shapiro 
463da7d7b9cSGregory Neil Shapiro 		  case QDYNMAILFLG:
464da7d7b9cSGregory Neil Shapiro 			rcpt->q_flags |= QDYNMAILER;
465da7d7b9cSGregory Neil Shapiro 			newmodmailer(rcpt, *fl);
466da7d7b9cSGregory Neil Shapiro 			break;
467da7d7b9cSGregory Neil Shapiro 
468da7d7b9cSGregory Neil Shapiro 		  default:
469da7d7b9cSGregory Neil Shapiro 			sm_syslog(LOG_INFO, e->e_id,
470da7d7b9cSGregory Neil Shapiro 				  "rcpt=%s, rcpt_flags=%s, status=unknown",
471da7d7b9cSGregory Neil Shapiro 				  rcpt->q_paddr, fl);
472da7d7b9cSGregory Neil Shapiro 			break;
473da7d7b9cSGregory Neil Shapiro 		}
474da7d7b9cSGregory Neil Shapiro 	}
475da7d7b9cSGregory Neil Shapiro 
476da7d7b9cSGregory Neil Shapiro 	/* reset macro to avoid confusion later on */
477da7d7b9cSGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, macid("{rcpt_flags}"), NULL);
478da7d7b9cSGregory Neil Shapiro 
479da7d7b9cSGregory Neil Shapiro }
480da7d7b9cSGregory Neil Shapiro #else /* _FFR_RCPTFLAGS */
481da7d7b9cSGregory Neil Shapiro # define rcptmods(a, e)
482da7d7b9cSGregory Neil Shapiro #endif /* _FFR_RCPTFLAGS */
483da7d7b9cSGregory Neil Shapiro 
484*d39bd2c1SGregory Neil Shapiro #if _FFR_8BITENVADDR
485*d39bd2c1SGregory Neil Shapiro 
486*d39bd2c1SGregory Neil Shapiro /*
487*d39bd2c1SGregory Neil Shapiro **  SEP_ARGS -- separate address and argument string for MAIL/RCPT command
488*d39bd2c1SGregory Neil Shapiro **
489*d39bd2c1SGregory Neil Shapiro **	Parameters:
490*d39bd2c1SGregory Neil Shapiro **		args -- arguments (converted to and from internal format)
491*d39bd2c1SGregory Neil Shapiro **		orig -- string after command (original data)
492*d39bd2c1SGregory Neil Shapiro **		id -- envelope id (for logging only)
493*d39bd2c1SGregory Neil Shapiro **		addr -- for logging only: address (original data)
494*d39bd2c1SGregory Neil Shapiro **
495*d39bd2c1SGregory Neil Shapiro **	Returns:
496*d39bd2c1SGregory Neil Shapiro **		nothing
497*d39bd2c1SGregory Neil Shapiro */
498*d39bd2c1SGregory Neil Shapiro 
499*d39bd2c1SGregory Neil Shapiro static void sep_args __P((char *, char *, const char *, const char *));
500*d39bd2c1SGregory Neil Shapiro 
501*d39bd2c1SGregory Neil Shapiro static void
sep_args(args,orig,id,addr)502*d39bd2c1SGregory Neil Shapiro sep_args(args, orig, id, addr)
503*d39bd2c1SGregory Neil Shapiro 	char *args;
504*d39bd2c1SGregory Neil Shapiro 	char *orig;
505*d39bd2c1SGregory Neil Shapiro 	const char *id;
506*d39bd2c1SGregory Neil Shapiro 	const char *addr;
507*d39bd2c1SGregory Neil Shapiro {
508*d39bd2c1SGregory Neil Shapiro 	int lr, lo;
509*d39bd2c1SGregory Neil Shapiro 	char *q;
510*d39bd2c1SGregory Neil Shapiro 
511*d39bd2c1SGregory Neil Shapiro 	lr = strlen(args);
512*d39bd2c1SGregory Neil Shapiro 	lo = strlen(orig);
513*d39bd2c1SGregory Neil Shapiro 	if (lr >= lo)
514*d39bd2c1SGregory Neil Shapiro 	{
515*d39bd2c1SGregory Neil Shapiro 		sm_syslog(LOG_ERR, id,
516*d39bd2c1SGregory Neil Shapiro 			"ERROR=ARGS_NOT_FOUND, address='%s', rest='%s', orig='%s', strlen(rest)=%d, strlen(orig)=%d",
517*d39bd2c1SGregory Neil Shapiro 			addr, args, orig, lr, lo);
518*d39bd2c1SGregory Neil Shapiro 		return;
519*d39bd2c1SGregory Neil Shapiro 	}
520*d39bd2c1SGregory Neil Shapiro 
521*d39bd2c1SGregory Neil Shapiro 	q = orig + (lo - lr);
522*d39bd2c1SGregory Neil Shapiro 	if (!(q > orig && *--q == ' '))
523*d39bd2c1SGregory Neil Shapiro 	{
524*d39bd2c1SGregory Neil Shapiro 		sm_syslog(LOG_INFO, id,
525*d39bd2c1SGregory Neil Shapiro 			"ERROR=ARGS_DO_NOT_MATCH, address='%s', rest='%s', orig='%s', q='%s', strlen(rest)=%d, strlen(orig)=%d, cmp=%d",
526*d39bd2c1SGregory Neil Shapiro 			addr, args, orig, q, lr, lo, strcmp(args, q));
527*d39bd2c1SGregory Neil Shapiro 		return;
528*d39bd2c1SGregory Neil Shapiro 	}
529*d39bd2c1SGregory Neil Shapiro 
530*d39bd2c1SGregory Neil Shapiro 	for (; q > orig && *q == ' '; q--)
531*d39bd2c1SGregory Neil Shapiro 		*q = '\0';
532*d39bd2c1SGregory Neil Shapiro }
533*d39bd2c1SGregory Neil Shapiro #endif /* _FFR_8BITENVADDR */
534*d39bd2c1SGregory Neil Shapiro 
535*d39bd2c1SGregory Neil Shapiro /*
536*d39bd2c1SGregory Neil Shapiro **  CHANNEL_READBLE -- determine if data is readable from the SMTP channel
537*d39bd2c1SGregory Neil Shapiro **
538*d39bd2c1SGregory Neil Shapiro **	Parameters:
539*d39bd2c1SGregory Neil Shapiro **		channel -- connect channel for reading
540*d39bd2c1SGregory Neil Shapiro **		timeout -- how long to pause for data in milliseconds
541*d39bd2c1SGregory Neil Shapiro **
542*d39bd2c1SGregory Neil Shapiro **	Returns:
543*d39bd2c1SGregory Neil Shapiro **		timeval contained how long we waited if data detected,
544*d39bd2c1SGregory Neil Shapiro **			NULL otherwise
545*d39bd2c1SGregory Neil Shapiro */
546*d39bd2c1SGregory Neil Shapiro 
547*d39bd2c1SGregory Neil Shapiro static struct timeval *
channel_readable(channel,timeout)548*d39bd2c1SGregory Neil Shapiro channel_readable(channel, timeout)
549*d39bd2c1SGregory Neil Shapiro 	SM_FILE_T *channel;
550*d39bd2c1SGregory Neil Shapiro 	int timeout;
551*d39bd2c1SGregory Neil Shapiro {
552*d39bd2c1SGregory Neil Shapiro 	struct timeval bp, ep; /* {begin,end} pause */
553*d39bd2c1SGregory Neil Shapiro 	static struct timeval tp; /* total pause */
554*d39bd2c1SGregory Neil Shapiro 	int eoftest;
555*d39bd2c1SGregory Neil Shapiro 
556*d39bd2c1SGregory Neil Shapiro 	/* check if data is on the channel during the pause */
557*d39bd2c1SGregory Neil Shapiro 	gettimeofday(&bp, NULL);
558*d39bd2c1SGregory Neil Shapiro 	if ((eoftest = sm_io_getc(channel, timeout)) != SM_IO_EOF)
559*d39bd2c1SGregory Neil Shapiro 	{
560*d39bd2c1SGregory Neil Shapiro 		gettimeofday(&ep, NULL);
561*d39bd2c1SGregory Neil Shapiro 		sm_io_ungetc(channel, SM_TIME_DEFAULT, eoftest);
562*d39bd2c1SGregory Neil Shapiro 		timersub(&ep, &bp, &tp);
563*d39bd2c1SGregory Neil Shapiro 		return &tp;
564*d39bd2c1SGregory Neil Shapiro 	}
565*d39bd2c1SGregory Neil Shapiro 	return NULL;
566*d39bd2c1SGregory Neil Shapiro }
567*d39bd2c1SGregory Neil Shapiro 
568d0cef73dSGregory Neil Shapiro /*
569c2aa98e2SPeter Wemm **  SMTP -- run the SMTP protocol.
570c2aa98e2SPeter Wemm **
571c2aa98e2SPeter Wemm **	Parameters:
572c2aa98e2SPeter Wemm **		nullserver -- if non-NULL, rejection message for
57340266059SGregory Neil Shapiro **			(almost) all SMTP commands.
57440266059SGregory Neil Shapiro **		d_flags -- daemon flags
575c2aa98e2SPeter Wemm **		e -- the envelope.
576c2aa98e2SPeter Wemm **
577c2aa98e2SPeter Wemm **	Returns:
578c2aa98e2SPeter Wemm **		never.
579c2aa98e2SPeter Wemm **
580c2aa98e2SPeter Wemm **	Side Effects:
58140266059SGregory Neil Shapiro **		Reads commands from the input channel and processes them.
58240266059SGregory Neil Shapiro */
58340266059SGregory Neil Shapiro 
58440266059SGregory Neil Shapiro /*
58540266059SGregory Neil Shapiro **  Notice: The smtp server doesn't have a session context like the client
58640266059SGregory Neil Shapiro **	side has (mci). Therefore some data (session oriented) is allocated
58740266059SGregory Neil Shapiro **	or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
58840266059SGregory Neil Shapiro **	This should be fixed in a successor version.
589c2aa98e2SPeter Wemm */
590c2aa98e2SPeter Wemm 
591c2aa98e2SPeter Wemm struct cmd
592c2aa98e2SPeter Wemm {
59306f25ae9SGregory Neil Shapiro 	char	*cmd_name;	/* command name */
59406f25ae9SGregory Neil Shapiro 	int	cmd_code;	/* internal code, see below */
595c2aa98e2SPeter Wemm };
596c2aa98e2SPeter Wemm 
59706f25ae9SGregory Neil Shapiro /* values for cmd_code */
598c2aa98e2SPeter Wemm #define CMDERROR	0	/* bad command */
599c2aa98e2SPeter Wemm #define CMDMAIL	1	/* mail -- designate sender */
600c2aa98e2SPeter Wemm #define CMDRCPT	2	/* rcpt -- designate recipient */
601c2aa98e2SPeter Wemm #define CMDDATA	3	/* data -- send message text */
602c2aa98e2SPeter Wemm #define CMDRSET	4	/* rset -- reset state */
603c2aa98e2SPeter Wemm #define CMDVRFY	5	/* vrfy -- verify address */
604c2aa98e2SPeter Wemm #define CMDEXPN	6	/* expn -- expand address */
605c2aa98e2SPeter Wemm #define CMDNOOP	7	/* noop -- do nothing */
606c2aa98e2SPeter Wemm #define CMDQUIT	8	/* quit -- close connection and die */
607c2aa98e2SPeter Wemm #define CMDHELO	9	/* helo -- be polite */
608c2aa98e2SPeter Wemm #define CMDHELP	10	/* help -- give usage info */
609c2aa98e2SPeter Wemm #define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
610c2aa98e2SPeter Wemm #define CMDETRN	12	/* etrn -- flush queue */
61106f25ae9SGregory Neil Shapiro #if SASL
61206f25ae9SGregory Neil Shapiro # define CMDAUTH	13	/* auth -- SASL authenticate */
6135b0945b5SGregory Neil Shapiro #endif
61406f25ae9SGregory Neil Shapiro #if STARTTLS
61506f25ae9SGregory Neil Shapiro # define CMDSTLS	14	/* STARTTLS -- start TLS session */
6165b0945b5SGregory Neil Shapiro #endif
617c2aa98e2SPeter Wemm /* non-standard commands */
618c2aa98e2SPeter Wemm #define CMDVERB	17	/* verb -- go into verbose mode */
61906f25ae9SGregory Neil Shapiro /* unimplemented commands from RFC 821 */
62006f25ae9SGregory Neil Shapiro #define CMDUNIMPL	19	/* unimplemented rfc821 commands */
621c2aa98e2SPeter Wemm /* use this to catch and log "door handle" attempts on your system */
622c2aa98e2SPeter Wemm #define CMDLOGBOGUS	23	/* bogus command that should be logged */
623c2aa98e2SPeter Wemm /* debugging-only commands, only enabled if SMTPDEBUG is defined */
624c2aa98e2SPeter Wemm #define CMDDBGQSHOW	24	/* showq -- show send queue */
625c2aa98e2SPeter Wemm #define CMDDBGDEBUG	25	/* debug -- set debug mode */
626c2aa98e2SPeter Wemm 
62742e5d165SGregory Neil Shapiro /*
62840266059SGregory Neil Shapiro **  Note: If you change this list, remember to update 'helpfile'
62942e5d165SGregory Neil Shapiro */
63042e5d165SGregory Neil Shapiro 
631c2aa98e2SPeter Wemm static struct cmd	CmdTab[] =
632c2aa98e2SPeter Wemm {
633c2aa98e2SPeter Wemm 	{ "mail",	CMDMAIL		},
634c2aa98e2SPeter Wemm 	{ "rcpt",	CMDRCPT		},
635c2aa98e2SPeter Wemm 	{ "data",	CMDDATA		},
636c2aa98e2SPeter Wemm 	{ "rset",	CMDRSET		},
637c2aa98e2SPeter Wemm 	{ "vrfy",	CMDVRFY		},
638c2aa98e2SPeter Wemm 	{ "expn",	CMDEXPN		},
639c2aa98e2SPeter Wemm 	{ "help",	CMDHELP		},
640c2aa98e2SPeter Wemm 	{ "noop",	CMDNOOP		},
641c2aa98e2SPeter Wemm 	{ "quit",	CMDQUIT		},
642c2aa98e2SPeter Wemm 	{ "helo",	CMDHELO		},
643c2aa98e2SPeter Wemm 	{ "ehlo",	CMDEHLO		},
644c2aa98e2SPeter Wemm 	{ "etrn",	CMDETRN		},
645c2aa98e2SPeter Wemm 	{ "verb",	CMDVERB		},
64606f25ae9SGregory Neil Shapiro 	{ "send",	CMDUNIMPL	},
64706f25ae9SGregory Neil Shapiro 	{ "saml",	CMDUNIMPL	},
64806f25ae9SGregory Neil Shapiro 	{ "soml",	CMDUNIMPL	},
64906f25ae9SGregory Neil Shapiro 	{ "turn",	CMDUNIMPL	},
65006f25ae9SGregory Neil Shapiro #if SASL
65106f25ae9SGregory Neil Shapiro 	{ "auth",	CMDAUTH,	},
6525b0945b5SGregory Neil Shapiro #endif
65306f25ae9SGregory Neil Shapiro #if STARTTLS
65406f25ae9SGregory Neil Shapiro 	{ "starttls",	CMDSTLS,	},
6555b0945b5SGregory Neil Shapiro #endif
656c2aa98e2SPeter Wemm     /* remaining commands are here only to trap and log attempts to use them */
657c2aa98e2SPeter Wemm 	{ "showq",	CMDDBGQSHOW	},
658c2aa98e2SPeter Wemm 	{ "debug",	CMDDBGDEBUG	},
659c2aa98e2SPeter Wemm 	{ "wiz",	CMDLOGBOGUS	},
660c2aa98e2SPeter Wemm 
661c2aa98e2SPeter Wemm 	{ NULL,		CMDERROR	}
662c2aa98e2SPeter Wemm };
663c2aa98e2SPeter Wemm 
66406f25ae9SGregory Neil Shapiro static char	*CurSmtpClient;		/* who's at the other end of channel */
665c2aa98e2SPeter Wemm 
66640266059SGregory Neil Shapiro #ifndef MAXBADCOMMANDS
667c2aa98e2SPeter Wemm # define MAXBADCOMMANDS 25	/* maximum number of bad commands */
6685b0945b5SGregory Neil Shapiro #endif
66940266059SGregory Neil Shapiro #ifndef MAXHELOCOMMANDS
670c2aa98e2SPeter Wemm # define MAXHELOCOMMANDS 3	/* max HELO/EHLO commands before slowdown */
6715b0945b5SGregory Neil Shapiro #endif
67240266059SGregory Neil Shapiro #ifndef MAXVRFYCOMMANDS
673c2aa98e2SPeter Wemm # define MAXVRFYCOMMANDS 6	/* max VRFY/EXPN commands before slowdown */
6745b0945b5SGregory Neil Shapiro #endif
67540266059SGregory Neil Shapiro #ifndef MAXETRNCOMMANDS
676c2aa98e2SPeter Wemm # define MAXETRNCOMMANDS 8	/* max ETRN commands before slowdown */
6775b0945b5SGregory Neil Shapiro #endif
67840266059SGregory Neil Shapiro #ifndef MAXTIMEOUT
67906f25ae9SGregory Neil Shapiro # define MAXTIMEOUT (4 * 60)	/* max timeout for bad commands */
6805b0945b5SGregory Neil Shapiro #endif
68106f25ae9SGregory Neil Shapiro 
682e92d3f3fSGregory Neil Shapiro /*
683e92d3f3fSGregory Neil Shapiro **  Maximum shift value to compute timeout for bad commands.
684e92d3f3fSGregory Neil Shapiro **  This introduces an upper limit of 2^MAXSHIFT for the timeout.
685e92d3f3fSGregory Neil Shapiro */
686e92d3f3fSGregory Neil Shapiro 
687e92d3f3fSGregory Neil Shapiro #ifndef MAXSHIFT
688e92d3f3fSGregory Neil Shapiro # define MAXSHIFT 8
6895b0945b5SGregory Neil Shapiro #endif
690e92d3f3fSGregory Neil Shapiro #if MAXSHIFT > 31
691*d39bd2c1SGregory Neil Shapiro # error "MAXSHIFT > 31 is invalid"
6925b0945b5SGregory Neil Shapiro #endif
693e92d3f3fSGregory Neil Shapiro 
694e92d3f3fSGregory Neil Shapiro 
695e92d3f3fSGregory Neil Shapiro #if MAXBADCOMMANDS > 0
696e92d3f3fSGregory Neil Shapiro # define STOP_IF_ATTACK(r)	do		\
697e92d3f3fSGregory Neil Shapiro 	{					\
698e92d3f3fSGregory Neil Shapiro 		if ((r) == STOP_ATTACK)		\
699e92d3f3fSGregory Neil Shapiro 			goto stopattack;	\
700e92d3f3fSGregory Neil Shapiro 	} while (0)
701e92d3f3fSGregory Neil Shapiro 
702e92d3f3fSGregory Neil Shapiro #else /* MAXBADCOMMANDS > 0 */
703e92d3f3fSGregory Neil Shapiro # define STOP_IF_ATTACK(r)	r
704e92d3f3fSGregory Neil Shapiro #endif /* MAXBADCOMMANDS > 0 */
705e92d3f3fSGregory Neil Shapiro 
706e92d3f3fSGregory Neil Shapiro 
70740266059SGregory Neil Shapiro #if SM_HEAP_CHECK
70840266059SGregory Neil Shapiro static SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
70940266059SGregory Neil Shapiro 	"@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
7105b0945b5SGregory Neil Shapiro #endif
71140266059SGregory Neil Shapiro 
71240266059SGregory Neil Shapiro typedef struct
71340266059SGregory Neil Shapiro {
71440266059SGregory Neil Shapiro 	bool		sm_gotmail;	/* mail command received */
71540266059SGregory Neil Shapiro 	unsigned int	sm_nrcpts;	/* number of successful RCPT commands */
71640266059SGregory Neil Shapiro 	bool		sm_discard;
71740266059SGregory Neil Shapiro #if MILTER
71840266059SGregory Neil Shapiro 	bool		sm_milterize;
71940266059SGregory Neil Shapiro 	bool		sm_milterlist;	/* any filters in the list? */
720ffb83623SGregory Neil Shapiro 	milters_T	sm_milters;
721ffb83623SGregory Neil Shapiro 
722ffb83623SGregory Neil Shapiro 	/* e_nrcpts from envelope before recipient() call */
723ffb83623SGregory Neil Shapiro 	unsigned int	sm_e_nrcpts_orig;
72440266059SGregory Neil Shapiro #endif /* MILTER */
72540266059SGregory Neil Shapiro 	char		*sm_quarmsg;	/* carry quarantining across messages */
72640266059SGregory Neil Shapiro } SMTP_T;
72740266059SGregory Neil Shapiro 
728*d39bd2c1SGregory Neil Shapiro static bool	smtp_data __P((SMTP_T *, ENVELOPE *, bool));
72940266059SGregory Neil Shapiro 
730e92d3f3fSGregory Neil Shapiro #define MSG_TEMPFAIL "451 4.3.2 Please try again later"
73140266059SGregory Neil Shapiro 
73240266059SGregory Neil Shapiro #if MILTER
73340266059SGregory Neil Shapiro # define MILTER_ABORT(e)	milter_abort((e))
73413bd1963SGregory Neil Shapiro 
73540266059SGregory Neil Shapiro # define MILTER_REPLY(str)						\
73640266059SGregory Neil Shapiro 	{								\
73740266059SGregory Neil Shapiro 		int savelogusrerrs = LogUsrErrs;			\
73840266059SGregory Neil Shapiro 									\
739d0cef73dSGregory Neil Shapiro 		milter_cmd_fail = true;					\
74040266059SGregory Neil Shapiro 		switch (state)						\
74140266059SGregory Neil Shapiro 		{							\
7424e4196cbSGregory Neil Shapiro 		  case SMFIR_SHUTDOWN:					\
7434e4196cbSGregory Neil Shapiro 			if (MilterLogLevel > 3)				\
7444e4196cbSGregory Neil Shapiro 			{						\
7454e4196cbSGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,		\
7464e4196cbSGregory Neil Shapiro 					  "Milter: %s=%s, reject=421, errormode=4",	\
7474e4196cbSGregory Neil Shapiro 					  str, addr);			\
7484e4196cbSGregory Neil Shapiro 				LogUsrErrs = false;			\
7494e4196cbSGregory Neil Shapiro 			}						\
7504e4196cbSGregory Neil Shapiro 			{						\
7514e4196cbSGregory Neil Shapiro 				bool tsave = QuickAbort;		\
7524e4196cbSGregory Neil Shapiro 									\
7534e4196cbSGregory Neil Shapiro 				QuickAbort = false;			\
7544e4196cbSGregory Neil Shapiro 				usrerr("421 4.3.0 closing connection");	\
7554e4196cbSGregory Neil Shapiro 				QuickAbort = tsave;			\
7564e4196cbSGregory Neil Shapiro 				e->e_sendqueue = NULL;			\
7574e4196cbSGregory Neil Shapiro 				goto doquit;				\
7584e4196cbSGregory Neil Shapiro 			}						\
7594e4196cbSGregory Neil Shapiro 			break;						\
76040266059SGregory Neil Shapiro 		  case SMFIR_REPLYCODE:					\
76140266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)				\
76240266059SGregory Neil Shapiro 			{						\
76340266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,		\
76440266059SGregory Neil Shapiro 					  "Milter: %s=%s, reject=%s",	\
76540266059SGregory Neil Shapiro 					  str, addr, response);		\
76640266059SGregory Neil Shapiro 				LogUsrErrs = false;			\
76740266059SGregory Neil Shapiro 			}						\
7684e4196cbSGregory Neil Shapiro 			if (strncmp(response, "421 ", 4) == 0		\
7694e4196cbSGregory Neil Shapiro 			    || strncmp(response, "421-", 4) == 0)	\
770e92d3f3fSGregory Neil Shapiro 			{						\
771e92d3f3fSGregory Neil Shapiro 				bool tsave = QuickAbort;		\
772e92d3f3fSGregory Neil Shapiro 									\
773e92d3f3fSGregory Neil Shapiro 				QuickAbort = false;			\
77440266059SGregory Neil Shapiro 				usrerr(response);			\
775e92d3f3fSGregory Neil Shapiro 				QuickAbort = tsave;			\
776e92d3f3fSGregory Neil Shapiro 				e->e_sendqueue = NULL;			\
777e92d3f3fSGregory Neil Shapiro 				goto doquit;				\
778e92d3f3fSGregory Neil Shapiro 			}						\
779e92d3f3fSGregory Neil Shapiro 			else						\
780e92d3f3fSGregory Neil Shapiro 				usrerr(response);			\
78140266059SGregory Neil Shapiro 			break;						\
78240266059SGregory Neil Shapiro 									\
78340266059SGregory Neil Shapiro 		  case SMFIR_REJECT:					\
78440266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)				\
78540266059SGregory Neil Shapiro 			{						\
78640266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,		\
78740266059SGregory Neil Shapiro 					  "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
78840266059SGregory Neil Shapiro 					  str, addr);			\
78940266059SGregory Neil Shapiro 				LogUsrErrs = false;			\
79040266059SGregory Neil Shapiro 			}						\
79140266059SGregory Neil Shapiro 			usrerr("550 5.7.1 Command rejected");		\
79240266059SGregory Neil Shapiro 			break;						\
79340266059SGregory Neil Shapiro 									\
79440266059SGregory Neil Shapiro 		  case SMFIR_DISCARD:					\
79540266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)				\
79640266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,		\
79740266059SGregory Neil Shapiro 					  "Milter: %s=%s, discard",	\
79840266059SGregory Neil Shapiro 					  str, addr);			\
79940266059SGregory Neil Shapiro 			e->e_flags |= EF_DISCARD;			\
800d0cef73dSGregory Neil Shapiro 			milter_cmd_fail = false;			\
80140266059SGregory Neil Shapiro 			break;						\
80240266059SGregory Neil Shapiro 									\
80340266059SGregory Neil Shapiro 		  case SMFIR_TEMPFAIL:					\
80440266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)				\
80540266059SGregory Neil Shapiro 			{						\
80640266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,		\
80740266059SGregory Neil Shapiro 					  "Milter: %s=%s, reject=%s",	\
80840266059SGregory Neil Shapiro 					  str, addr, MSG_TEMPFAIL);	\
80940266059SGregory Neil Shapiro 				LogUsrErrs = false;			\
81040266059SGregory Neil Shapiro 			}						\
81140266059SGregory Neil Shapiro 			usrerr(MSG_TEMPFAIL);				\
81240266059SGregory Neil Shapiro 			break;						\
813d0cef73dSGregory Neil Shapiro 		  default:						\
814d0cef73dSGregory Neil Shapiro 			milter_cmd_fail = false;			\
815d0cef73dSGregory Neil Shapiro 			break;						\
81640266059SGregory Neil Shapiro 		}							\
81740266059SGregory Neil Shapiro 		LogUsrErrs = savelogusrerrs;				\
81840266059SGregory Neil Shapiro 		if (response != NULL)					\
81940266059SGregory Neil Shapiro 			sm_free(response); /* XXX */			\
82040266059SGregory Neil Shapiro 	}
82140266059SGregory Neil Shapiro 
82240266059SGregory Neil Shapiro #else /* MILTER */
82340266059SGregory Neil Shapiro # define MILTER_ABORT(e)
82440266059SGregory Neil Shapiro #endif /* MILTER */
82540266059SGregory Neil Shapiro 
82640266059SGregory Neil Shapiro /* clear all SMTP state (for HELO/EHLO/RSET) */
82740266059SGregory Neil Shapiro #define CLEAR_STATE(cmd)					\
828e92d3f3fSGregory Neil Shapiro do								\
82940266059SGregory Neil Shapiro {								\
83040266059SGregory Neil Shapiro 	/* abort milter filters */				\
83140266059SGregory Neil Shapiro 	MILTER_ABORT(e);					\
83240266059SGregory Neil Shapiro 								\
83340266059SGregory Neil Shapiro 	if (smtp.sm_nrcpts > 0)					\
83440266059SGregory Neil Shapiro 	{							\
83540266059SGregory Neil Shapiro 		logundelrcpts(e, cmd, 10, false);		\
83640266059SGregory Neil Shapiro 		smtp.sm_nrcpts = 0;				\
83740266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM,			\
83840266059SGregory Neil Shapiro 			  macid("{nrcpts}"), "0");		\
83940266059SGregory Neil Shapiro 	}							\
84040266059SGregory Neil Shapiro 								\
84140266059SGregory Neil Shapiro 	e->e_sendqueue = NULL;					\
84240266059SGregory Neil Shapiro 	e->e_flags |= EF_CLRQUEUE;				\
84340266059SGregory Neil Shapiro 								\
8449bd497b8SGregory Neil Shapiro 	if (tTd(92, 2))						\
8459bd497b8SGregory Neil Shapiro 		sm_dprintf("CLEAR_STATE: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",\
8469bd497b8SGregory Neil Shapiro 			e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel);\
84740266059SGregory Neil Shapiro 	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))	\
84840266059SGregory Neil Shapiro 		logsender(e, NULL);				\
84940266059SGregory Neil Shapiro 	e->e_flags &= ~EF_LOGSENDER;				\
85040266059SGregory Neil Shapiro 								\
85140266059SGregory Neil Shapiro 	/* clean up a bit */					\
85240266059SGregory Neil Shapiro 	smtp.sm_gotmail = false;				\
85340266059SGregory Neil Shapiro 	SuprErrs = true;					\
8549bd497b8SGregory Neil Shapiro 	(void) dropenvelope(e, true, false);			\
85540266059SGregory Neil Shapiro 	sm_rpool_free(e->e_rpool);				\
85640266059SGregory Neil Shapiro 	e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL));	\
85740266059SGregory Neil Shapiro 	CurEnv = e;						\
858d0cef73dSGregory Neil Shapiro 	e->e_features = features;				\
859e92d3f3fSGregory Neil Shapiro 								\
860e92d3f3fSGregory Neil Shapiro 	/* put back discard bit */				\
861e92d3f3fSGregory Neil Shapiro 	if (smtp.sm_discard)					\
862e92d3f3fSGregory Neil Shapiro 		e->e_flags |= EF_DISCARD;			\
863e92d3f3fSGregory Neil Shapiro 								\
864e92d3f3fSGregory Neil Shapiro 	/* restore connection quarantining */			\
865e92d3f3fSGregory Neil Shapiro 	if (smtp.sm_quarmsg == NULL)				\
866e92d3f3fSGregory Neil Shapiro 	{							\
867e92d3f3fSGregory Neil Shapiro 		e->e_quarmsg = NULL;				\
868e92d3f3fSGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM,			\
869e92d3f3fSGregory Neil Shapiro 			macid("{quarantine}"), "");		\
870e92d3f3fSGregory Neil Shapiro 	}							\
871e92d3f3fSGregory Neil Shapiro 	else							\
872e92d3f3fSGregory Neil Shapiro 	{							\
873e92d3f3fSGregory Neil Shapiro 		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,	\
874e92d3f3fSGregory Neil Shapiro 						smtp.sm_quarmsg);	\
875e92d3f3fSGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"),	\
876e92d3f3fSGregory Neil Shapiro 			  e->e_quarmsg);			\
877e92d3f3fSGregory Neil Shapiro 	}							\
878e92d3f3fSGregory Neil Shapiro } while (0)
87940266059SGregory Neil Shapiro 
88040266059SGregory Neil Shapiro /* sleep to flatten out connection load */
88140266059SGregory Neil Shapiro #define MIN_DELAY_LOG	15	/* wait before logging this again */
88240266059SGregory Neil Shapiro 
88340266059SGregory Neil Shapiro /* is it worth setting the process title for 1s? */
88440266059SGregory Neil Shapiro #define DELAY_CONN(cmd)						\
88540266059SGregory Neil Shapiro 	if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA)	\
88640266059SGregory Neil Shapiro 	{							\
88740266059SGregory Neil Shapiro 		time_t dnow;					\
88840266059SGregory Neil Shapiro 								\
88940266059SGregory Neil Shapiro 		sm_setproctitle(true, e,			\
89040266059SGregory Neil Shapiro 				"%s: %s: delaying %s: load average: %d", \
89140266059SGregory Neil Shapiro 				qid_printname(e), CurSmtpClient,	\
89240266059SGregory Neil Shapiro 				cmd, DelayLA);	\
89340266059SGregory Neil Shapiro 		if (LogLevel > 8 && (dnow = curtime()) > log_delay)	\
89440266059SGregory Neil Shapiro 		{						\
89540266059SGregory Neil Shapiro 			sm_syslog(LOG_INFO, e->e_id,		\
89640266059SGregory Neil Shapiro 				  "delaying=%s, load average=%d >= %d",	\
89740266059SGregory Neil Shapiro 				  cmd, CurrentLA, DelayLA);		\
89840266059SGregory Neil Shapiro 			log_delay = dnow + MIN_DELAY_LOG;	\
89940266059SGregory Neil Shapiro 		}						\
90040266059SGregory Neil Shapiro 		(void) sleep(1);				\
90140266059SGregory Neil Shapiro 		sm_setproctitle(true, e, "%s %s: %.80s",	\
90240266059SGregory Neil Shapiro 				qid_printname(e), CurSmtpClient, inp);	\
90340266059SGregory Neil Shapiro 	}
90440266059SGregory Neil Shapiro 
905da7d7b9cSGregory Neil Shapiro /*
906da7d7b9cSGregory Neil Shapiro **  Determine the correct protocol keyword to use in the
907da7d7b9cSGregory Neil Shapiro **  Received: header, following RFC 3848.
908da7d7b9cSGregory Neil Shapiro */
909da7d7b9cSGregory Neil Shapiro 
910da7d7b9cSGregory Neil Shapiro #if !STARTTLS
911da7d7b9cSGregory Neil Shapiro # define tls_active	false
912da7d7b9cSGregory Neil Shapiro #endif
913da7d7b9cSGregory Neil Shapiro #if SASL
914da7d7b9cSGregory Neil Shapiro # define auth_active	(authenticating == SASL_IS_AUTH)
915da7d7b9cSGregory Neil Shapiro #else
916da7d7b9cSGregory Neil Shapiro # define auth_active	false
917da7d7b9cSGregory Neil Shapiro #endif
9182fb4f839SGregory Neil Shapiro #if USE_EAI
9195b0945b5SGregory Neil Shapiro #define GET_PROTOCOL()					\
9205b0945b5SGregory Neil Shapiro 	(e->e_smtputf8					\
9215b0945b5SGregory Neil Shapiro 	    ? (auth_active				\
9225b0945b5SGregory Neil Shapiro 		? (tls_active ? "UTF8SMTPSA" : "UTF8SMTPA") \
9235b0945b5SGregory Neil Shapiro 		: (tls_active ? "UTF8SMTPS"  : "UTF8SMTP")) \
9245b0945b5SGregory Neil Shapiro 	    : (auth_active				\
9255b0945b5SGregory Neil Shapiro 		? (tls_active ? "ESMTPSA" : "ESMTPA")	\
9265b0945b5SGregory Neil Shapiro 		: (tls_active ? "ESMTPS"  : "ESMTP")))
9272fb4f839SGregory Neil Shapiro #else /* USE_EAI */
928da7d7b9cSGregory Neil Shapiro #define GET_PROTOCOL()					\
929da7d7b9cSGregory Neil Shapiro 	(auth_active					\
930da7d7b9cSGregory Neil Shapiro 	    ? (tls_active ? "ESMTPSA" : "ESMTPA")	\
931da7d7b9cSGregory Neil Shapiro 	    : (tls_active ? "ESMTPS"  : "ESMTP"))
9322fb4f839SGregory Neil Shapiro #endif /* USE_EAI */
933da7d7b9cSGregory Neil Shapiro 
9342fb4f839SGregory Neil Shapiro #if _FFR_NOREFLECT
9352fb4f839SGregory Neil Shapiro # define SHOWCMDINREPLY(inp) (bitset(PRIV_NOREFLECTION, PrivacyFlags) ? \
9362fb4f839SGregory Neil Shapiro 		"(suppressed)" : inp)
9372fb4f839SGregory Neil Shapiro # define SHOWSHRTCMDINREPLY(inp) (bitset(PRIV_NOREFLECTION, PrivacyFlags) ? \
9382fb4f839SGregory Neil Shapiro 		"(suppressed)" : shortenstring(inp, MAXSHORTSTR))
9392fb4f839SGregory Neil Shapiro #else
9402fb4f839SGregory Neil Shapiro # define SHOWCMDINREPLY(inp) inp
9412fb4f839SGregory Neil Shapiro # define SHOWSHRTCMDINREPLY(inp) shortenstring(inp, MAXSHORTSTR)
9422fb4f839SGregory Neil Shapiro #endif
9432fb4f839SGregory Neil Shapiro 
944c2aa98e2SPeter Wemm void
smtp(nullserver,d_flags,e)94506f25ae9SGregory Neil Shapiro smtp(nullserver, d_flags, e)
94606f25ae9SGregory Neil Shapiro 	char *volatile nullserver;
94706f25ae9SGregory Neil Shapiro 	BITMAP256 d_flags;
948c2aa98e2SPeter Wemm 	register ENVELOPE *volatile e;
949c2aa98e2SPeter Wemm {
950c2aa98e2SPeter Wemm 	register char *volatile p;
95106f25ae9SGregory Neil Shapiro 	register struct cmd *volatile c = NULL;
952c2aa98e2SPeter Wemm 	char *cmd;
953c2aa98e2SPeter Wemm 	auto ADDRESS *vrfyqueue;
954c2aa98e2SPeter Wemm 	ADDRESS *a;
955c2aa98e2SPeter Wemm 	volatile bool gothello;		/* helo command received */
956c2aa98e2SPeter Wemm 	bool vrfy;			/* set if this is a vrfy command */
957c2aa98e2SPeter Wemm 	char *volatile protocol;	/* sending protocol */
958c2aa98e2SPeter Wemm 	char *volatile sendinghost;	/* sending hostname */
959c2aa98e2SPeter Wemm 	char *volatile peerhostname;	/* name of SMTP peer or "localhost" */
960c2aa98e2SPeter Wemm 	auto char *delimptr;
961c2aa98e2SPeter Wemm 	char *id;
96240266059SGregory Neil Shapiro 	volatile unsigned int n_badcmds = 0;	/* count of bad commands */
96340266059SGregory Neil Shapiro 	volatile unsigned int n_badrcpts = 0;	/* number of rejected RCPT */
96440266059SGregory Neil Shapiro 	volatile unsigned int n_verifies = 0;	/* count of VRFY/EXPN */
96540266059SGregory Neil Shapiro 	volatile unsigned int n_etrn = 0;	/* count of ETRN */
96640266059SGregory Neil Shapiro 	volatile unsigned int n_noop = 0;	/* count of NOOP/VERB/etc */
96740266059SGregory Neil Shapiro 	volatile unsigned int n_helo = 0;	/* count of HELO/EHLO */
968c2aa98e2SPeter Wemm 	bool ok;
96940266059SGregory Neil Shapiro 	volatile bool first;
97040266059SGregory Neil Shapiro 	volatile bool tempfail = false;
97106f25ae9SGregory Neil Shapiro 	volatile time_t wt;		/* timeout after too many commands */
97206f25ae9SGregory Neil Shapiro 	volatile time_t previous;	/* time after checksmtpattack() */
97340266059SGregory Neil Shapiro 	volatile bool lognullconnection = true;
974c2aa98e2SPeter Wemm 	register char *q;
97540266059SGregory Neil Shapiro 	SMTP_T smtp;
97606f25ae9SGregory Neil Shapiro 	char *addr;
97706f25ae9SGregory Neil Shapiro 	char *greetcode = "220";
978da7d7b9cSGregory Neil Shapiro 	const char *greetmsg = "not accepting messages";
97940266059SGregory Neil Shapiro 	char *hostname;			/* my hostname ($j) */
980c2aa98e2SPeter Wemm 	QUEUE_CHAR *new;
98106f25ae9SGregory Neil Shapiro 	char *args[MAXSMTPARGS];
982d0cef73dSGregory Neil Shapiro 	char inp[MAXINPLINE];
983d0cef73dSGregory Neil Shapiro #if MAXINPLINE < MAXLINE
984*d39bd2c1SGregory Neil Shapiro # error "MAXINPLINE must NOT be less than MAXLINE"
9855b0945b5SGregory Neil Shapiro #endif
986c2aa98e2SPeter Wemm 	char cmdbuf[MAXLINE];
98706f25ae9SGregory Neil Shapiro #if SASL
98806f25ae9SGregory Neil Shapiro 	sasl_conn_t *conn;
98906f25ae9SGregory Neil Shapiro 	volatile bool sasl_ok;
99040266059SGregory Neil Shapiro 	volatile unsigned int n_auth = 0;	/* count of AUTH commands */
99106f25ae9SGregory Neil Shapiro 	bool ismore;
99206f25ae9SGregory Neil Shapiro 	int result;
99306f25ae9SGregory Neil Shapiro 	volatile int authenticating;
99406f25ae9SGregory Neil Shapiro 	char *user;
99594c01205SGregory Neil Shapiro 	char *in, *out2;
9965b0945b5SGregory Neil Shapiro 	char auth_user[MAX_AUTH_USER_LEN], auth_user_tmp[MAX_AUTH_USER_LEN];
9975b0945b5SGregory Neil Shapiro 	unsigned int auth_user_len;
99894c01205SGregory Neil Shapiro # if SASL >= 20000
999d0cef73dSGregory Neil Shapiro 	char *auth_id = NULL;
100094c01205SGregory Neil Shapiro 	const char *out;
100194c01205SGregory Neil Shapiro 	sasl_ssf_t ext_ssf;
1002a7ec597cSGregory Neil Shapiro 	char localip[60], remoteip[60];
100394c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
100494c01205SGregory Neil Shapiro 	char *out;
100506f25ae9SGregory Neil Shapiro 	const char *errstr;
100694c01205SGregory Neil Shapiro 	sasl_external_properties_t ext_ssf;
1007a7ec597cSGregory Neil Shapiro 	struct sockaddr_in saddr_l;
1008a7ec597cSGregory Neil Shapiro 	struct sockaddr_in saddr_r;
100994c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
101094c01205SGregory Neil Shapiro 	sasl_security_properties_t ssp;
101194c01205SGregory Neil Shapiro 	sasl_ssf_t *ssf;
101240266059SGregory Neil Shapiro 	unsigned int inlen, out2len;
101306f25ae9SGregory Neil Shapiro 	unsigned int outlen;
101406f25ae9SGregory Neil Shapiro 	char *volatile auth_type;
101506f25ae9SGregory Neil Shapiro 	char *mechlist;
101640266059SGregory Neil Shapiro 	volatile unsigned int n_mechs;
101740266059SGregory Neil Shapiro 	unsigned int len;
101806f25ae9SGregory Neil Shapiro #endif /* SASL */
101906f25ae9SGregory Neil Shapiro 	int r;
1020e92d3f3fSGregory Neil Shapiro #if STARTTLS
102142e5d165SGregory Neil Shapiro 	int rfd, wfd;
102240266059SGregory Neil Shapiro 	volatile bool tls_active = false;
1023e92d3f3fSGregory Neil Shapiro 	volatile bool smtps = bitnset(D_SMTPS, d_flags);
1024*d39bd2c1SGregory Neil Shapiro 	bool gotostarttls = false;
102506f25ae9SGregory Neil Shapiro 	bool saveQuickAbort;
102606f25ae9SGregory Neil Shapiro 	bool saveSuprErrs;
102740266059SGregory Neil Shapiro 	time_t tlsstart;
10285b0945b5SGregory Neil Shapiro 	int ssl_err, tlsret;
10295b0945b5SGregory Neil Shapiro 	int save_errno;
10305b0945b5SGregory Neil Shapiro 	extern int TLSsslidx;
103106f25ae9SGregory Neil Shapiro #endif /* STARTTLS */
1032*d39bd2c1SGregory Neil Shapiro 	volatile unsigned long features;
1033*d39bd2c1SGregory Neil Shapiro #if PIPELINING && _FFR_NO_PIPE
103440266059SGregory Neil Shapiro 	int np_log = 0;
10355b0945b5SGregory Neil Shapiro #endif
103640266059SGregory Neil Shapiro 	volatile time_t log_delay = (time_t) 0;
1037d0cef73dSGregory Neil Shapiro #if MILTER
1038d0cef73dSGregory Neil Shapiro 	volatile bool milter_cmd_done, milter_cmd_safe;
1039d0cef73dSGregory Neil Shapiro 	volatile bool milter_rcpt_added, milter_cmd_fail;
1040d0cef73dSGregory Neil Shapiro 	ADDRESS addr_st;
1041d0cef73dSGregory Neil Shapiro # define p_addr_st	&addr_st
1042d0cef73dSGregory Neil Shapiro #else /* MILTER */
1043d0cef73dSGregory Neil Shapiro # define p_addr_st	NULL
1044d0cef73dSGregory Neil Shapiro #endif /* MILTER */
1045d0cef73dSGregory Neil Shapiro 	size_t inplen;
1046e3793f76SGregory Neil Shapiro #if _FFR_BADRCPT_SHUTDOWN
1047e3793f76SGregory Neil Shapiro 	int n_badrcpts_adj;
10485b0945b5SGregory Neil Shapiro #endif
1049*d39bd2c1SGregory Neil Shapiro 	bool gotodoquit = false;
1050c2aa98e2SPeter Wemm 
10515b0945b5SGregory Neil Shapiro 	RESET_AUTH_FAIL_LOG_USER;
105240266059SGregory Neil Shapiro 	smtp.sm_nrcpts = 0;
105340266059SGregory Neil Shapiro #if MILTER
105440266059SGregory Neil Shapiro 	smtp.sm_milterize = (nullserver == NULL);
105540266059SGregory Neil Shapiro 	smtp.sm_milterlist = false;
1056d0cef73dSGregory Neil Shapiro 	addr = NULL;
10575b0945b5SGregory Neil Shapiro #endif
105840266059SGregory Neil Shapiro 
105940266059SGregory Neil Shapiro 	/* setup I/O fd correctly for the SMTP server */
106040266059SGregory Neil Shapiro 	setup_smtpd_io();
106140266059SGregory Neil Shapiro 
106240266059SGregory Neil Shapiro #if SM_HEAP_CHECK
106340266059SGregory Neil Shapiro 	if (sm_debug_active(&DebugLeakSmtp, 1))
1064c2aa98e2SPeter Wemm 	{
106540266059SGregory Neil Shapiro 		sm_heap_newgroup();
106640266059SGregory Neil Shapiro 		sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
1067c2aa98e2SPeter Wemm 	}
106840266059SGregory Neil Shapiro #endif /* SM_HEAP_CHECK */
106940266059SGregory Neil Shapiro 
107040266059SGregory Neil Shapiro 	/* XXX the rpool should be set when e is initialized in main() */
107140266059SGregory Neil Shapiro 	e->e_rpool = sm_rpool_new_x(NULL);
107240266059SGregory Neil Shapiro 	e->e_macro.mac_rpool = e->e_rpool;
107306f25ae9SGregory Neil Shapiro 
1074c2aa98e2SPeter Wemm 	settime(e);
107540266059SGregory Neil Shapiro 	sm_getla();
1076c2aa98e2SPeter Wemm 	peerhostname = RealHostName;
1077c2aa98e2SPeter Wemm 	if (peerhostname == NULL)
1078c2aa98e2SPeter Wemm 		peerhostname = "localhost";
1079c2aa98e2SPeter Wemm 	CurHostName = peerhostname;
1080c2aa98e2SPeter Wemm 	CurSmtpClient = macvalue('_', e);
1081c2aa98e2SPeter Wemm 	if (CurSmtpClient == NULL)
1082c2aa98e2SPeter Wemm 		CurSmtpClient = CurHostName;
1083c2aa98e2SPeter Wemm 
1084c2aa98e2SPeter Wemm 	/* check_relay may have set discard bit, save for later */
108540266059SGregory Neil Shapiro 	smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
1086c2aa98e2SPeter Wemm 
108740266059SGregory Neil Shapiro #if PIPELINING
108840266059SGregory Neil Shapiro 	/* auto-flush output when reading input */
108940266059SGregory Neil Shapiro 	(void) sm_io_autoflush(InChannel, OutChannel);
10902fb4f839SGregory Neil Shapiro #endif
109140266059SGregory Neil Shapiro 
109240266059SGregory Neil Shapiro 	sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
109340266059SGregory Neil Shapiro 
1094*d39bd2c1SGregory Neil Shapiro 	maps_reset_chged("server:smtp");
1095*d39bd2c1SGregory Neil Shapiro 
1096*d39bd2c1SGregory Neil Shapiro 	/*
1097*d39bd2c1SGregory Neil Shapiro 	**  Set default features for server.
1098*d39bd2c1SGregory Neil Shapiro 	**
1099*d39bd2c1SGregory Neil Shapiro 	**  Changing SRV_BARE_LF_421 | SRV_BARE_CR_421 below also
1100*d39bd2c1SGregory Neil Shapiro 	**  requires changing srvfeatures() variant code.
1101*d39bd2c1SGregory Neil Shapiro 	*/
1102*d39bd2c1SGregory Neil Shapiro 
110340266059SGregory Neil Shapiro 	features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
110440266059SGregory Neil Shapiro 		     bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
110540266059SGregory Neil Shapiro 		| (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
110640266059SGregory Neil Shapiro 		| (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
110740266059SGregory Neil Shapiro 			: (SRV_OFFER_EXPN
110840266059SGregory Neil Shapiro 			  | (bitset(PRIV_NOVERB, PrivacyFlags)
110940266059SGregory Neil Shapiro 			     ? SRV_NONE : SRV_OFFER_VERB)))
1110af9557fdSGregory Neil Shapiro 		| ((bitset(PRIV_NORECEIPTS, PrivacyFlags) || !SendMIMEErrors)
1111af9557fdSGregory Neil Shapiro 			 ? SRV_NONE : SRV_OFFER_DSN)
111240266059SGregory Neil Shapiro #if SASL
111340266059SGregory Neil Shapiro 		| (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
1114e92d3f3fSGregory Neil Shapiro 		| (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC
1115e92d3f3fSGregory Neil Shapiro 							  : SRV_NONE)
111640266059SGregory Neil Shapiro #endif /* SASL */
111740266059SGregory Neil Shapiro #if PIPELINING
111840266059SGregory Neil Shapiro 		| SRV_OFFER_PIPE
11195b0945b5SGregory Neil Shapiro #endif
1120*d39bd2c1SGregory Neil Shapiro 		| SRV_BAD_PIPELINE
112140266059SGregory Neil Shapiro #if STARTTLS
112240266059SGregory Neil Shapiro 		| (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
112340266059SGregory Neil Shapiro 		| (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
112440266059SGregory Neil Shapiro 						       : SRV_VRFY_CLT)
11255b0945b5SGregory Neil Shapiro #endif
11262fb4f839SGregory Neil Shapiro #if USE_EAI
1127*d39bd2c1SGregory Neil Shapiro 		| (SMTP_UTF8 ? SRV_OFFER_EAI : 0)
11282fb4f839SGregory Neil Shapiro #endif
1129*d39bd2c1SGregory Neil Shapiro 		| SRV_REQ_CRLF | SRV_BARE_LF_421 | SRV_BARE_CR_421
113040266059SGregory Neil Shapiro 		;
113140266059SGregory Neil Shapiro 	if (nullserver == NULL)
113240266059SGregory Neil Shapiro 	{
113340266059SGregory Neil Shapiro 		features = srvfeatures(e, CurSmtpClient, features);
113440266059SGregory Neil Shapiro 		if (bitset(SRV_TMP_FAIL, features))
113540266059SGregory Neil Shapiro 		{
113640266059SGregory Neil Shapiro 			if (LogLevel > 4)
113740266059SGregory Neil Shapiro 				sm_syslog(LOG_ERR, NOQID,
113840266059SGregory Neil Shapiro 					  "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
113940266059SGregory Neil Shapiro 					  CurSmtpClient);
114040266059SGregory Neil Shapiro 			nullserver = "450 4.3.0 Please try again later.";
114140266059SGregory Neil Shapiro 		}
1142e92d3f3fSGregory Neil Shapiro 		else
1143e92d3f3fSGregory Neil Shapiro 		{
1144*d39bd2c1SGregory Neil Shapiro #if PIPELINING && _FFR_NO_PIPE
1145e92d3f3fSGregory Neil Shapiro 			if (bitset(SRV_NO_PIPE, features))
114640266059SGregory Neil Shapiro 			{
114740266059SGregory Neil Shapiro 				/* for consistency */
114840266059SGregory Neil Shapiro 				features &= ~SRV_OFFER_PIPE;
114940266059SGregory Neil Shapiro 			}
1150*d39bd2c1SGregory Neil Shapiro #endif /* PIPELINING && _FFR_NO_PIPE */
1151e92d3f3fSGregory Neil Shapiro #if SASL
1152e92d3f3fSGregory Neil Shapiro 			if (bitset(SRV_REQ_SEC, features))
1153e92d3f3fSGregory Neil Shapiro 				SASLOpts |= SASL_SEC_NOPLAINTEXT;
1154e92d3f3fSGregory Neil Shapiro 			else
1155e92d3f3fSGregory Neil Shapiro 				SASLOpts &= ~SASL_SEC_NOPLAINTEXT;
1156e92d3f3fSGregory Neil Shapiro #endif /* SASL */
1157e92d3f3fSGregory Neil Shapiro 		}
1158e92d3f3fSGregory Neil Shapiro 	}
1159e92d3f3fSGregory Neil Shapiro 	else if (strncmp(nullserver, "421 ", 4) == 0)
1160e92d3f3fSGregory Neil Shapiro 	{
11615b0945b5SGregory Neil Shapiro 		/* Can't use ("%s", ...) due to message() requirements */
1162e92d3f3fSGregory Neil Shapiro 		message(nullserver);
1163*d39bd2c1SGregory Neil Shapiro 		gotodoquit = true;
1164*d39bd2c1SGregory Neil Shapiro 		goto cmdloop;
116540266059SGregory Neil Shapiro 	}
116640266059SGregory Neil Shapiro 
1167d0cef73dSGregory Neil Shapiro 	e->e_features = features;
116840266059SGregory Neil Shapiro 	hostname = macvalue('j', e);
116906f25ae9SGregory Neil Shapiro #if SASL
1170e92d3f3fSGregory Neil Shapiro 	if (AuthRealm == NULL)
1171e92d3f3fSGregory Neil Shapiro 		AuthRealm = hostname;
117240266059SGregory Neil Shapiro 	sasl_ok = bitset(SRV_OFFER_AUTH, features);
117306f25ae9SGregory Neil Shapiro 	n_mechs = 0;
117440266059SGregory Neil Shapiro 	authenticating = SASL_NOT_AUTH;
117506f25ae9SGregory Neil Shapiro 
117606f25ae9SGregory Neil Shapiro 	/* SASL server new connection */
117740266059SGregory Neil Shapiro 	if (sasl_ok)
117840266059SGregory Neil Shapiro 	{
117994c01205SGregory Neil Shapiro # if SASL >= 20000
1180e92d3f3fSGregory Neil Shapiro 		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL,
118194c01205SGregory Neil Shapiro 					 NULL, 0, &conn);
118294c01205SGregory Neil Shapiro # elif SASL > 10505
118306f25ae9SGregory Neil Shapiro 		/* use empty realm: only works in SASL > 1.5.5 */
1184e92d3f3fSGregory Neil Shapiro 		result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn);
118594c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
118606f25ae9SGregory Neil Shapiro 		/* use no realm -> realm is set to hostname by SASL lib */
1187e92d3f3fSGregory Neil Shapiro 		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0,
118840266059SGregory Neil Shapiro 					 &conn);
118994c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
119040266059SGregory Neil Shapiro 		sasl_ok = result == SASL_OK;
119140266059SGregory Neil Shapiro 		if (!sasl_ok)
1192c2aa98e2SPeter Wemm 		{
119340266059SGregory Neil Shapiro 			if (LogLevel > 9)
119440266059SGregory Neil Shapiro 				sm_syslog(LOG_WARNING, NOQID,
119540266059SGregory Neil Shapiro 					  "AUTH error: sasl_server_new failed=%d",
119640266059SGregory Neil Shapiro 					  result);
119740266059SGregory Neil Shapiro 		}
119840266059SGregory Neil Shapiro 	}
119940266059SGregory Neil Shapiro 	if (sasl_ok)
120040266059SGregory Neil Shapiro 	{
120106f25ae9SGregory Neil Shapiro 		/*
120206f25ae9SGregory Neil Shapiro 		**  SASL set properties for sasl
120306f25ae9SGregory Neil Shapiro 		**  set local/remote IP
120494c01205SGregory Neil Shapiro 		**  XXX Cyrus SASL v1 only supports IPv4
120506f25ae9SGregory Neil Shapiro 		**
120606f25ae9SGregory Neil Shapiro 		**  XXX where exactly are these used/required?
120706f25ae9SGregory Neil Shapiro 		**  Kerberos_v4
120806f25ae9SGregory Neil Shapiro 		*/
120906f25ae9SGregory Neil Shapiro 
121094c01205SGregory Neil Shapiro # if SASL >= 20000
121113d88268SGregory Neil Shapiro 		localip[0] = remoteip[0] = '\0';
121294c01205SGregory Neil Shapiro #  if NETINET || NETINET6
121394c01205SGregory Neil Shapiro 		in = macvalue(macid("{daemon_family}"), e);
121494c01205SGregory Neil Shapiro 		if (in != NULL && (
121594c01205SGregory Neil Shapiro #   if NETINET6
121694c01205SGregory Neil Shapiro 		    strcmp(in, "inet6") == 0 ||
12175b0945b5SGregory Neil Shapiro #   endif
121894c01205SGregory Neil Shapiro 		    strcmp(in, "inet") == 0))
121994c01205SGregory Neil Shapiro 		{
122094c01205SGregory Neil Shapiro 			SOCKADDR_LEN_T addrsize;
122194c01205SGregory Neil Shapiro 			SOCKADDR saddr_l;
122294c01205SGregory Neil Shapiro 			SOCKADDR saddr_r;
122394c01205SGregory Neil Shapiro 
122494c01205SGregory Neil Shapiro 			addrsize = sizeof(saddr_r);
122594c01205SGregory Neil Shapiro 			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
122694c01205SGregory Neil Shapiro 						      NULL),
122794c01205SGregory Neil Shapiro 					(struct sockaddr *) &saddr_r,
122894c01205SGregory Neil Shapiro 					&addrsize) == 0)
122994c01205SGregory Neil Shapiro 			{
123094c01205SGregory Neil Shapiro 				if (iptostring(&saddr_r, addrsize,
1231d0cef73dSGregory Neil Shapiro 					       remoteip, sizeof(remoteip)))
123294c01205SGregory Neil Shapiro 				{
123394c01205SGregory Neil Shapiro 					sasl_setprop(conn, SASL_IPREMOTEPORT,
123494c01205SGregory Neil Shapiro 						     remoteip);
123594c01205SGregory Neil Shapiro 				}
123694c01205SGregory Neil Shapiro 				addrsize = sizeof(saddr_l);
123794c01205SGregory Neil Shapiro 				if (getsockname(sm_io_getinfo(InChannel,
123894c01205SGregory Neil Shapiro 							      SM_IO_WHAT_FD,
123994c01205SGregory Neil Shapiro 							      NULL),
124094c01205SGregory Neil Shapiro 						(struct sockaddr *) &saddr_l,
124194c01205SGregory Neil Shapiro 						&addrsize) == 0)
124294c01205SGregory Neil Shapiro 				{
124394c01205SGregory Neil Shapiro 					if (iptostring(&saddr_l, addrsize,
124494c01205SGregory Neil Shapiro 						       localip,
1245d0cef73dSGregory Neil Shapiro 						       sizeof(localip)))
124694c01205SGregory Neil Shapiro 					{
124794c01205SGregory Neil Shapiro 						sasl_setprop(conn,
124894c01205SGregory Neil Shapiro 							     SASL_IPLOCALPORT,
124994c01205SGregory Neil Shapiro 							     localip);
125094c01205SGregory Neil Shapiro 					}
125194c01205SGregory Neil Shapiro 				}
125294c01205SGregory Neil Shapiro 			}
125394c01205SGregory Neil Shapiro 		}
125494c01205SGregory Neil Shapiro #  endif /* NETINET || NETINET6 */
125594c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
125606f25ae9SGregory Neil Shapiro #  if NETINET
125740266059SGregory Neil Shapiro 		in = macvalue(macid("{daemon_family}"), e);
125806f25ae9SGregory Neil Shapiro 		if (in != NULL && strcmp(in, "inet") == 0)
125906f25ae9SGregory Neil Shapiro 		{
126006f25ae9SGregory Neil Shapiro 			SOCKADDR_LEN_T addrsize;
126106f25ae9SGregory Neil Shapiro 
126206f25ae9SGregory Neil Shapiro 			addrsize = sizeof(struct sockaddr_in);
126340266059SGregory Neil Shapiro 			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
126440266059SGregory Neil Shapiro 						      NULL),
126506f25ae9SGregory Neil Shapiro 					(struct sockaddr *)&saddr_r,
126606f25ae9SGregory Neil Shapiro 					&addrsize) == 0)
126706f25ae9SGregory Neil Shapiro 			{
126806f25ae9SGregory Neil Shapiro 				sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
126906f25ae9SGregory Neil Shapiro 				addrsize = sizeof(struct sockaddr_in);
127040266059SGregory Neil Shapiro 				if (getsockname(sm_io_getinfo(InChannel,
127140266059SGregory Neil Shapiro 							      SM_IO_WHAT_FD,
127240266059SGregory Neil Shapiro 							      NULL),
127306f25ae9SGregory Neil Shapiro 						(struct sockaddr *)&saddr_l,
127406f25ae9SGregory Neil Shapiro 						&addrsize) == 0)
127506f25ae9SGregory Neil Shapiro 					sasl_setprop(conn, SASL_IP_LOCAL,
127606f25ae9SGregory Neil Shapiro 						     &saddr_l);
1277c2aa98e2SPeter Wemm 			}
127806f25ae9SGregory Neil Shapiro 		}
127906f25ae9SGregory Neil Shapiro #  endif /* NETINET */
128094c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
128106f25ae9SGregory Neil Shapiro 
128206f25ae9SGregory Neil Shapiro 		auth_type = NULL;
128306f25ae9SGregory Neil Shapiro 		mechlist = NULL;
128406f25ae9SGregory Neil Shapiro 		user = NULL;
128506f25ae9SGregory Neil Shapiro # if 0
128640266059SGregory Neil Shapiro 		macdefine(&BlankEnvelope.e_macro, A_PERM,
128740266059SGregory Neil Shapiro 			macid("{auth_author}"), NULL);
12885b0945b5SGregory Neil Shapiro # endif
128906f25ae9SGregory Neil Shapiro 
129006f25ae9SGregory Neil Shapiro 		/* set properties */
1291d0cef73dSGregory Neil Shapiro 		(void) memset(&ssp, '\0', sizeof(ssp));
129240266059SGregory Neil Shapiro 
129306f25ae9SGregory Neil Shapiro 		/* XXX should these be options settable via .cf ? */
129406f25ae9SGregory Neil Shapiro 		/* ssp.min_ssf = 0; is default due to memset() */
129540266059SGregory Neil Shapiro 		ssp.max_ssf = MaxSLBits;
129606f25ae9SGregory Neil Shapiro 		ssp.maxbufsize = MAXOUTLEN;
129706f25ae9SGregory Neil Shapiro 		ssp.security_flags = SASLOpts & SASL_SEC_MASK;
129806f25ae9SGregory Neil Shapiro 		sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
129906f25ae9SGregory Neil Shapiro 
130006f25ae9SGregory Neil Shapiro 		if (sasl_ok)
130106f25ae9SGregory Neil Shapiro 		{
130206f25ae9SGregory Neil Shapiro 			/*
130306f25ae9SGregory Neil Shapiro 			**  external security strength factor;
130440266059SGregory Neil Shapiro 			**	currently we have none so zero
130506f25ae9SGregory Neil Shapiro 			*/
130640266059SGregory Neil Shapiro 
130794c01205SGregory Neil Shapiro # if SASL >= 20000
130894c01205SGregory Neil Shapiro 			ext_ssf = 0;
130994c01205SGregory Neil Shapiro 			auth_id = NULL;
131094c01205SGregory Neil Shapiro 			sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL,
131194c01205SGregory Neil Shapiro 						 &ext_ssf) == SASL_OK) &&
131294c01205SGregory Neil Shapiro 				   (sasl_setprop(conn, SASL_AUTH_EXTERNAL,
131394c01205SGregory Neil Shapiro 						 auth_id) == SASL_OK));
131494c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
131506f25ae9SGregory Neil Shapiro 			ext_ssf.ssf = 0;
131606f25ae9SGregory Neil Shapiro 			ext_ssf.auth_id = NULL;
131706f25ae9SGregory Neil Shapiro 			sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
131806f25ae9SGregory Neil Shapiro 					       &ext_ssf) == SASL_OK;
131994c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
132006f25ae9SGregory Neil Shapiro 		}
132106f25ae9SGregory Neil Shapiro 		if (sasl_ok)
132206f25ae9SGregory Neil Shapiro 			n_mechs = saslmechs(conn, &mechlist);
132306f25ae9SGregory Neil Shapiro 	}
132406f25ae9SGregory Neil Shapiro #endif /* SASL */
132506f25ae9SGregory Neil Shapiro 
1326da7d7b9cSGregory Neil Shapiro 	(void) set_tls_rd_tmo(TimeOuts.to_nextcommand);
1327a7ec597cSGregory Neil Shapiro 
132840266059SGregory Neil Shapiro #if MILTER
132940266059SGregory Neil Shapiro 	if (smtp.sm_milterize)
133006f25ae9SGregory Neil Shapiro 	{
133106f25ae9SGregory Neil Shapiro 		char state;
133206f25ae9SGregory Neil Shapiro 
133306f25ae9SGregory Neil Shapiro 		/* initialize mail filter connection */
1334ffb83623SGregory Neil Shapiro 		smtp.sm_milterlist = milter_init(e, &state, &smtp.sm_milters);
133506f25ae9SGregory Neil Shapiro 		switch (state)
133606f25ae9SGregory Neil Shapiro 		{
133706f25ae9SGregory Neil Shapiro 		  case SMFIR_REJECT:
133840266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)
133940266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
1340605302a5SGregory Neil Shapiro 					  "Milter: initialization failed, rejecting commands");
134106f25ae9SGregory Neil Shapiro 			greetcode = "554";
134206f25ae9SGregory Neil Shapiro 			nullserver = "Command rejected";
134340266059SGregory Neil Shapiro 			smtp.sm_milterize = false;
134406f25ae9SGregory Neil Shapiro 			break;
134506f25ae9SGregory Neil Shapiro 
134606f25ae9SGregory Neil Shapiro 		  case SMFIR_TEMPFAIL:
134740266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)
134840266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
1349605302a5SGregory Neil Shapiro 					  "Milter: initialization failed, temp failing commands");
135040266059SGregory Neil Shapiro 			tempfail = true;
135140266059SGregory Neil Shapiro 			smtp.sm_milterize = false;
135206f25ae9SGregory Neil Shapiro 			break;
13534e4196cbSGregory Neil Shapiro 
13544e4196cbSGregory Neil Shapiro 		  case SMFIR_SHUTDOWN:
13554e4196cbSGregory Neil Shapiro 			if (MilterLogLevel > 3)
13564e4196cbSGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
13574e4196cbSGregory Neil Shapiro 					  "Milter: initialization failed, closing connection");
13584e4196cbSGregory Neil Shapiro 			tempfail = true;
13594e4196cbSGregory Neil Shapiro 			smtp.sm_milterize = false;
13604e4196cbSGregory Neil Shapiro 			message("421 4.7.0 %s closing connection",
13614e4196cbSGregory Neil Shapiro 					MyHostName);
13624e4196cbSGregory Neil Shapiro 
13634e4196cbSGregory Neil Shapiro 			/* arrange to ignore send list */
13644e4196cbSGregory Neil Shapiro 			e->e_sendqueue = NULL;
1365e3793f76SGregory Neil Shapiro 			lognullconnection = false;
1366*d39bd2c1SGregory Neil Shapiro 			gotodoquit = true;
1367*d39bd2c1SGregory Neil Shapiro 			goto cmdloop;
136806f25ae9SGregory Neil Shapiro 		}
136906f25ae9SGregory Neil Shapiro 	}
137006f25ae9SGregory Neil Shapiro 
137140266059SGregory Neil Shapiro 	if (smtp.sm_milterlist && smtp.sm_milterize &&
137240266059SGregory Neil Shapiro 	    !bitset(EF_DISCARD, e->e_flags))
137306f25ae9SGregory Neil Shapiro 	{
137406f25ae9SGregory Neil Shapiro 		char state;
137540266059SGregory Neil Shapiro 		char *response;
137606f25ae9SGregory Neil Shapiro 
13773a3ef73dSGregory Neil Shapiro 		q = macvalue(macid("{client_name}"), e);
1378d0cef73dSGregory Neil Shapiro 		SM_ASSERT(q != NULL || OpMode == MD_SMTP);
1379d0cef73dSGregory Neil Shapiro 		if (q == NULL)
1380d0cef73dSGregory Neil Shapiro 			q = "localhost";
13813a3ef73dSGregory Neil Shapiro 		response = milter_connect(q, RealHostAddr, e, &state);
138206f25ae9SGregory Neil Shapiro 		switch (state)
138306f25ae9SGregory Neil Shapiro 		{
1384da7d7b9cSGregory Neil Shapiro # if _FFR_MILTER_CONNECT_REPLYCODE
1385da7d7b9cSGregory Neil Shapiro 		  case SMFIR_REPLYCODE:
1386da7d7b9cSGregory Neil Shapiro 			if (*response == '5')
1387da7d7b9cSGregory Neil Shapiro 			{
1388da7d7b9cSGregory Neil Shapiro 				if (MilterLogLevel > 3)
1389da7d7b9cSGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
1390da7d7b9cSGregory Neil Shapiro 						  "Milter: connect: host=%s, addr=%s, reject=%s",
1391da7d7b9cSGregory Neil Shapiro 						  peerhostname,
1392da7d7b9cSGregory Neil Shapiro 						  anynet_ntoa(&RealHostAddr),
1393da7d7b9cSGregory Neil Shapiro 						  response);
1394da7d7b9cSGregory Neil Shapiro 				greetcode = "554"; /* Required by 2821 3.1 */
1395da7d7b9cSGregory Neil Shapiro 				nullserver = newstr(response);
1396da7d7b9cSGregory Neil Shapiro 				if (strlen(nullserver) > 4)
1397da7d7b9cSGregory Neil Shapiro 				{
1398da7d7b9cSGregory Neil Shapiro 					int skip;
1399da7d7b9cSGregory Neil Shapiro 
1400da7d7b9cSGregory Neil Shapiro 					greetmsg = nullserver + 4;
1401da7d7b9cSGregory Neil Shapiro 
1402da7d7b9cSGregory Neil Shapiro 					/* skip over enhanced status code */
1403da7d7b9cSGregory Neil Shapiro 					skip = isenhsc(greetmsg, ' ');
1404da7d7b9cSGregory Neil Shapiro 					if (skip > 0)
1405da7d7b9cSGregory Neil Shapiro 						greetmsg += skip + 1;
1406da7d7b9cSGregory Neil Shapiro 				}
1407da7d7b9cSGregory Neil Shapiro 				smtp.sm_milterize = false;
1408da7d7b9cSGregory Neil Shapiro 				break;
1409da7d7b9cSGregory Neil Shapiro 			}
1410da7d7b9cSGregory Neil Shapiro 			else if (strncmp(response, "421 ", 4) == 0)
1411da7d7b9cSGregory Neil Shapiro 			{
1412da7d7b9cSGregory Neil Shapiro 				int skip;
1413da7d7b9cSGregory Neil Shapiro 				const char *msg = response + 4;
1414da7d7b9cSGregory Neil Shapiro 
1415da7d7b9cSGregory Neil Shapiro 				if (MilterLogLevel > 3)
1416da7d7b9cSGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
1417da7d7b9cSGregory Neil Shapiro 						  "Milter: connect: host=%s, addr=%s, shutdown=%s",
1418da7d7b9cSGregory Neil Shapiro 						  peerhostname,
1419da7d7b9cSGregory Neil Shapiro 						  anynet_ntoa(&RealHostAddr),
1420da7d7b9cSGregory Neil Shapiro 						  response);
1421da7d7b9cSGregory Neil Shapiro 				tempfail = true;
1422da7d7b9cSGregory Neil Shapiro 				smtp.sm_milterize = false;
1423da7d7b9cSGregory Neil Shapiro 
1424da7d7b9cSGregory Neil Shapiro 				/* skip over enhanced status code */
1425da7d7b9cSGregory Neil Shapiro 				skip = isenhsc(msg, ' ');
1426da7d7b9cSGregory Neil Shapiro 				if (skip > 0)
1427da7d7b9cSGregory Neil Shapiro 					msg += skip + 1;
1428da7d7b9cSGregory Neil Shapiro 				message("421 %s %s", MyHostName, msg);
1429da7d7b9cSGregory Neil Shapiro 
1430da7d7b9cSGregory Neil Shapiro 				/* arrange to ignore send list */
1431da7d7b9cSGregory Neil Shapiro 				e->e_sendqueue = NULL;
1432*d39bd2c1SGregory Neil Shapiro 				gotodoquit = true;
1433*d39bd2c1SGregory Neil Shapiro 				goto cmdloop;
1434da7d7b9cSGregory Neil Shapiro 			}
1435da7d7b9cSGregory Neil Shapiro 			else
1436da7d7b9cSGregory Neil Shapiro 			{
1437da7d7b9cSGregory Neil Shapiro 				if (MilterLogLevel > 3)
1438da7d7b9cSGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
1439da7d7b9cSGregory Neil Shapiro 						  "Milter: connect: host=%s, addr=%s, temp failing commands=%s",
1440da7d7b9cSGregory Neil Shapiro 						  peerhostname,
1441da7d7b9cSGregory Neil Shapiro 						  anynet_ntoa(&RealHostAddr),
1442da7d7b9cSGregory Neil Shapiro 						  response);
1443da7d7b9cSGregory Neil Shapiro 				/*tempfail = true;*/
1444da7d7b9cSGregory Neil Shapiro 				smtp.sm_milterize = false;
1445da7d7b9cSGregory Neil Shapiro 				nullserver = newstr(response);
1446da7d7b9cSGregory Neil Shapiro 				break;
1447da7d7b9cSGregory Neil Shapiro 			}
1448da7d7b9cSGregory Neil Shapiro 
1449da7d7b9cSGregory Neil Shapiro # else /* _FFR_MILTER_CONNECT_REPLYCODE */
145006f25ae9SGregory Neil Shapiro 		  case SMFIR_REPLYCODE:	/* REPLYCODE shouldn't happen */
1451da7d7b9cSGregory Neil Shapiro # endif /* _FFR_MILTER_CONNECT_REPLYCODE */
145206f25ae9SGregory Neil Shapiro 		  case SMFIR_REJECT:
145340266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)
145440266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
145540266059SGregory Neil Shapiro 					  "Milter: connect: host=%s, addr=%s, rejecting commands",
145640266059SGregory Neil Shapiro 					  peerhostname,
145740266059SGregory Neil Shapiro 					  anynet_ntoa(&RealHostAddr));
145806f25ae9SGregory Neil Shapiro 			greetcode = "554";
145906f25ae9SGregory Neil Shapiro 			nullserver = "Command rejected";
146040266059SGregory Neil Shapiro 			smtp.sm_milterize = false;
146106f25ae9SGregory Neil Shapiro 			break;
146206f25ae9SGregory Neil Shapiro 
146306f25ae9SGregory Neil Shapiro 		  case SMFIR_TEMPFAIL:
146440266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)
146540266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
146640266059SGregory Neil Shapiro 					  "Milter: connect: host=%s, addr=%s, temp failing commands",
146740266059SGregory Neil Shapiro 					  peerhostname,
146840266059SGregory Neil Shapiro 					  anynet_ntoa(&RealHostAddr));
146940266059SGregory Neil Shapiro 			tempfail = true;
147040266059SGregory Neil Shapiro 			smtp.sm_milterize = false;
147106f25ae9SGregory Neil Shapiro 			break;
147213bd1963SGregory Neil Shapiro 
147313bd1963SGregory Neil Shapiro 		  case SMFIR_SHUTDOWN:
147413bd1963SGregory Neil Shapiro 			if (MilterLogLevel > 3)
147513bd1963SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
147613bd1963SGregory Neil Shapiro 					  "Milter: connect: host=%s, addr=%s, shutdown",
147713bd1963SGregory Neil Shapiro 					  peerhostname,
147813bd1963SGregory Neil Shapiro 					  anynet_ntoa(&RealHostAddr));
147913bd1963SGregory Neil Shapiro 			tempfail = true;
148013bd1963SGregory Neil Shapiro 			smtp.sm_milterize = false;
148113bd1963SGregory Neil Shapiro 			message("421 4.7.0 %s closing connection",
148213bd1963SGregory Neil Shapiro 					MyHostName);
148313bd1963SGregory Neil Shapiro 
148413bd1963SGregory Neil Shapiro 			/* arrange to ignore send list */
148513bd1963SGregory Neil Shapiro 			e->e_sendqueue = NULL;
1486*d39bd2c1SGregory Neil Shapiro 			gotodoquit = true;
1487*d39bd2c1SGregory Neil Shapiro 			goto cmdloop;
148806f25ae9SGregory Neil Shapiro 		}
148940266059SGregory Neil Shapiro 		if (response != NULL)
1490da7d7b9cSGregory Neil Shapiro 			sm_free(response);
149106f25ae9SGregory Neil Shapiro 	}
149240266059SGregory Neil Shapiro #endif /* MILTER */
149340266059SGregory Neil Shapiro 
1494e92d3f3fSGregory Neil Shapiro 	/*
1495e92d3f3fSGregory Neil Shapiro 	**  Broken proxies and SMTP slammers
1496e92d3f3fSGregory Neil Shapiro 	**  push data without waiting, catch them
1497e92d3f3fSGregory Neil Shapiro 	*/
1498e92d3f3fSGregory Neil Shapiro 
1499e92d3f3fSGregory Neil Shapiro 	if (
150040266059SGregory Neil Shapiro #if STARTTLS
1501e92d3f3fSGregory Neil Shapiro 	    !smtps &&
15025b0945b5SGregory Neil Shapiro #endif
1503d0cef73dSGregory Neil Shapiro 	    *greetcode == '2' && nullserver == NULL)
1504e92d3f3fSGregory Neil Shapiro 	{
1505e92d3f3fSGregory Neil Shapiro 		time_t msecs = 0;
1506e92d3f3fSGregory Neil Shapiro 		char **pvp;
1507e92d3f3fSGregory Neil Shapiro 		char pvpbuf[PSBUFSIZE];
1508e92d3f3fSGregory Neil Shapiro 
1509e92d3f3fSGregory Neil Shapiro 		/* Ask the rulesets how long to pause */
1510e92d3f3fSGregory Neil Shapiro 		pvp = NULL;
1511e92d3f3fSGregory Neil Shapiro 		r = rscap("greet_pause", peerhostname,
1512e92d3f3fSGregory Neil Shapiro 			  anynet_ntoa(&RealHostAddr), e,
1513e92d3f3fSGregory Neil Shapiro 			  &pvp, pvpbuf, sizeof(pvpbuf));
1514e92d3f3fSGregory Neil Shapiro 		if (r == EX_OK && pvp != NULL && pvp[0] != NULL &&
1515e92d3f3fSGregory Neil Shapiro 		    (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
1516e92d3f3fSGregory Neil Shapiro 		{
1517e92d3f3fSGregory Neil Shapiro 			msecs = strtol(pvp[1], NULL, 10);
1518e92d3f3fSGregory Neil Shapiro 		}
1519e92d3f3fSGregory Neil Shapiro 
1520e92d3f3fSGregory Neil Shapiro 		if (msecs > 0)
1521e92d3f3fSGregory Neil Shapiro 		{
1522*d39bd2c1SGregory Neil Shapiro 			struct timeval *tp; /* total pause */
1523e92d3f3fSGregory Neil Shapiro 
1524*d39bd2c1SGregory Neil Shapiro 			/* Obey RFC 2821: 4.5.3.2: 220 timeout of 5 minutes (300 seconds) */
1525*d39bd2c1SGregory Neil Shapiro 			if (msecs >= 300000)
1526*d39bd2c1SGregory Neil Shapiro 				msecs = 300000;
1527e92d3f3fSGregory Neil Shapiro 
1528e92d3f3fSGregory Neil Shapiro 			/* check if data is on the socket during the pause */
1529*d39bd2c1SGregory Neil Shapiro 			if ((tp = channel_readable(InChannel, msecs)) != NULL)
1530e92d3f3fSGregory Neil Shapiro 			{
1531e92d3f3fSGregory Neil Shapiro 				greetcode = "554";
1532e92d3f3fSGregory Neil Shapiro 				nullserver = "Command rejected";
1533e92d3f3fSGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
1534d0cef73dSGregory Neil Shapiro 					  "rejecting commands from %s [%s] due to pre-greeting traffic after %d seconds",
1535e92d3f3fSGregory Neil Shapiro 					  peerhostname,
1536d0cef73dSGregory Neil Shapiro 					  anynet_ntoa(&RealHostAddr),
1537*d39bd2c1SGregory Neil Shapiro 					  (int) tp->tv_sec +
1538*d39bd2c1SGregory Neil Shapiro 						(tp->tv_usec >= 500000 ? 1 : 0)
15394e4196cbSGregory Neil Shapiro 					 );
1540e92d3f3fSGregory Neil Shapiro 			}
1541e92d3f3fSGregory Neil Shapiro 		}
1542e92d3f3fSGregory Neil Shapiro 	}
1543e92d3f3fSGregory Neil Shapiro 
1544e92d3f3fSGregory Neil Shapiro #if STARTTLS
154540266059SGregory Neil Shapiro 	/* If this an smtps connection, start TLS now */
154640266059SGregory Neil Shapiro 	if (smtps)
1547a7ec597cSGregory Neil Shapiro 	{
15485b0945b5SGregory Neil Shapiro 		if (!tls_ok_srv || srv_ctx == NULL)
15495b0945b5SGregory Neil Shapiro 		{
15505b0945b5SGregory Neil Shapiro 			sm_syslog(LOG_ERR, e->e_id,
15515b0945b5SGregory Neil Shapiro 				"smtps: TLS not available, exiting");
15525b0945b5SGregory Neil Shapiro 			exit(EX_CONFIG);
15535b0945b5SGregory Neil Shapiro 		}
1554a7ec597cSGregory Neil Shapiro 		Errors = 0;
15555b0945b5SGregory Neil Shapiro 		first = true;
15565b0945b5SGregory Neil Shapiro 		gothello = false;
15575b0945b5SGregory Neil Shapiro 		smtp.sm_gotmail = false;
1558*d39bd2c1SGregory Neil Shapiro 		gotostarttls = true;
1559*d39bd2c1SGregory Neil Shapiro 		goto cmdloop;
1560a7ec597cSGregory Neil Shapiro 	}
156140266059SGregory Neil Shapiro 
156240266059SGregory Neil Shapiro   greeting:
156340266059SGregory Neil Shapiro 
156440266059SGregory Neil Shapiro #endif /* STARTTLS */
1565c2aa98e2SPeter Wemm 
1566c2aa98e2SPeter Wemm 	/* output the first line, inserting "ESMTP" as second word */
156740266059SGregory Neil Shapiro 	if (*greetcode == '5')
1568da7d7b9cSGregory Neil Shapiro 		(void) sm_snprintf(inp, sizeof(inp), "%s %s", hostname,
1569da7d7b9cSGregory Neil Shapiro 				   greetmsg);
157040266059SGregory Neil Shapiro 	else
1571d0cef73dSGregory Neil Shapiro 		expand(SmtpGreeting, inp, sizeof(inp), e);
157240266059SGregory Neil Shapiro 
1573c2aa98e2SPeter Wemm 	p = strchr(inp, '\n');
1574c2aa98e2SPeter Wemm 	if (p != NULL)
1575c2aa98e2SPeter Wemm 		*p++ = '\0';
1576c2aa98e2SPeter Wemm 	id = strchr(inp, ' ');
1577c2aa98e2SPeter Wemm 	if (id == NULL)
1578c2aa98e2SPeter Wemm 		id = &inp[strlen(inp)];
157906f25ae9SGregory Neil Shapiro 	if (p == NULL)
1580d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
158106f25ae9SGregory Neil Shapiro 			 "%s %%.*s ESMTP%%s", greetcode);
158206f25ae9SGregory Neil Shapiro 	else
1583d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
158406f25ae9SGregory Neil Shapiro 			 "%s-%%.*s ESMTP%%s", greetcode);
158542e5d165SGregory Neil Shapiro 	message(cmdbuf, (int) (id - inp), inp, id);
1586c2aa98e2SPeter Wemm 
1587c2aa98e2SPeter Wemm 	/* output remaining lines */
1588c2aa98e2SPeter Wemm 	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
1589c2aa98e2SPeter Wemm 	{
1590c2aa98e2SPeter Wemm 		*p++ = '\0';
15915b0945b5SGregory Neil Shapiro 		if (SM_ISSPACE(*id))
1592c2aa98e2SPeter Wemm 			id++;
1593d0cef73dSGregory Neil Shapiro 		(void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, "-%s");
159406f25ae9SGregory Neil Shapiro 		message(cmdbuf, id);
1595c2aa98e2SPeter Wemm 	}
1596c2aa98e2SPeter Wemm 	if (id != NULL)
1597c2aa98e2SPeter Wemm 	{
15985b0945b5SGregory Neil Shapiro 		if (SM_ISSPACE(*id))
1599c2aa98e2SPeter Wemm 			id++;
1600d0cef73dSGregory Neil Shapiro 		(void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, " %s");
160106f25ae9SGregory Neil Shapiro 		message(cmdbuf, id);
1602c2aa98e2SPeter Wemm 	}
1603c2aa98e2SPeter Wemm 
1604c2aa98e2SPeter Wemm 	protocol = NULL;
1605c2aa98e2SPeter Wemm 	sendinghost = macvalue('s', e);
160640266059SGregory Neil Shapiro 
160740266059SGregory Neil Shapiro 	/* If quarantining by a connect/ehlo action, save between messages */
160840266059SGregory Neil Shapiro 	if (e->e_quarmsg == NULL)
160940266059SGregory Neil Shapiro 		smtp.sm_quarmsg = NULL;
161040266059SGregory Neil Shapiro 	else
161140266059SGregory Neil Shapiro 		smtp.sm_quarmsg = newstr(e->e_quarmsg);
161240266059SGregory Neil Shapiro 
161340266059SGregory Neil Shapiro 	/* sendinghost's storage must outlive the current envelope */
161440266059SGregory Neil Shapiro 	if (sendinghost != NULL)
161540266059SGregory Neil Shapiro 		sendinghost = sm_strdup_x(sendinghost);
161640266059SGregory Neil Shapiro 	first = true;
161740266059SGregory Neil Shapiro 	gothello = false;
161840266059SGregory Neil Shapiro 	smtp.sm_gotmail = false;
1619c2aa98e2SPeter Wemm 	for (;;)
1620c2aa98e2SPeter Wemm 	{
1621*d39bd2c1SGregory Neil Shapiro 
1622*d39bd2c1SGregory Neil Shapiro   cmdloop:
162340266059SGregory Neil Shapiro 	    SM_TRY
162440266059SGregory Neil Shapiro 	    {
162540266059SGregory Neil Shapiro 		QuickAbort = false;
162640266059SGregory Neil Shapiro 		HoldErrs = false;
162740266059SGregory Neil Shapiro 		SuprErrs = false;
162840266059SGregory Neil Shapiro 		LogUsrErrs = false;
162940266059SGregory Neil Shapiro 		OnlyOneError = true;
1630c2aa98e2SPeter Wemm 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
1631d0cef73dSGregory Neil Shapiro #if MILTER
1632d0cef73dSGregory Neil Shapiro 		milter_cmd_fail = false;
16335b0945b5SGregory Neil Shapiro #endif
1634c2aa98e2SPeter Wemm 
1635c2aa98e2SPeter Wemm 		/* setup for the read */
1636c2aa98e2SPeter Wemm 		e->e_to = NULL;
1637c2aa98e2SPeter Wemm 		Errors = 0;
163806f25ae9SGregory Neil Shapiro 		FileName = NULL;
163940266059SGregory Neil Shapiro 		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
1640c2aa98e2SPeter Wemm 
1641*d39bd2c1SGregory Neil Shapiro 		if (gotodoquit)
1642*d39bd2c1SGregory Neil Shapiro 		{
1643*d39bd2c1SGregory Neil Shapiro 			gotodoquit = false;
1644*d39bd2c1SGregory Neil Shapiro 			goto doquit;
1645*d39bd2c1SGregory Neil Shapiro 		}
1646*d39bd2c1SGregory Neil Shapiro #if STARTTLS
1647*d39bd2c1SGregory Neil Shapiro 		if (gotostarttls)
1648*d39bd2c1SGregory Neil Shapiro 		{
1649*d39bd2c1SGregory Neil Shapiro 			gotostarttls = false;
1650*d39bd2c1SGregory Neil Shapiro 			goto starttls;
1651*d39bd2c1SGregory Neil Shapiro 		}
1652*d39bd2c1SGregory Neil Shapiro #endif
1653*d39bd2c1SGregory Neil Shapiro 
1654c2aa98e2SPeter Wemm 		/* read the input line */
1655c2aa98e2SPeter Wemm 		SmtpPhase = "server cmd read";
165640266059SGregory Neil Shapiro 		sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
1657c2aa98e2SPeter Wemm 
1658c2aa98e2SPeter Wemm 		/* handle errors */
165940266059SGregory Neil Shapiro 		if (sm_io_error(OutChannel) ||
1660d0cef73dSGregory Neil Shapiro 		    (p = sfgets(inp, sizeof(inp), InChannel,
166106f25ae9SGregory Neil Shapiro 				TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
1662c2aa98e2SPeter Wemm 		{
166306f25ae9SGregory Neil Shapiro 			char *d;
166406f25ae9SGregory Neil Shapiro 
166540266059SGregory Neil Shapiro 			d = macvalue(macid("{daemon_name}"), e);
166606f25ae9SGregory Neil Shapiro 			if (d == NULL)
166706f25ae9SGregory Neil Shapiro 				d = "stdin";
1668c2aa98e2SPeter Wemm 			/* end of file, just die */
1669c2aa98e2SPeter Wemm 			disconnect(1, e);
167006f25ae9SGregory Neil Shapiro 
167140266059SGregory Neil Shapiro #if MILTER
167206f25ae9SGregory Neil Shapiro 			/* close out milter filters */
167306f25ae9SGregory Neil Shapiro 			milter_quit(e);
16745b0945b5SGregory Neil Shapiro #endif
167506f25ae9SGregory Neil Shapiro 
167606f25ae9SGregory Neil Shapiro 			message("421 4.4.1 %s Lost input channel from %s",
1677c2aa98e2SPeter Wemm 				MyHostName, CurSmtpClient);
167840266059SGregory Neil Shapiro 			if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
1679c2aa98e2SPeter Wemm 				sm_syslog(LOG_NOTICE, e->e_id,
168013bd1963SGregory Neil Shapiro 					  "lost input channel from %s to %s after %s",
168106f25ae9SGregory Neil Shapiro 					  CurSmtpClient, d,
168206f25ae9SGregory Neil Shapiro 					  (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
1683c2aa98e2SPeter Wemm 			/*
1684c2aa98e2SPeter Wemm 			**  If have not accepted mail (DATA), do not bounce
1685c2aa98e2SPeter Wemm 			**  bad addresses back to sender.
1686c2aa98e2SPeter Wemm 			*/
168706f25ae9SGregory Neil Shapiro 
1688c2aa98e2SPeter Wemm 			if (bitset(EF_CLRQUEUE, e->e_flags))
1689c2aa98e2SPeter Wemm 				e->e_sendqueue = NULL;
169006f25ae9SGregory Neil Shapiro 			goto doquit;
1691c2aa98e2SPeter Wemm 		}
1692c2aa98e2SPeter Wemm 
1693d0cef73dSGregory Neil Shapiro 		/* also used by "proxy" check below */
1694d0cef73dSGregory Neil Shapiro 		inplen = strlen(inp);
1695d0cef73dSGregory Neil Shapiro #if SASL
1696d0cef73dSGregory Neil Shapiro 		/*
1697d0cef73dSGregory Neil Shapiro 		**  SMTP AUTH requires accepting any length,
1698d0cef73dSGregory Neil Shapiro 		**  at least for challenge/response. However, not imposing
1699d0cef73dSGregory Neil Shapiro 		**  a limit is a bad idea (denial of service).
1700d0cef73dSGregory Neil Shapiro 		*/
1701d0cef73dSGregory Neil Shapiro 
1702d0cef73dSGregory Neil Shapiro 		if (authenticating != SASL_PROC_AUTH
1703d0cef73dSGregory Neil Shapiro 		    && sm_strncasecmp(inp, "AUTH ", 5) != 0
1704d0cef73dSGregory Neil Shapiro 		    && inplen > MAXLINE)
1705d0cef73dSGregory Neil Shapiro 		{
1706d0cef73dSGregory Neil Shapiro 			message("421 4.7.0 %s Command too long, possible attack %s",
1707d0cef73dSGregory Neil Shapiro 				MyHostName, CurSmtpClient);
1708d0cef73dSGregory Neil Shapiro 			sm_syslog(LOG_INFO, e->e_id,
1709d0cef73dSGregory Neil Shapiro 				  "%s: SMTP violation, input too long: %lu",
1710d0cef73dSGregory Neil Shapiro 				  CurSmtpClient, (unsigned long) inplen);
1711d0cef73dSGregory Neil Shapiro 			goto doquit;
1712d0cef73dSGregory Neil Shapiro 		}
1713d0cef73dSGregory Neil Shapiro #endif /* SASL */
1714d0cef73dSGregory Neil Shapiro 
17152fb4f839SGregory Neil Shapiro 		if (first || bitset(SRV_NO_HTTP_CMD, features))
171640266059SGregory Neil Shapiro 		{
1717d0cef73dSGregory Neil Shapiro 			size_t cmdlen;
171813bd1963SGregory Neil Shapiro 			int idx;
171913bd1963SGregory Neil Shapiro 			char *http_cmd;
172013bd1963SGregory Neil Shapiro 			static char *http_cmds[] = { "GET", "POST",
172113bd1963SGregory Neil Shapiro 						     "CONNECT", "USER", NULL };
172213bd1963SGregory Neil Shapiro 
172313bd1963SGregory Neil Shapiro 			for (idx = 0; (http_cmd = http_cmds[idx]) != NULL;
172413bd1963SGregory Neil Shapiro 			     idx++)
172513bd1963SGregory Neil Shapiro 			{
172613bd1963SGregory Neil Shapiro 				cmdlen = strlen(http_cmd);
172713bd1963SGregory Neil Shapiro 				if (cmdlen < inplen &&
172813bd1963SGregory Neil Shapiro 				    sm_strncasecmp(inp, http_cmd, cmdlen) == 0 &&
17295b0945b5SGregory Neil Shapiro 				    SM_ISSPACE(inp[cmdlen]))
173013bd1963SGregory Neil Shapiro 				{
173113bd1963SGregory Neil Shapiro 					/* Open proxy, drop it */
17322fb4f839SGregory Neil Shapiro 					message("421 4.7.0 %s %s %s",
17332fb4f839SGregory Neil Shapiro 						MyHostName,
17342fb4f839SGregory Neil Shapiro 						first ? "Rejecting open proxy"
17352fb4f839SGregory Neil Shapiro 							: "HTTP command",
17362fb4f839SGregory Neil Shapiro 						CurSmtpClient);
173713bd1963SGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
173813bd1963SGregory Neil Shapiro 						  "%s: probable open proxy: command=%.40s",
173913bd1963SGregory Neil Shapiro 						  CurSmtpClient, inp);
174013bd1963SGregory Neil Shapiro 					goto doquit;
174113bd1963SGregory Neil Shapiro 				}
174213bd1963SGregory Neil Shapiro 			}
174340266059SGregory Neil Shapiro 			first = false;
174440266059SGregory Neil Shapiro 		}
174540266059SGregory Neil Shapiro 
1746c2aa98e2SPeter Wemm 		/* clean up end of line */
174740266059SGregory Neil Shapiro 		fixcrlf(inp, true);
174840266059SGregory Neil Shapiro 
1749*d39bd2c1SGregory Neil Shapiro #if PIPELINING && _FFR_NO_PIPE
175040266059SGregory Neil Shapiro 		/*
175140266059SGregory Neil Shapiro 		**  if there is more input and pipelining is disabled:
175240266059SGregory Neil Shapiro 		**	delay ... (and maybe discard the input?)
175340266059SGregory Neil Shapiro 		*/
175440266059SGregory Neil Shapiro 
175540266059SGregory Neil Shapiro 		if (bitset(SRV_NO_PIPE, features) &&
175613bd1963SGregory Neil Shapiro 		    sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
175740266059SGregory Neil Shapiro 		{
175840266059SGregory Neil Shapiro 			if (++np_log < 3)
175940266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, NOQID,
17609bd497b8SGregory Neil Shapiro 					  "unauthorized PIPELINING, sleeping, relay=%.100s",
17619bd497b8SGregory Neil Shapiro 					   CurSmtpClient);
176240266059SGregory Neil Shapiro 			sleep(1);
176340266059SGregory Neil Shapiro 		}
1764*d39bd2c1SGregory Neil Shapiro #endif /* PIPELINING && _FFR_NO_PIPE */
1765c2aa98e2SPeter Wemm 
176606f25ae9SGregory Neil Shapiro #if SASL
176706f25ae9SGregory Neil Shapiro 		if (authenticating == SASL_PROC_AUTH)
176806f25ae9SGregory Neil Shapiro 		{
176906f25ae9SGregory Neil Shapiro # if 0
177006f25ae9SGregory Neil Shapiro 			if (*inp == '\0')
177106f25ae9SGregory Neil Shapiro 			{
177206f25ae9SGregory Neil Shapiro 				authenticating = SASL_NOT_AUTH;
177306f25ae9SGregory Neil Shapiro 				message("501 5.5.2 missing input");
1774a7ec597cSGregory Neil Shapiro 				RESET_SASLCONN;
177506f25ae9SGregory Neil Shapiro 				continue;
177606f25ae9SGregory Neil Shapiro 			}
177706f25ae9SGregory Neil Shapiro # endif /* 0 */
177806f25ae9SGregory Neil Shapiro 			if (*inp == '*' && *(inp + 1) == '\0')
177906f25ae9SGregory Neil Shapiro 			{
178006f25ae9SGregory Neil Shapiro 				authenticating = SASL_NOT_AUTH;
178106f25ae9SGregory Neil Shapiro 
1782ffb83623SGregory Neil Shapiro 				/* RFC 2554 4. */
178306f25ae9SGregory Neil Shapiro 				message("501 5.0.0 AUTH aborted");
1784a7ec597cSGregory Neil Shapiro 				RESET_SASLCONN;
178506f25ae9SGregory Neil Shapiro 				continue;
178606f25ae9SGregory Neil Shapiro 			}
178706f25ae9SGregory Neil Shapiro 
178806f25ae9SGregory Neil Shapiro 			/* could this be shorter? XXX */
178994c01205SGregory Neil Shapiro # if SASL >= 20000
179094c01205SGregory Neil Shapiro 			in = xalloc(strlen(inp) + 1);
179194c01205SGregory Neil Shapiro 			result = sasl_decode64(inp, strlen(inp), in,
179294c01205SGregory Neil Shapiro 					       strlen(inp), &inlen);
179394c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
179406f25ae9SGregory Neil Shapiro 			out = xalloc(strlen(inp));
179506f25ae9SGregory Neil Shapiro 			result = sasl_decode64(inp, strlen(inp), out, &outlen);
179694c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
179706f25ae9SGregory Neil Shapiro 			if (result != SASL_OK)
179806f25ae9SGregory Neil Shapiro 			{
179906f25ae9SGregory Neil Shapiro 				authenticating = SASL_NOT_AUTH;
180006f25ae9SGregory Neil Shapiro 
1801ffb83623SGregory Neil Shapiro 				/* RFC 2554 4. */
180206f25ae9SGregory Neil Shapiro 				message("501 5.5.4 cannot decode AUTH parameter %s",
180306f25ae9SGregory Neil Shapiro 					inp);
180494c01205SGregory Neil Shapiro # if SASL >= 20000
180594c01205SGregory Neil Shapiro 				sm_free(in);
18065b0945b5SGregory Neil Shapiro # endif
1807a7ec597cSGregory Neil Shapiro 				RESET_SASLCONN;
180806f25ae9SGregory Neil Shapiro 				continue;
180906f25ae9SGregory Neil Shapiro 			}
181006f25ae9SGregory Neil Shapiro 
181194c01205SGregory Neil Shapiro # if SASL >= 20000
18125b0945b5SGregory Neil Shapiro 			SET_AUTH_USER_TMP(in, inlen);
181394c01205SGregory Neil Shapiro 			result = sasl_server_step(conn,	in, inlen,
181494c01205SGregory Neil Shapiro 						  &out, &outlen);
181594c01205SGregory Neil Shapiro 			sm_free(in);
181694c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
18175b0945b5SGregory Neil Shapiro 			SET_AUTH_USER_TMP(out, outlen);
181806f25ae9SGregory Neil Shapiro 			result = sasl_server_step(conn,	out, outlen,
181906f25ae9SGregory Neil Shapiro 						  &out, &outlen, &errstr);
182094c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
182106f25ae9SGregory Neil Shapiro 
182206f25ae9SGregory Neil Shapiro 			/* get an OK if we're done */
182306f25ae9SGregory Neil Shapiro 			if (result == SASL_OK)
182406f25ae9SGregory Neil Shapiro 			{
182506f25ae9SGregory Neil Shapiro   authenticated:
182606f25ae9SGregory Neil Shapiro 				message("235 2.0.0 OK Authenticated");
182706f25ae9SGregory Neil Shapiro 				authenticating = SASL_IS_AUTH;
182840266059SGregory Neil Shapiro 				macdefine(&BlankEnvelope.e_macro, A_TEMP,
182940266059SGregory Neil Shapiro 					macid("{auth_type}"), auth_type);
183006f25ae9SGregory Neil Shapiro 
183194c01205SGregory Neil Shapiro # if SASL >= 20000
183294c01205SGregory Neil Shapiro 				user = macvalue(macid("{auth_authen}"), e);
183394c01205SGregory Neil Shapiro 
183494c01205SGregory Neil Shapiro 				/* get security strength (features) */
183594c01205SGregory Neil Shapiro 				result = sasl_getprop(conn, SASL_SSF,
183694c01205SGregory Neil Shapiro 						      (const void **) &ssf);
183794c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
183806f25ae9SGregory Neil Shapiro 				result = sasl_getprop(conn, SASL_USERNAME,
183906f25ae9SGregory Neil Shapiro 						      (void **)&user);
184006f25ae9SGregory Neil Shapiro 				if (result != SASL_OK)
184106f25ae9SGregory Neil Shapiro 				{
184206f25ae9SGregory Neil Shapiro 					user = "";
184340266059SGregory Neil Shapiro 					macdefine(&BlankEnvelope.e_macro,
184440266059SGregory Neil Shapiro 						  A_PERM,
184540266059SGregory Neil Shapiro 						  macid("{auth_authen}"), NULL);
184606f25ae9SGregory Neil Shapiro 				}
184706f25ae9SGregory Neil Shapiro 				else
184806f25ae9SGregory Neil Shapiro 				{
184940266059SGregory Neil Shapiro 					macdefine(&BlankEnvelope.e_macro,
185040266059SGregory Neil Shapiro 						  A_TEMP,
1851e92d3f3fSGregory Neil Shapiro 						  macid("{auth_authen}"),
1852e92d3f3fSGregory Neil Shapiro 						  xtextify(user, "<>\")"));
185306f25ae9SGregory Neil Shapiro 				}
185406f25ae9SGregory Neil Shapiro 
185506f25ae9SGregory Neil Shapiro #  if 0
185606f25ae9SGregory Neil Shapiro 				/* get realm? */
185706f25ae9SGregory Neil Shapiro 				sasl_getprop(conn, SASL_REALM, (void **) &data);
18585b0945b5SGregory Neil Shapiro #  endif
185906f25ae9SGregory Neil Shapiro 
186006f25ae9SGregory Neil Shapiro 				/* get security strength (features) */
186106f25ae9SGregory Neil Shapiro 				result = sasl_getprop(conn, SASL_SSF,
186206f25ae9SGregory Neil Shapiro 						      (void **) &ssf);
186394c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
186406f25ae9SGregory Neil Shapiro 				if (result != SASL_OK)
186506f25ae9SGregory Neil Shapiro 				{
186640266059SGregory Neil Shapiro 					macdefine(&BlankEnvelope.e_macro,
186740266059SGregory Neil Shapiro 						  A_PERM,
186840266059SGregory Neil Shapiro 						  macid("{auth_ssf}"), "0");
186906f25ae9SGregory Neil Shapiro 					ssf = NULL;
187006f25ae9SGregory Neil Shapiro 				}
187106f25ae9SGregory Neil Shapiro 				else
187206f25ae9SGregory Neil Shapiro 				{
187306f25ae9SGregory Neil Shapiro 					char pbuf[8];
187406f25ae9SGregory Neil Shapiro 
1875d0cef73dSGregory Neil Shapiro 					(void) sm_snprintf(pbuf, sizeof(pbuf),
187640266059SGregory Neil Shapiro 							   "%u", *ssf);
187740266059SGregory Neil Shapiro 					macdefine(&BlankEnvelope.e_macro,
187840266059SGregory Neil Shapiro 						  A_TEMP,
187940266059SGregory Neil Shapiro 						  macid("{auth_ssf}"), pbuf);
188006f25ae9SGregory Neil Shapiro 					if (tTd(95, 8))
188140266059SGregory Neil Shapiro 						sm_dprintf("AUTH auth_ssf: %u\n",
188206f25ae9SGregory Neil Shapiro 							   *ssf);
188306f25ae9SGregory Neil Shapiro 				}
188440266059SGregory Neil Shapiro 
1885da7d7b9cSGregory Neil Shapiro 				protocol = GET_PROTOCOL();
1886da7d7b9cSGregory Neil Shapiro 
188706f25ae9SGregory Neil Shapiro 				/*
188840266059SGregory Neil Shapiro 				**  Only switch to encrypted connection
188906f25ae9SGregory Neil Shapiro 				**  if a security layer has been negotiated
189006f25ae9SGregory Neil Shapiro 				*/
189140266059SGregory Neil Shapiro 
189206f25ae9SGregory Neil Shapiro 				if (ssf != NULL && *ssf > 0)
189306f25ae9SGregory Neil Shapiro 				{
1894af9557fdSGregory Neil Shapiro 					int tmo;
1895af9557fdSGregory Neil Shapiro 
189606f25ae9SGregory Neil Shapiro 					/*
189740266059SGregory Neil Shapiro 					**  Convert I/O layer to use SASL.
189840266059SGregory Neil Shapiro 					**  If the call fails, the connection
189940266059SGregory Neil Shapiro 					**  is aborted.
190006f25ae9SGregory Neil Shapiro 					*/
190140266059SGregory Neil Shapiro 
1902af9557fdSGregory Neil Shapiro 					tmo = TimeOuts.to_datablock * 1000;
190340266059SGregory Neil Shapiro 					if (sfdcsasl(&InChannel, &OutChannel,
1904af9557fdSGregory Neil Shapiro 						     conn, tmo) == 0)
190506f25ae9SGregory Neil Shapiro 					{
190606f25ae9SGregory Neil Shapiro 						/* restart dialogue */
190706f25ae9SGregory Neil Shapiro 						n_helo = 0;
190840266059SGregory Neil Shapiro # if PIPELINING
190940266059SGregory Neil Shapiro 						(void) sm_io_autoflush(InChannel,
191040266059SGregory Neil Shapiro 								       OutChannel);
191140266059SGregory Neil Shapiro # endif /* PIPELINING */
191206f25ae9SGregory Neil Shapiro 					}
191306f25ae9SGregory Neil Shapiro 					else
191406f25ae9SGregory Neil Shapiro 						syserr("503 5.3.3 SASL TLS failed");
191506f25ae9SGregory Neil Shapiro 				}
191640266059SGregory Neil Shapiro 
191740266059SGregory Neil Shapiro 				/* NULL pointer ok since it's our function */
191840266059SGregory Neil Shapiro 				if (LogLevel > 8)
191906f25ae9SGregory Neil Shapiro 					sm_syslog(LOG_INFO, NOQID,
192013bd1963SGregory Neil Shapiro 						  "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d",
192140266059SGregory Neil Shapiro 						  CurSmtpClient,
192240266059SGregory Neil Shapiro 						  shortenstring(user, 128),
192340266059SGregory Neil Shapiro 						  auth_type, *ssf);
192406f25ae9SGregory Neil Shapiro 			}
192506f25ae9SGregory Neil Shapiro 			else if (result == SASL_CONTINUE)
192606f25ae9SGregory Neil Shapiro 			{
19275b0945b5SGregory Neil Shapiro 				SET_AUTH_USER;
19285b0945b5SGregory Neil Shapiro 
192906f25ae9SGregory Neil Shapiro 				len = ENC64LEN(outlen);
193006f25ae9SGregory Neil Shapiro 				out2 = xalloc(len);
193106f25ae9SGregory Neil Shapiro 				result = sasl_encode64(out, outlen, out2, len,
193240266059SGregory Neil Shapiro 						       &out2len);
193306f25ae9SGregory Neil Shapiro 				if (result != SASL_OK)
193406f25ae9SGregory Neil Shapiro 				{
193506f25ae9SGregory Neil Shapiro 					/* correct code? XXX */
193606f25ae9SGregory Neil Shapiro 					/* 454 Temp. authentication failure */
193706f25ae9SGregory Neil Shapiro 					message("454 4.5.4 Internal error: unable to encode64");
193806f25ae9SGregory Neil Shapiro 					if (LogLevel > 5)
193906f25ae9SGregory Neil Shapiro 						sm_syslog(LOG_WARNING, e->e_id,
19409bd497b8SGregory Neil Shapiro 							  "AUTH encode64 error [%d for \"%s\"], relay=%.100s",
19419bd497b8SGregory Neil Shapiro 							  result, out,
19429bd497b8SGregory Neil Shapiro 							  CurSmtpClient);
194306f25ae9SGregory Neil Shapiro 					/* start over? */
194406f25ae9SGregory Neil Shapiro 					authenticating = SASL_NOT_AUTH;
194506f25ae9SGregory Neil Shapiro 				}
194606f25ae9SGregory Neil Shapiro 				else
194706f25ae9SGregory Neil Shapiro 				{
194806f25ae9SGregory Neil Shapiro 					message("334 %s", out2);
194906f25ae9SGregory Neil Shapiro 					if (tTd(95, 2))
195040266059SGregory Neil Shapiro 						sm_dprintf("AUTH continue: msg='%s' len=%u\n",
195106f25ae9SGregory Neil Shapiro 							   out2, out2len);
195206f25ae9SGregory Neil Shapiro 				}
195394c01205SGregory Neil Shapiro # if SASL >= 20000
195494c01205SGregory Neil Shapiro 				sm_free(out2);
19555b0945b5SGregory Neil Shapiro # endif
195606f25ae9SGregory Neil Shapiro 			}
195706f25ae9SGregory Neil Shapiro 			else
195806f25ae9SGregory Neil Shapiro 			{
19595b0945b5SGregory Neil Shapiro 
196094c01205SGregory Neil Shapiro # if SASL >= 20000
19615b0945b5SGregory Neil Shapiro #  define SASLERR sasl_errdetail(conn)
19625b0945b5SGregory Neil Shapiro # else
19635b0945b5SGregory Neil Shapiro #  define SASLERR errstr == NULL ? "" : errstr
19645b0945b5SGregory Neil Shapiro # endif
19655b0945b5SGregory Neil Shapiro #define LOGAUTHFAIL	\
19665b0945b5SGregory Neil Shapiro 	do	\
19675b0945b5SGregory Neil Shapiro 	{	\
19685b0945b5SGregory Neil Shapiro 		SET_AUTH_USER_CONDITIONALLY	\
19695b0945b5SGregory Neil Shapiro 		message("535 5.7.0 authentication failed");	\
19705b0945b5SGregory Neil Shapiro 		if (LogLevel >= 9)	\
19715b0945b5SGregory Neil Shapiro 			sm_syslog(LOG_WARNING, e->e_id,	\
19725b0945b5SGregory Neil Shapiro 				  "AUTH failure (%s): %s (%d) %s%s%.*s, relay=%.100s",	\
19735b0945b5SGregory Neil Shapiro 				  (auth_type != NULL) ? auth_type : "unknown", \
19745b0945b5SGregory Neil Shapiro 				  sasl_errstring(result, NULL, NULL),	\
19755b0945b5SGregory Neil Shapiro 				  result,	\
19765b0945b5SGregory Neil Shapiro 				  SASLERR, \
19775b0945b5SGregory Neil Shapiro 				  LOG_AUTH_FAIL_USER,	\
19785b0945b5SGregory Neil Shapiro 				  CurSmtpClient);	\
19795b0945b5SGregory Neil Shapiro 		RESET_SASLCONN;	\
19805b0945b5SGregory Neil Shapiro 	} while (0)
19815b0945b5SGregory Neil Shapiro 
19825b0945b5SGregory Neil Shapiro 
19835b0945b5SGregory Neil Shapiro 				LOGAUTHFAIL;
198406f25ae9SGregory Neil Shapiro 				authenticating = SASL_NOT_AUTH;
198506f25ae9SGregory Neil Shapiro 			}
198606f25ae9SGregory Neil Shapiro 		}
198706f25ae9SGregory Neil Shapiro 		else
198806f25ae9SGregory Neil Shapiro 		{
198906f25ae9SGregory Neil Shapiro 			/* don't want to do any of this if authenticating */
199006f25ae9SGregory Neil Shapiro #endif /* SASL */
199106f25ae9SGregory Neil Shapiro 
1992c2aa98e2SPeter Wemm 		/* echo command to transcript */
1993c2aa98e2SPeter Wemm 		if (e->e_xfp != NULL)
199440266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
199540266059SGregory Neil Shapiro 					     "<<< %s\n", inp);
1996c2aa98e2SPeter Wemm 
199740266059SGregory Neil Shapiro 		if (LogLevel > 14)
199840266059SGregory Neil Shapiro 			sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
1999c2aa98e2SPeter Wemm 
2000c2aa98e2SPeter Wemm 		/* break off command */
20015b0945b5SGregory Neil Shapiro 		for (p = inp; SM_ISSPACE(*p); p++)
2002c2aa98e2SPeter Wemm 			continue;
2003c2aa98e2SPeter Wemm 		cmd = cmdbuf;
2004c2aa98e2SPeter Wemm 		while (*p != '\0' &&
20055b0945b5SGregory Neil Shapiro 		       !(SM_ISSPACE(*p)) &&
2006d0cef73dSGregory Neil Shapiro 		       cmd < &cmdbuf[sizeof(cmdbuf) - 2])
2007c2aa98e2SPeter Wemm 			*cmd++ = *p++;
2008c2aa98e2SPeter Wemm 		*cmd = '\0';
2009c2aa98e2SPeter Wemm 
2010c2aa98e2SPeter Wemm 		/* throw away leading whitespace */
201140266059SGregory Neil Shapiro 		SKIP_SPACE(p);
2012c2aa98e2SPeter Wemm 
2013c2aa98e2SPeter Wemm 		/* decode command */
201406f25ae9SGregory Neil Shapiro 		for (c = CmdTab; c->cmd_name != NULL; c++)
2015c2aa98e2SPeter Wemm 		{
20162fb4f839SGregory Neil Shapiro 			if (SM_STRCASEEQ(c->cmd_name, cmdbuf))
2017c2aa98e2SPeter Wemm 				break;
2018c2aa98e2SPeter Wemm 		}
2019c2aa98e2SPeter Wemm 
2020c2aa98e2SPeter Wemm 		/* reset errors */
2021c2aa98e2SPeter Wemm 		errno = 0;
2022c2aa98e2SPeter Wemm 
202340266059SGregory Neil Shapiro 		/* check whether a "non-null" command has been used */
202440266059SGregory Neil Shapiro 		switch (c->cmd_code)
202540266059SGregory Neil Shapiro 		{
202640266059SGregory Neil Shapiro #if SASL
202740266059SGregory Neil Shapiro 		  case CMDAUTH:
202840266059SGregory Neil Shapiro 			/* avoid information leak; take first two words? */
202940266059SGregory Neil Shapiro 			q = "AUTH";
203040266059SGregory Neil Shapiro 			break;
203140266059SGregory Neil Shapiro #endif /* SASL */
203240266059SGregory Neil Shapiro 
203340266059SGregory Neil Shapiro 		  case CMDMAIL:
203440266059SGregory Neil Shapiro 		  case CMDEXPN:
203540266059SGregory Neil Shapiro 		  case CMDVRFY:
203640266059SGregory Neil Shapiro 		  case CMDETRN:
203740266059SGregory Neil Shapiro 			lognullconnection = false;
203840266059SGregory Neil Shapiro 			/* FALLTHROUGH */
203940266059SGregory Neil Shapiro 		  default:
204040266059SGregory Neil Shapiro 			q = inp;
204140266059SGregory Neil Shapiro 			break;
204240266059SGregory Neil Shapiro 		}
204340266059SGregory Neil Shapiro 
204440266059SGregory Neil Shapiro 		if (e->e_id == NULL)
204540266059SGregory Neil Shapiro 			sm_setproctitle(true, e, "%s: %.80s",
204640266059SGregory Neil Shapiro 					CurSmtpClient, q);
204740266059SGregory Neil Shapiro 		else
204840266059SGregory Neil Shapiro 			sm_setproctitle(true, e, "%s %s: %.80s",
204940266059SGregory Neil Shapiro 					qid_printname(e),
205040266059SGregory Neil Shapiro 					CurSmtpClient, q);
205140266059SGregory Neil Shapiro 
2052c2aa98e2SPeter Wemm 		/*
2053c2aa98e2SPeter Wemm 		**  Process command.
2054c2aa98e2SPeter Wemm 		**
2055c2aa98e2SPeter Wemm 		**	If we are running as a null server, return 550
205640266059SGregory Neil Shapiro 		**	to almost everything.
2057c2aa98e2SPeter Wemm 		*/
2058c2aa98e2SPeter Wemm 
205906f25ae9SGregory Neil Shapiro 		if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
2060c2aa98e2SPeter Wemm 		{
206106f25ae9SGregory Neil Shapiro 			switch (c->cmd_code)
2062c2aa98e2SPeter Wemm 			{
2063c2aa98e2SPeter Wemm 			  case CMDQUIT:
2064c2aa98e2SPeter Wemm 			  case CMDHELO:
2065c2aa98e2SPeter Wemm 			  case CMDEHLO:
2066c2aa98e2SPeter Wemm 			  case CMDNOOP:
206706f25ae9SGregory Neil Shapiro 			  case CMDRSET:
2068323f6dcbSGregory Neil Shapiro 			  case CMDERROR:
2069c2aa98e2SPeter Wemm 				/* process normally */
2070c2aa98e2SPeter Wemm 				break;
2071c2aa98e2SPeter Wemm 
207206f25ae9SGregory Neil Shapiro 			  case CMDETRN:
207306f25ae9SGregory Neil Shapiro 				if (bitnset(D_ETRNONLY, d_flags) &&
207406f25ae9SGregory Neil Shapiro 				    nullserver == NULL)
207506f25ae9SGregory Neil Shapiro 					break;
207640266059SGregory Neil Shapiro 				DELAY_CONN("ETRN");
207713058a91SGregory Neil Shapiro 				/* FALLTHROUGH */
207806f25ae9SGregory Neil Shapiro 
2079c2aa98e2SPeter Wemm 			  default:
208040266059SGregory Neil Shapiro #if MAXBADCOMMANDS > 0
208140266059SGregory Neil Shapiro 				/* theoretically this could overflow */
208240266059SGregory Neil Shapiro 				if (nullserver != NULL &&
208340266059SGregory Neil Shapiro 				    ++n_badcmds > MAXBADCOMMANDS)
208406f25ae9SGregory Neil Shapiro 				{
208540266059SGregory Neil Shapiro 					message("421 4.7.0 %s Too many bad commands; closing connection",
208640266059SGregory Neil Shapiro 						MyHostName);
208740266059SGregory Neil Shapiro 
208840266059SGregory Neil Shapiro 					/* arrange to ignore send list */
208940266059SGregory Neil Shapiro 					e->e_sendqueue = NULL;
209040266059SGregory Neil Shapiro 					goto doquit;
209106f25ae9SGregory Neil Shapiro 				}
209240266059SGregory Neil Shapiro #endif /* MAXBADCOMMANDS > 0 */
209306f25ae9SGregory Neil Shapiro 				if (nullserver != NULL)
209406f25ae9SGregory Neil Shapiro 				{
209506f25ae9SGregory Neil Shapiro 					if (ISSMTPREPLY(nullserver))
20965b0945b5SGregory Neil Shapiro 					{
20975b0945b5SGregory Neil Shapiro 						/* Can't use ("%s", ...) due to usrerr() requirements */
209806f25ae9SGregory Neil Shapiro 						usrerr(nullserver);
20995b0945b5SGregory Neil Shapiro 					}
210006f25ae9SGregory Neil Shapiro 					else
21015b0945b5SGregory Neil Shapiro 					{
210240266059SGregory Neil Shapiro 						usrerr("550 5.0.0 %s",
210340266059SGregory Neil Shapiro 						       nullserver);
210406f25ae9SGregory Neil Shapiro 					}
21055b0945b5SGregory Neil Shapiro 				}
210606f25ae9SGregory Neil Shapiro 				else
210706f25ae9SGregory Neil Shapiro 					usrerr("452 4.4.5 Insufficient disk space; try again later");
2108c2aa98e2SPeter Wemm 				continue;
2109c2aa98e2SPeter Wemm 			}
2110c2aa98e2SPeter Wemm 		}
2111c2aa98e2SPeter Wemm 
211206f25ae9SGregory Neil Shapiro 		switch (c->cmd_code)
2113c2aa98e2SPeter Wemm 		{
211406f25ae9SGregory Neil Shapiro #if SASL
211506f25ae9SGregory Neil Shapiro 		  case CMDAUTH: /* sasl */
211640266059SGregory Neil Shapiro 			DELAY_CONN("AUTH");
211740266059SGregory Neil Shapiro 			if (!sasl_ok || n_mechs <= 0)
211806f25ae9SGregory Neil Shapiro 			{
211906f25ae9SGregory Neil Shapiro 				message("503 5.3.3 AUTH not available");
212006f25ae9SGregory Neil Shapiro 				break;
212106f25ae9SGregory Neil Shapiro 			}
2122*d39bd2c1SGregory Neil Shapiro 			if (auth_active)
212306f25ae9SGregory Neil Shapiro 			{
212406f25ae9SGregory Neil Shapiro 				message("503 5.5.0 Already Authenticated");
212506f25ae9SGregory Neil Shapiro 				break;
212606f25ae9SGregory Neil Shapiro 			}
212740266059SGregory Neil Shapiro 			if (smtp.sm_gotmail)
212806f25ae9SGregory Neil Shapiro 			{
212906f25ae9SGregory Neil Shapiro 				message("503 5.5.0 AUTH not permitted during a mail transaction");
213006f25ae9SGregory Neil Shapiro 				break;
213106f25ae9SGregory Neil Shapiro 			}
213206f25ae9SGregory Neil Shapiro 			if (tempfail)
213306f25ae9SGregory Neil Shapiro 			{
213406f25ae9SGregory Neil Shapiro 				if (LogLevel > 9)
213506f25ae9SGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
213613bd1963SGregory Neil Shapiro 						  "SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)",
213706f25ae9SGregory Neil Shapiro 						  p, CurSmtpClient);
2138e92d3f3fSGregory Neil Shapiro 				usrerr("454 4.3.0 Please try again later");
213906f25ae9SGregory Neil Shapiro 				break;
214006f25ae9SGregory Neil Shapiro 			}
214106f25ae9SGregory Neil Shapiro 
214240266059SGregory Neil Shapiro 			ismore = false;
214306f25ae9SGregory Neil Shapiro 
214406f25ae9SGregory Neil Shapiro 			/* crude way to avoid crack attempts */
2145e92d3f3fSGregory Neil Shapiro 			STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1,
2146e92d3f3fSGregory Neil Shapiro 							true, "AUTH", e));
214706f25ae9SGregory Neil Shapiro 
214840266059SGregory Neil Shapiro 			/* make sure mechanism (p) is a valid string */
214906f25ae9SGregory Neil Shapiro 			for (q = p; *q != '\0' && isascii(*q); q++)
215006f25ae9SGregory Neil Shapiro 			{
215106f25ae9SGregory Neil Shapiro 				if (isspace(*q))
215206f25ae9SGregory Neil Shapiro 				{
215306f25ae9SGregory Neil Shapiro 					*q = '\0';
21545b0945b5SGregory Neil Shapiro 					while (*++q != '\0' && SM_ISSPACE(*q))
215506f25ae9SGregory Neil Shapiro 						continue;
215606f25ae9SGregory Neil Shapiro 					*(q - 1) = '\0';
215706f25ae9SGregory Neil Shapiro 					ismore = (*q != '\0');
215806f25ae9SGregory Neil Shapiro 					break;
215906f25ae9SGregory Neil Shapiro 				}
216006f25ae9SGregory Neil Shapiro 			}
216106f25ae9SGregory Neil Shapiro 
216294c01205SGregory Neil Shapiro 			if (*p == '\0')
216394c01205SGregory Neil Shapiro 			{
216494c01205SGregory Neil Shapiro 				message("501 5.5.2 AUTH mechanism must be specified");
216594c01205SGregory Neil Shapiro 				break;
216694c01205SGregory Neil Shapiro 			}
216794c01205SGregory Neil Shapiro 
216806f25ae9SGregory Neil Shapiro 			/* check whether mechanism is available */
216906f25ae9SGregory Neil Shapiro 			if (iteminlist(p, mechlist, " ") == NULL)
217006f25ae9SGregory Neil Shapiro 			{
217194c01205SGregory Neil Shapiro 				message("504 5.3.3 AUTH mechanism %.32s not available",
217206f25ae9SGregory Neil Shapiro 					p);
217306f25ae9SGregory Neil Shapiro 				break;
217406f25ae9SGregory Neil Shapiro 			}
217506f25ae9SGregory Neil Shapiro 
2176ffb83623SGregory Neil Shapiro 			/*
2177ffb83623SGregory Neil Shapiro 			**  RFC 2554 4.
2178ffb83623SGregory Neil Shapiro 			**  Unlike a zero-length client answer to a
2179ffb83623SGregory Neil Shapiro 			**  334 reply, a zero- length initial response
2180ffb83623SGregory Neil Shapiro 			**  is sent as a single equals sign ("=").
2181ffb83623SGregory Neil Shapiro 			*/
2182ffb83623SGregory Neil Shapiro 
2183ffb83623SGregory Neil Shapiro 			if (ismore && *q == '=' && *(q + 1) == '\0')
2184ffb83623SGregory Neil Shapiro 			{
2185ffb83623SGregory Neil Shapiro 				/* will be free()d, don't use in=""; */
2186ffb83623SGregory Neil Shapiro 				in = xalloc(1);
2187ffb83623SGregory Neil Shapiro 				*in = '\0';
2188ffb83623SGregory Neil Shapiro 				inlen = 0;
2189ffb83623SGregory Neil Shapiro 			}
2190ffb83623SGregory Neil Shapiro 			else if (ismore)
219106f25ae9SGregory Neil Shapiro 			{
219206f25ae9SGregory Neil Shapiro 				/* could this be shorter? XXX */
219394c01205SGregory Neil Shapiro # if SASL >= 20000
219494c01205SGregory Neil Shapiro 				in = xalloc(strlen(q) + 1);
219594c01205SGregory Neil Shapiro 				result = sasl_decode64(q, strlen(q), in,
219694c01205SGregory Neil Shapiro 						       strlen(q), &inlen);
219794c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
219840266059SGregory Neil Shapiro 				in = sm_rpool_malloc(e->e_rpool, strlen(q));
219906f25ae9SGregory Neil Shapiro 				result = sasl_decode64(q, strlen(q), in,
220040266059SGregory Neil Shapiro 						       &inlen);
220194c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
22025b0945b5SGregory Neil Shapiro 
220306f25ae9SGregory Neil Shapiro 				if (result != SASL_OK)
220406f25ae9SGregory Neil Shapiro 				{
220506f25ae9SGregory Neil Shapiro 					message("501 5.5.4 cannot BASE64 decode '%s'",
220606f25ae9SGregory Neil Shapiro 						q);
220706f25ae9SGregory Neil Shapiro 					if (LogLevel > 5)
220806f25ae9SGregory Neil Shapiro 						sm_syslog(LOG_WARNING, e->e_id,
22099bd497b8SGregory Neil Shapiro 							  "AUTH decode64 error [%d for \"%s\"], relay=%.100s",
22109bd497b8SGregory Neil Shapiro 							  result, q,
22119bd497b8SGregory Neil Shapiro 							  CurSmtpClient);
221206f25ae9SGregory Neil Shapiro 					/* start over? */
221306f25ae9SGregory Neil Shapiro 					authenticating = SASL_NOT_AUTH;
221494c01205SGregory Neil Shapiro # if SASL >= 20000
221594c01205SGregory Neil Shapiro 					sm_free(in);
22165b0945b5SGregory Neil Shapiro # endif
221706f25ae9SGregory Neil Shapiro 					in = NULL;
221806f25ae9SGregory Neil Shapiro 					inlen = 0;
221906f25ae9SGregory Neil Shapiro 					break;
222006f25ae9SGregory Neil Shapiro 				}
22215b0945b5SGregory Neil Shapiro 				SET_AUTH_USER_TMP(in, inlen);
222206f25ae9SGregory Neil Shapiro 			}
222306f25ae9SGregory Neil Shapiro 			else
222406f25ae9SGregory Neil Shapiro 			{
222506f25ae9SGregory Neil Shapiro 				in = NULL;
222606f25ae9SGregory Neil Shapiro 				inlen = 0;
222706f25ae9SGregory Neil Shapiro 			}
222806f25ae9SGregory Neil Shapiro 
222906f25ae9SGregory Neil Shapiro 			/* see if that auth type exists */
223094c01205SGregory Neil Shapiro # if SASL >= 20000
223194c01205SGregory Neil Shapiro 			result = sasl_server_start(conn, p, in, inlen,
223294c01205SGregory Neil Shapiro 						   &out, &outlen);
22335b0945b5SGregory Neil Shapiro 			SM_FREE(in);
223494c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
223506f25ae9SGregory Neil Shapiro 			result = sasl_server_start(conn, p, in, inlen,
223606f25ae9SGregory Neil Shapiro 						   &out, &outlen, &errstr);
223794c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
223806f25ae9SGregory Neil Shapiro 
22395b0945b5SGregory Neil Shapiro 			if (p != NULL)
22405b0945b5SGregory Neil Shapiro 				auth_type = newstr(p);
224106f25ae9SGregory Neil Shapiro 			if (result != SASL_OK && result != SASL_CONTINUE)
224206f25ae9SGregory Neil Shapiro 			{
22435b0945b5SGregory Neil Shapiro 				LOGAUTHFAIL;
224406f25ae9SGregory Neil Shapiro 				break;
224506f25ae9SGregory Neil Shapiro 			}
224606f25ae9SGregory Neil Shapiro 
224706f25ae9SGregory Neil Shapiro 			if (result == SASL_OK)
224806f25ae9SGregory Neil Shapiro 			{
224906f25ae9SGregory Neil Shapiro 				/* ugly, but same code */
225006f25ae9SGregory Neil Shapiro 				goto authenticated;
225106f25ae9SGregory Neil Shapiro 				/* authenticated by the initial response */
225206f25ae9SGregory Neil Shapiro 			}
225306f25ae9SGregory Neil Shapiro 
22545b0945b5SGregory Neil Shapiro 			SET_AUTH_USER;
22555b0945b5SGregory Neil Shapiro 
225606f25ae9SGregory Neil Shapiro 			/* len is at least 2 */
225706f25ae9SGregory Neil Shapiro 			len = ENC64LEN(outlen);
225806f25ae9SGregory Neil Shapiro 			out2 = xalloc(len);
225906f25ae9SGregory Neil Shapiro 			result = sasl_encode64(out, outlen, out2, len,
226040266059SGregory Neil Shapiro 					       &out2len);
226106f25ae9SGregory Neil Shapiro 
226206f25ae9SGregory Neil Shapiro 			if (result != SASL_OK)
226306f25ae9SGregory Neil Shapiro 			{
226406f25ae9SGregory Neil Shapiro 				message("454 4.5.4 Temporary authentication failure");
226506f25ae9SGregory Neil Shapiro 				if (LogLevel > 5)
226606f25ae9SGregory Neil Shapiro 					sm_syslog(LOG_WARNING, e->e_id,
226740266059SGregory Neil Shapiro 						  "AUTH encode64 error [%d for \"%s\"]",
226806f25ae9SGregory Neil Shapiro 						  result, out);
226906f25ae9SGregory Neil Shapiro 
227006f25ae9SGregory Neil Shapiro 				/* start over? */
227106f25ae9SGregory Neil Shapiro 				authenticating = SASL_NOT_AUTH;
2272a7ec597cSGregory Neil Shapiro 				RESET_SASLCONN;
227306f25ae9SGregory Neil Shapiro 			}
227406f25ae9SGregory Neil Shapiro 			else
227506f25ae9SGregory Neil Shapiro 			{
227606f25ae9SGregory Neil Shapiro 				message("334 %s", out2);
227706f25ae9SGregory Neil Shapiro 				authenticating = SASL_PROC_AUTH;
227806f25ae9SGregory Neil Shapiro 			}
227994c01205SGregory Neil Shapiro # if SASL >= 20000
228094c01205SGregory Neil Shapiro 			sm_free(out2);
22815b0945b5SGregory Neil Shapiro # endif
228206f25ae9SGregory Neil Shapiro 			break;
228306f25ae9SGregory Neil Shapiro #endif /* SASL */
228406f25ae9SGregory Neil Shapiro 
228506f25ae9SGregory Neil Shapiro #if STARTTLS
228606f25ae9SGregory Neil Shapiro 		  case CMDSTLS: /* starttls */
228740266059SGregory Neil Shapiro 			DELAY_CONN("STARTTLS");
228806f25ae9SGregory Neil Shapiro 			if (*p != '\0')
228906f25ae9SGregory Neil Shapiro 			{
229006f25ae9SGregory Neil Shapiro 				message("501 5.5.2 Syntax error (no parameters allowed)");
229106f25ae9SGregory Neil Shapiro 				break;
229206f25ae9SGregory Neil Shapiro 			}
229340266059SGregory Neil Shapiro 			if (!bitset(SRV_OFFER_TLS, features))
229406f25ae9SGregory Neil Shapiro 			{
229506f25ae9SGregory Neil Shapiro 				message("503 5.5.0 TLS not available");
229606f25ae9SGregory Neil Shapiro 				break;
229706f25ae9SGregory Neil Shapiro 			}
22985b0945b5SGregory Neil Shapiro   starttls:
22998774250cSGregory Neil Shapiro 			if (!tls_ok_srv)
230006f25ae9SGregory Neil Shapiro 			{
230106f25ae9SGregory Neil Shapiro 				message("454 4.3.3 TLS not available after start");
230206f25ae9SGregory Neil Shapiro 				break;
230306f25ae9SGregory Neil Shapiro 			}
230440266059SGregory Neil Shapiro 			if (smtp.sm_gotmail)
230506f25ae9SGregory Neil Shapiro 			{
230606f25ae9SGregory Neil Shapiro 				message("503 5.5.0 TLS not permitted during a mail transaction");
230706f25ae9SGregory Neil Shapiro 				break;
230806f25ae9SGregory Neil Shapiro 			}
230906f25ae9SGregory Neil Shapiro 			if (tempfail)
231006f25ae9SGregory Neil Shapiro 			{
231106f25ae9SGregory Neil Shapiro 				if (LogLevel > 9)
231206f25ae9SGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
231313bd1963SGregory Neil Shapiro 						  "SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)",
231406f25ae9SGregory Neil Shapiro 						  p, CurSmtpClient);
2315e92d3f3fSGregory Neil Shapiro 				usrerr("454 4.7.0 Please try again later");
231606f25ae9SGregory Neil Shapiro 				break;
231706f25ae9SGregory Neil Shapiro 			}
23185b0945b5SGregory Neil Shapiro 			if (!TLS_set_engine(SSLEngine, false))
23196f9c8e5bSGregory Neil Shapiro 			{
23206f9c8e5bSGregory Neil Shapiro 				sm_syslog(LOG_ERR, NOQID,
23215b0945b5SGregory Neil Shapiro 					  "STARTTLS=server, engine=%s, TLS_set_engine=failed",
23225b0945b5SGregory Neil Shapiro 					  SSLEngine);
23236f9c8e5bSGregory Neil Shapiro 				tls_ok_srv = false;
23246f9c8e5bSGregory Neil Shapiro 				message("454 4.3.3 TLS not available right now");
23256f9c8e5bSGregory Neil Shapiro 				break;
23266f9c8e5bSGregory Neil Shapiro 			}
232706f25ae9SGregory Neil Shapiro # if TLS_NO_RSA
232806f25ae9SGregory Neil Shapiro 			/*
232906f25ae9SGregory Neil Shapiro 			**  XXX do we need a temp key ?
233006f25ae9SGregory Neil Shapiro 			*/
23315b0945b5SGregory Neil Shapiro # endif
233240266059SGregory Neil Shapiro 
233340266059SGregory Neil Shapiro # if TLS_VRFY_PER_CTX
233440266059SGregory Neil Shapiro 			/*
233540266059SGregory Neil Shapiro 			**  Note: this sets the verification globally
233640266059SGregory Neil Shapiro 			**  (per SSL_CTX)
233740266059SGregory Neil Shapiro 			**  it's ok since it applies only to one transaction
233840266059SGregory Neil Shapiro 			*/
233940266059SGregory Neil Shapiro 
234040266059SGregory Neil Shapiro 			TLS_VERIFY_CLIENT();
234140266059SGregory Neil Shapiro # endif /* TLS_VRFY_PER_CTX */
234240266059SGregory Neil Shapiro 
23435b0945b5SGregory Neil Shapiro #define SMTLSFAILED				\
23445b0945b5SGregory Neil Shapiro 	do {					\
23455b0945b5SGregory Neil Shapiro 		SM_SSL_FREE(srv_ssl);		\
23465b0945b5SGregory Neil Shapiro 		goto tls_done;			\
23475b0945b5SGregory Neil Shapiro 	} while (0)
23485b0945b5SGregory Neil Shapiro 
234906f25ae9SGregory Neil Shapiro 			if (srv_ssl != NULL)
235006f25ae9SGregory Neil Shapiro 				SSL_clear(srv_ssl);
235106f25ae9SGregory Neil Shapiro 			else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
235206f25ae9SGregory Neil Shapiro 			{
235306f25ae9SGregory Neil Shapiro 				message("454 4.3.3 TLS not available: error generating SSL handle");
23545b0945b5SGregory Neil Shapiro 				tlslogerr(LOG_WARNING, 8, "server");
235540266059SGregory Neil Shapiro 				goto tls_done;
235606f25ae9SGregory Neil Shapiro 			}
2357*d39bd2c1SGregory Neil Shapiro # if DANE
2358*d39bd2c1SGregory Neil Shapiro 			tlsi_ctx.tlsi_dvc.dane_vrfy_dane_enabled = false;
2359*d39bd2c1SGregory Neil Shapiro 			tlsi_ctx.tlsi_dvc.dane_vrfy_chk = DANE_NEVER;
2360*d39bd2c1SGregory Neil Shapiro # endif
23612fb4f839SGregory Neil Shapiro 			if (get_tls_se_features(e, srv_ssl, &tlsi_ctx, true)
23622fb4f839SGregory Neil Shapiro 			    != EX_OK)
2363da7d7b9cSGregory Neil Shapiro 			{
23642fb4f839SGregory Neil Shapiro 				/* do not offer too much info to client */
2365*d39bd2c1SGregory Neil Shapiro 				message("454 4.3.3 TLS currently not available");
23665b0945b5SGregory Neil Shapiro 				SMTLSFAILED;
23675b0945b5SGregory Neil Shapiro 			}
23685b0945b5SGregory Neil Shapiro 			r = SSL_set_ex_data(srv_ssl, TLSsslidx, &tlsi_ctx);
23695b0945b5SGregory Neil Shapiro 			if (0 == r)
23705b0945b5SGregory Neil Shapiro 			{
23715b0945b5SGregory Neil Shapiro 				if (LogLevel > 5)
23725b0945b5SGregory Neil Shapiro 				{
23735b0945b5SGregory Neil Shapiro 					sm_syslog(LOG_ERR, NOQID,
2374*d39bd2c1SGregory Neil Shapiro 						"STARTTLS=server, error: SSL_set_ex_data failed=%d, TLSsslidx=%d",
2375*d39bd2c1SGregory Neil Shapiro 						r, TLSsslidx);
23765b0945b5SGregory Neil Shapiro 					tlslogerr(LOG_WARNING, 9, "server");
23775b0945b5SGregory Neil Shapiro 				}
23785b0945b5SGregory Neil Shapiro 				SMTLSFAILED;
2379da7d7b9cSGregory Neil Shapiro 			}
2380da7d7b9cSGregory Neil Shapiro 
238140266059SGregory Neil Shapiro # if !TLS_VRFY_PER_CTX
238240266059SGregory Neil Shapiro 			/*
238340266059SGregory Neil Shapiro 			**  this could be used if it were possible to set
238440266059SGregory Neil Shapiro 			**  verification per SSL (connection)
238540266059SGregory Neil Shapiro 			**  not just per SSL_CTX (global)
238640266059SGregory Neil Shapiro 			*/
238740266059SGregory Neil Shapiro 
238840266059SGregory Neil Shapiro 			TLS_VERIFY_CLIENT();
238940266059SGregory Neil Shapiro # endif /* !TLS_VRFY_PER_CTX */
239040266059SGregory Neil Shapiro 
239140266059SGregory Neil Shapiro 			rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
239240266059SGregory Neil Shapiro 			wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
239340266059SGregory Neil Shapiro 
239442e5d165SGregory Neil Shapiro 			if (rfd < 0 || wfd < 0 ||
239542e5d165SGregory Neil Shapiro 			    SSL_set_rfd(srv_ssl, rfd) <= 0 ||
239642e5d165SGregory Neil Shapiro 			    SSL_set_wfd(srv_ssl, wfd) <= 0)
239706f25ae9SGregory Neil Shapiro 			{
239806f25ae9SGregory Neil Shapiro 				message("454 4.3.3 TLS not available: error set fd");
23995b0945b5SGregory Neil Shapiro 				SMTLSFAILED;
240006f25ae9SGregory Neil Shapiro 			}
240140266059SGregory Neil Shapiro 			if (!smtps)
240206f25ae9SGregory Neil Shapiro 				message("220 2.0.0 Ready to start TLS");
240340266059SGregory Neil Shapiro # if PIPELINING
240440266059SGregory Neil Shapiro 			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
24055b0945b5SGregory Neil Shapiro # endif
240640266059SGregory Neil Shapiro 
240706f25ae9SGregory Neil Shapiro 			SSL_set_accept_state(srv_ssl);
240806f25ae9SGregory Neil Shapiro 
240940266059SGregory Neil Shapiro 			tlsstart = curtime();
241006f25ae9SGregory Neil Shapiro 
24115b0945b5SGregory Neil Shapiro 			ssl_err = SSL_ERROR_WANT_READ;
24125b0945b5SGregory Neil Shapiro 			save_errno = 0;
24135b0945b5SGregory Neil Shapiro 			do
24145b0945b5SGregory Neil Shapiro 			{
24155b0945b5SGregory Neil Shapiro 				tlsret = tls_retry(srv_ssl, rfd, wfd, tlsstart,
24164e4196cbSGregory Neil Shapiro 						TimeOuts.to_starttls, ssl_err,
24174e4196cbSGregory Neil Shapiro 						"server");
24185b0945b5SGregory Neil Shapiro 				if (tlsret <= 0)
24195b0945b5SGregory Neil Shapiro 				{
242006f25ae9SGregory Neil Shapiro 					if (LogLevel > 5)
242106f25ae9SGregory Neil Shapiro 					{
2422ba00ec3dSGregory Neil Shapiro 						unsigned long l;
2423ba00ec3dSGregory Neil Shapiro 						const char *sr;
2424ba00ec3dSGregory Neil Shapiro 
2425ba00ec3dSGregory Neil Shapiro 						l = ERR_peek_error();
2426ba00ec3dSGregory Neil Shapiro 						sr = ERR_reason_error_string(l);
24275b0945b5SGregory Neil Shapiro 
242840266059SGregory Neil Shapiro 						sm_syslog(LOG_WARNING, NOQID,
2429ba00ec3dSGregory Neil Shapiro 							  "STARTTLS=server, error: accept failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d, relay=%.100s",
2430ba00ec3dSGregory Neil Shapiro 							  r, sr == NULL ? "unknown"
2431ba00ec3dSGregory Neil Shapiro 									: sr,
24325b0945b5SGregory Neil Shapiro 							  ssl_err, save_errno,
24335b0945b5SGregory Neil Shapiro 							  tlsret, CurSmtpClient);
24345b0945b5SGregory Neil Shapiro 						tlslogerr(LOG_WARNING, 9, "server");
243506f25ae9SGregory Neil Shapiro 					}
243640266059SGregory Neil Shapiro 					tls_ok_srv = false;
24375b0945b5SGregory Neil Shapiro 					SM_SSL_FREE(srv_ssl);
243806f25ae9SGregory Neil Shapiro 
243906f25ae9SGregory Neil Shapiro 					/*
244006f25ae9SGregory Neil Shapiro 					**  according to the next draft of
24415b0945b5SGregory Neil Shapiro 					**  RFC 2487 the connection should
24425b0945b5SGregory Neil Shapiro 					**  be dropped
24435b0945b5SGregory Neil Shapiro 					**
24445b0945b5SGregory Neil Shapiro 					**  arrange to ignore any current
24455b0945b5SGregory Neil Shapiro 					**  send list
244606f25ae9SGregory Neil Shapiro 					*/
244706f25ae9SGregory Neil Shapiro 
244806f25ae9SGregory Neil Shapiro 					e->e_sendqueue = NULL;
244906f25ae9SGregory Neil Shapiro 					goto doquit;
245006f25ae9SGregory Neil Shapiro 				}
245106f25ae9SGregory Neil Shapiro 
24525b0945b5SGregory Neil Shapiro 				r = SSL_accept(srv_ssl);
24535b0945b5SGregory Neil Shapiro 				save_errno = 0;
24545b0945b5SGregory Neil Shapiro 				if (r <= 0)
24555b0945b5SGregory Neil Shapiro 					ssl_err = SSL_get_error(srv_ssl, r);
24565b0945b5SGregory Neil Shapiro 			} while (r <= 0);
24575b0945b5SGregory Neil Shapiro 
245806f25ae9SGregory Neil Shapiro 			/* ignore return code for now, it's in {verify} */
245940266059SGregory Neil Shapiro 			(void) tls_get_info(srv_ssl, true,
246040266059SGregory Neil Shapiro 					    CurSmtpClient,
246140266059SGregory Neil Shapiro 					    &BlankEnvelope.e_macro,
246240266059SGregory Neil Shapiro 					    bitset(SRV_VRFY_CLT, features));
246306f25ae9SGregory Neil Shapiro 
246406f25ae9SGregory Neil Shapiro 			/*
246506f25ae9SGregory Neil Shapiro 			**  call Stls_client to find out whether
246606f25ae9SGregory Neil Shapiro 			**  to accept the connection from the client
246706f25ae9SGregory Neil Shapiro 			*/
246806f25ae9SGregory Neil Shapiro 
246906f25ae9SGregory Neil Shapiro 			saveQuickAbort = QuickAbort;
247006f25ae9SGregory Neil Shapiro 			saveSuprErrs = SuprErrs;
247140266059SGregory Neil Shapiro 			SuprErrs = true;
247240266059SGregory Neil Shapiro 			QuickAbort = false;
247306f25ae9SGregory Neil Shapiro 			if (rscheck("tls_client",
247440266059SGregory Neil Shapiro 				     macvalue(macid("{verify}"), e),
2475959366dcSGregory Neil Shapiro 				     "STARTTLS", e,
2476959366dcSGregory Neil Shapiro 				     RSF_RMCOMM|RSF_COUNT,
2477da7d7b9cSGregory Neil Shapiro 				     5, NULL, NOQID, NULL, NULL) != EX_OK ||
247840266059SGregory Neil Shapiro 			    Errors > 0)
247906f25ae9SGregory Neil Shapiro 			{
248006f25ae9SGregory Neil Shapiro 				extern char MsgBuf[];
248106f25ae9SGregory Neil Shapiro 
248206f25ae9SGregory Neil Shapiro 				if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
248306f25ae9SGregory Neil Shapiro 					nullserver = newstr(MsgBuf);
248406f25ae9SGregory Neil Shapiro 				else
248506f25ae9SGregory Neil Shapiro 					nullserver = "503 5.7.0 Authentication required.";
248606f25ae9SGregory Neil Shapiro 			}
248706f25ae9SGregory Neil Shapiro 			QuickAbort = saveQuickAbort;
248806f25ae9SGregory Neil Shapiro 			SuprErrs = saveSuprErrs;
248906f25ae9SGregory Neil Shapiro 
249040266059SGregory Neil Shapiro 			tls_ok_srv = false;	/* don't offer STARTTLS again */
24912fb4f839SGregory Neil Shapiro 			first = true;
249206f25ae9SGregory Neil Shapiro 			n_helo = 0;
249306f25ae9SGregory Neil Shapiro # if SASL
249406f25ae9SGregory Neil Shapiro 			if (sasl_ok)
249506f25ae9SGregory Neil Shapiro 			{
2496e92d3f3fSGregory Neil Shapiro 				int cipher_bits;
2497e92d3f3fSGregory Neil Shapiro 				bool verified;
2498e92d3f3fSGregory Neil Shapiro 				char *s, *v, *c;
249906f25ae9SGregory Neil Shapiro 
250040266059SGregory Neil Shapiro 				s = macvalue(macid("{cipher_bits}"), e);
2501e92d3f3fSGregory Neil Shapiro 				v = macvalue(macid("{verify}"), e);
2502e92d3f3fSGregory Neil Shapiro 				c = macvalue(macid("{cert_subject}"), e);
2503e92d3f3fSGregory Neil Shapiro 				verified = (v != NULL && strcmp(v, "OK") == 0);
2504e92d3f3fSGregory Neil Shapiro 				if (s != NULL && (cipher_bits = atoi(s)) > 0)
250594c01205SGregory Neil Shapiro 				{
2506e92d3f3fSGregory Neil Shapiro #  if SASL >= 20000
2507e92d3f3fSGregory Neil Shapiro 					ext_ssf = cipher_bits;
2508e92d3f3fSGregory Neil Shapiro 					auth_id = verified ? c : NULL;
2509e92d3f3fSGregory Neil Shapiro 					sasl_ok = ((sasl_setprop(conn,
2510e92d3f3fSGregory Neil Shapiro 							SASL_SSF_EXTERNAL,
251194c01205SGregory Neil Shapiro 							&ext_ssf) == SASL_OK) &&
2512e92d3f3fSGregory Neil Shapiro 						   (sasl_setprop(conn,
2513e92d3f3fSGregory Neil Shapiro 							SASL_AUTH_EXTERNAL,
251494c01205SGregory Neil Shapiro 							auth_id) == SASL_OK));
251594c01205SGregory Neil Shapiro #  else /* SASL >= 20000 */
2516e92d3f3fSGregory Neil Shapiro 					ext_ssf.ssf = cipher_bits;
2517e92d3f3fSGregory Neil Shapiro 					ext_ssf.auth_id = verified ? c : NULL;
2518e92d3f3fSGregory Neil Shapiro 					sasl_ok = sasl_setprop(conn,
2519e92d3f3fSGregory Neil Shapiro 							SASL_SSF_EXTERNAL,
252006f25ae9SGregory Neil Shapiro 							&ext_ssf) == SASL_OK;
252194c01205SGregory Neil Shapiro #  endif /* SASL >= 20000 */
252206f25ae9SGregory Neil Shapiro 					mechlist = NULL;
252306f25ae9SGregory Neil Shapiro 					if (sasl_ok)
252406f25ae9SGregory Neil Shapiro 						n_mechs = saslmechs(conn,
252506f25ae9SGregory Neil Shapiro 								    &mechlist);
252606f25ae9SGregory Neil Shapiro 				}
252706f25ae9SGregory Neil Shapiro 			}
252806f25ae9SGregory Neil Shapiro # endif /* SASL */
252906f25ae9SGregory Neil Shapiro 
253006f25ae9SGregory Neil Shapiro 			/* switch to secure connection */
253140266059SGregory Neil Shapiro 			if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
253240266059SGregory Neil Shapiro 			{
253340266059SGregory Neil Shapiro 				tls_active = true;
253440266059SGregory Neil Shapiro # if PIPELINING
253540266059SGregory Neil Shapiro 				(void) sm_io_autoflush(InChannel, OutChannel);
25365b0945b5SGregory Neil Shapiro # endif
253740266059SGregory Neil Shapiro 			}
253806f25ae9SGregory Neil Shapiro 			else
253906f25ae9SGregory Neil Shapiro 			{
254006f25ae9SGregory Neil Shapiro 				/*
254106f25ae9SGregory Neil Shapiro 				**  XXX this is an internal error
254206f25ae9SGregory Neil Shapiro 				**  how to deal with it?
254306f25ae9SGregory Neil Shapiro 				**  we can't generate an error message
254406f25ae9SGregory Neil Shapiro 				**  since the other side switched to an
254506f25ae9SGregory Neil Shapiro 				**  encrypted layer, but we could not...
254606f25ae9SGregory Neil Shapiro 				**  just "hang up"?
254706f25ae9SGregory Neil Shapiro 				*/
254840266059SGregory Neil Shapiro 
254906f25ae9SGregory Neil Shapiro 				nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
255040266059SGregory Neil Shapiro 				syserr("STARTTLS: can't switch to encrypted layer");
255106f25ae9SGregory Neil Shapiro 			}
255240266059SGregory Neil Shapiro 		  tls_done:
255340266059SGregory Neil Shapiro 			if (smtps)
255440266059SGregory Neil Shapiro 			{
255540266059SGregory Neil Shapiro 				if (tls_active)
255640266059SGregory Neil Shapiro 					goto greeting;
255740266059SGregory Neil Shapiro 				else
255840266059SGregory Neil Shapiro 					goto doquit;
255940266059SGregory Neil Shapiro 			}
256006f25ae9SGregory Neil Shapiro 			break;
256106f25ae9SGregory Neil Shapiro #endif /* STARTTLS */
256206f25ae9SGregory Neil Shapiro 
2563c2aa98e2SPeter Wemm 		  case CMDHELO:		/* hello -- introduce yourself */
2564c2aa98e2SPeter Wemm 		  case CMDEHLO:		/* extended hello */
256540266059SGregory Neil Shapiro 			DELAY_CONN("EHLO");
256606f25ae9SGregory Neil Shapiro 			if (c->cmd_code == CMDEHLO)
2567c2aa98e2SPeter Wemm 			{
2568da7d7b9cSGregory Neil Shapiro 				protocol = GET_PROTOCOL();
2569c2aa98e2SPeter Wemm 				SmtpPhase = "server EHLO";
2570c2aa98e2SPeter Wemm 			}
2571c2aa98e2SPeter Wemm 			else
2572c2aa98e2SPeter Wemm 			{
2573c2aa98e2SPeter Wemm 				protocol = "SMTP";
2574c2aa98e2SPeter Wemm 				SmtpPhase = "server HELO";
2575c2aa98e2SPeter Wemm 			}
2576c2aa98e2SPeter Wemm 
2577c2aa98e2SPeter Wemm 			/* avoid denial-of-service */
2578e92d3f3fSGregory Neil Shapiro 			STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
2579e92d3f3fSGregory Neil Shapiro 							true, "HELO/EHLO", e));
2580c2aa98e2SPeter Wemm 
2581*d39bd2c1SGregory Neil Shapiro 			/*
2582*d39bd2c1SGregory Neil Shapiro 			**  Despite the fact that the name indicates this
2583*d39bd2c1SGregory Neil Shapiro 			**  a PIPELINE related feature, do not enclose
2584*d39bd2c1SGregory Neil Shapiro 			**  it in #if PIPELINING so we can protect SMTP
2585*d39bd2c1SGregory Neil Shapiro 			**  servers not compiled with PIPELINE support
2586*d39bd2c1SGregory Neil Shapiro 			**  from transaction stuffing.
2587*d39bd2c1SGregory Neil Shapiro 			*/
2588*d39bd2c1SGregory Neil Shapiro 
2589*d39bd2c1SGregory Neil Shapiro 			/* check if data is on the socket before the EHLO reply */
2590*d39bd2c1SGregory Neil Shapiro 			if (bitset(SRV_BAD_PIPELINE, features) &&
2591*d39bd2c1SGregory Neil Shapiro 			    sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
2592*d39bd2c1SGregory Neil Shapiro 			{
2593*d39bd2c1SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
2594*d39bd2c1SGregory Neil Shapiro 					"rejecting %s from %s [%s] due to traffic before response",
2595*d39bd2c1SGregory Neil Shapiro 					SmtpPhase, CurHostName,
2596*d39bd2c1SGregory Neil Shapiro 					anynet_ntoa(&RealHostAddr));
2597*d39bd2c1SGregory Neil Shapiro 				usrerr("554 5.5.0 SMTP protocol error");
2598*d39bd2c1SGregory Neil Shapiro 				nullserver = "Command rejected";
2599*d39bd2c1SGregory Neil Shapiro #if MILTER
2600*d39bd2c1SGregory Neil Shapiro 				smtp.sm_milterize = false;
2601*d39bd2c1SGregory Neil Shapiro #endif
2602*d39bd2c1SGregory Neil Shapiro 				break;
2603*d39bd2c1SGregory Neil Shapiro 			}
2604*d39bd2c1SGregory Neil Shapiro 
260540266059SGregory Neil Shapiro #if 0
260640266059SGregory Neil Shapiro 			/* RFC2821 4.1.4 allows duplicate HELO/EHLO */
2607c2aa98e2SPeter Wemm 			/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
2608c2aa98e2SPeter Wemm 			if (gothello)
2609c2aa98e2SPeter Wemm 			{
2610c2aa98e2SPeter Wemm 				usrerr("503 %s Duplicate HELO/EHLO",
2611c2aa98e2SPeter Wemm 				       MyHostName);
2612c2aa98e2SPeter Wemm 				break;
2613c2aa98e2SPeter Wemm 			}
261440266059SGregory Neil Shapiro #endif /* 0 */
2615c2aa98e2SPeter Wemm 
2616c2aa98e2SPeter Wemm 			/* check for valid domain name (re 1123 5.2.5) */
2617c2aa98e2SPeter Wemm 			if (*p == '\0' && !AllowBogusHELO)
2618c2aa98e2SPeter Wemm 			{
2619c2aa98e2SPeter Wemm 				usrerr("501 %s requires domain address",
2620c2aa98e2SPeter Wemm 					cmdbuf);
2621c2aa98e2SPeter Wemm 				break;
2622c2aa98e2SPeter Wemm 			}
2623c2aa98e2SPeter Wemm 
2624c2aa98e2SPeter Wemm 			/* check for long domain name (hides Received: info) */
26252fb4f839SGregory Neil Shapiro 			if (strlen(p) > MAXNAME) /* EAI:ok:EHLO name must be ASCII */
2626c2aa98e2SPeter Wemm 			{
2627c2aa98e2SPeter Wemm 				usrerr("501 Invalid domain name");
262806f25ae9SGregory Neil Shapiro 				if (LogLevel > 9)
262906f25ae9SGregory Neil Shapiro 					sm_syslog(LOG_INFO, CurEnv->e_id,
263013bd1963SGregory Neil Shapiro 						  "invalid domain name (too long) from %s",
263106f25ae9SGregory Neil Shapiro 						  CurSmtpClient);
2632c2aa98e2SPeter Wemm 				break;
2633c2aa98e2SPeter Wemm 			}
2634c2aa98e2SPeter Wemm 
263594c01205SGregory Neil Shapiro 			ok = true;
2636c2aa98e2SPeter Wemm 			for (q = p; *q != '\0'; q++)
2637c2aa98e2SPeter Wemm 			{
2638c2aa98e2SPeter Wemm 				if (!isascii(*q))
2639c2aa98e2SPeter Wemm 					break;
2640c2aa98e2SPeter Wemm 				if (isalnum(*q))
2641c2aa98e2SPeter Wemm 					continue;
2642c2aa98e2SPeter Wemm 				if (isspace(*q))
2643c2aa98e2SPeter Wemm 				{
2644c2aa98e2SPeter Wemm 					*q = '\0';
264594c01205SGregory Neil Shapiro 
264694c01205SGregory Neil Shapiro 					/* only complain if strict check */
264794c01205SGregory Neil Shapiro 					ok = AllowBogusHELO;
2648b6bacd31SGregory Neil Shapiro 
2649b6bacd31SGregory Neil Shapiro 					/* allow trailing whitespace */
2650b6bacd31SGregory Neil Shapiro 					while (!ok && *++q != '\0' &&
2651b6bacd31SGregory Neil Shapiro 					       isspace(*q))
2652b6bacd31SGregory Neil Shapiro 						;
2653b6bacd31SGregory Neil Shapiro 					if (*q == '\0')
2654b6bacd31SGregory Neil Shapiro 						ok = true;
2655c2aa98e2SPeter Wemm 					break;
2656c2aa98e2SPeter Wemm 				}
2657a7ec597cSGregory Neil Shapiro 				if (strchr("[].-_#:", *q) == NULL)
2658c2aa98e2SPeter Wemm 					break;
2659c2aa98e2SPeter Wemm 			}
266006f25ae9SGregory Neil Shapiro 
266194c01205SGregory Neil Shapiro 			if (*q == '\0' && ok)
2662c2aa98e2SPeter Wemm 			{
2663c2aa98e2SPeter Wemm 				q = "pleased to meet you";
266440266059SGregory Neil Shapiro 				sendinghost = sm_strdup_x(p);
2665c2aa98e2SPeter Wemm 			}
2666c2aa98e2SPeter Wemm 			else if (!AllowBogusHELO)
2667c2aa98e2SPeter Wemm 			{
2668c2aa98e2SPeter Wemm 				usrerr("501 Invalid domain name");
266906f25ae9SGregory Neil Shapiro 				if (LogLevel > 9)
267006f25ae9SGregory Neil Shapiro 					sm_syslog(LOG_INFO, CurEnv->e_id,
267113bd1963SGregory Neil Shapiro 						  "invalid domain name (%s) from %.100s",
267206f25ae9SGregory Neil Shapiro 						  p, CurSmtpClient);
2673c2aa98e2SPeter Wemm 				break;
2674c2aa98e2SPeter Wemm 			}
2675c2aa98e2SPeter Wemm 			else
2676c2aa98e2SPeter Wemm 			{
2677c2aa98e2SPeter Wemm 				q = "accepting invalid domain name";
2678c2aa98e2SPeter Wemm 			}
2679c2aa98e2SPeter Wemm 
26804e4196cbSGregory Neil Shapiro 			if (gothello || smtp.sm_gotmail)
268140266059SGregory Neil Shapiro 				CLEAR_STATE(cmdbuf);
268240266059SGregory Neil Shapiro 
268340266059SGregory Neil Shapiro #if MILTER
268440266059SGregory Neil Shapiro 			if (smtp.sm_milterlist && smtp.sm_milterize &&
268540266059SGregory Neil Shapiro 			    !bitset(EF_DISCARD, e->e_flags))
268606f25ae9SGregory Neil Shapiro 			{
268706f25ae9SGregory Neil Shapiro 				char state;
268806f25ae9SGregory Neil Shapiro 				char *response;
268906f25ae9SGregory Neil Shapiro 
269006f25ae9SGregory Neil Shapiro 				response = milter_helo(p, e, &state);
269106f25ae9SGregory Neil Shapiro 				switch (state)
269206f25ae9SGregory Neil Shapiro 				{
269306f25ae9SGregory Neil Shapiro 				  case SMFIR_REJECT:
269440266059SGregory Neil Shapiro 					if (MilterLogLevel > 3)
269540266059SGregory Neil Shapiro 						sm_syslog(LOG_INFO, e->e_id,
269640266059SGregory Neil Shapiro 							  "Milter: helo=%s, reject=Command rejected",
269740266059SGregory Neil Shapiro 							  p);
269806f25ae9SGregory Neil Shapiro 					nullserver = "Command rejected";
269940266059SGregory Neil Shapiro 					smtp.sm_milterize = false;
270006f25ae9SGregory Neil Shapiro 					break;
270106f25ae9SGregory Neil Shapiro 
270206f25ae9SGregory Neil Shapiro 				  case SMFIR_TEMPFAIL:
270340266059SGregory Neil Shapiro 					if (MilterLogLevel > 3)
270440266059SGregory Neil Shapiro 						sm_syslog(LOG_INFO, e->e_id,
270540266059SGregory Neil Shapiro 							  "Milter: helo=%s, reject=%s",
270640266059SGregory Neil Shapiro 							  p, MSG_TEMPFAIL);
270740266059SGregory Neil Shapiro 					tempfail = true;
270840266059SGregory Neil Shapiro 					smtp.sm_milterize = false;
270906f25ae9SGregory Neil Shapiro 					break;
27104e4196cbSGregory Neil Shapiro 
2711d0cef73dSGregory Neil Shapiro 				  case SMFIR_REPLYCODE:
27124e4196cbSGregory Neil Shapiro 					if (MilterLogLevel > 3)
27134e4196cbSGregory Neil Shapiro 						sm_syslog(LOG_INFO, e->e_id,
2714d0cef73dSGregory Neil Shapiro 							  "Milter: helo=%s, reject=%s",
2715d0cef73dSGregory Neil Shapiro 							  p, response);
2716d0cef73dSGregory Neil Shapiro 					if (strncmp(response, "421 ", 4) != 0
2717d0cef73dSGregory Neil Shapiro 					    && strncmp(response, "421-", 4) != 0)
2718d0cef73dSGregory Neil Shapiro 					{
2719d0cef73dSGregory Neil Shapiro 						nullserver = newstr(response);
2720d0cef73dSGregory Neil Shapiro 						smtp.sm_milterize = false;
2721d0cef73dSGregory Neil Shapiro 						break;
2722d0cef73dSGregory Neil Shapiro 					}
2723d0cef73dSGregory Neil Shapiro 					/* FALLTHROUGH */
2724d0cef73dSGregory Neil Shapiro 
2725d0cef73dSGregory Neil Shapiro 				  case SMFIR_SHUTDOWN:
2726d0cef73dSGregory Neil Shapiro 					if (MilterLogLevel > 3 &&
2727d0cef73dSGregory Neil Shapiro 					    response == NULL)
2728d0cef73dSGregory Neil Shapiro 						sm_syslog(LOG_INFO, e->e_id,
2729af9557fdSGregory Neil Shapiro 							  "Milter: helo=%s, reject=421 4.7.0 %s closing connection",
27304e4196cbSGregory Neil Shapiro 							  p, MyHostName);
27314e4196cbSGregory Neil Shapiro 					tempfail = true;
27324e4196cbSGregory Neil Shapiro 					smtp.sm_milterize = false;
2733d0cef73dSGregory Neil Shapiro 					if (response != NULL)
27345b0945b5SGregory Neil Shapiro 					{
27355b0945b5SGregory Neil Shapiro 						/* Can't use ("%s", ...) due to usrerr() requirements */
2736d0cef73dSGregory Neil Shapiro 						usrerr(response);
27375b0945b5SGregory Neil Shapiro 					}
2738d0cef73dSGregory Neil Shapiro 					else
27395b0945b5SGregory Neil Shapiro 					{
27404e4196cbSGregory Neil Shapiro 						message("421 4.7.0 %s closing connection",
27414e4196cbSGregory Neil Shapiro 							MyHostName);
27425b0945b5SGregory Neil Shapiro 					}
27434e4196cbSGregory Neil Shapiro 					/* arrange to ignore send list */
27444e4196cbSGregory Neil Shapiro 					e->e_sendqueue = NULL;
2745d0cef73dSGregory Neil Shapiro 					lognullconnection = false;
27464e4196cbSGregory Neil Shapiro 					goto doquit;
274706f25ae9SGregory Neil Shapiro 				}
274840266059SGregory Neil Shapiro 				if (response != NULL)
274940266059SGregory Neil Shapiro 					sm_free(response);
275040266059SGregory Neil Shapiro 
275140266059SGregory Neil Shapiro 				/*
275240266059SGregory Neil Shapiro 				**  If quarantining by a connect/ehlo action,
275340266059SGregory Neil Shapiro 				**  save between messages
275440266059SGregory Neil Shapiro 				*/
275540266059SGregory Neil Shapiro 
275640266059SGregory Neil Shapiro 				if (smtp.sm_quarmsg == NULL &&
275740266059SGregory Neil Shapiro 				    e->e_quarmsg != NULL)
275840266059SGregory Neil Shapiro 					smtp.sm_quarmsg = newstr(e->e_quarmsg);
275906f25ae9SGregory Neil Shapiro 			}
276040266059SGregory Neil Shapiro #endif /* MILTER */
276140266059SGregory Neil Shapiro 			gothello = true;
276206f25ae9SGregory Neil Shapiro 
2763c2aa98e2SPeter Wemm 			/* print HELO response message */
276406f25ae9SGregory Neil Shapiro 			if (c->cmd_code != CMDEHLO)
2765c2aa98e2SPeter Wemm 			{
2766c2aa98e2SPeter Wemm 				message("250 %s Hello %s, %s",
2767c2aa98e2SPeter Wemm 					MyHostName, CurSmtpClient, q);
2768c2aa98e2SPeter Wemm 				break;
2769c2aa98e2SPeter Wemm 			}
2770c2aa98e2SPeter Wemm 
2771c2aa98e2SPeter Wemm 			message("250-%s Hello %s, %s",
2772c2aa98e2SPeter Wemm 				MyHostName, CurSmtpClient, q);
2773c2aa98e2SPeter Wemm 
277406f25ae9SGregory Neil Shapiro 			/* offer ENHSC even for nullserver */
277506f25ae9SGregory Neil Shapiro 			if (nullserver != NULL)
277606f25ae9SGregory Neil Shapiro 			{
277706f25ae9SGregory Neil Shapiro 				message("250 ENHANCEDSTATUSCODES");
277806f25ae9SGregory Neil Shapiro 				break;
277906f25ae9SGregory Neil Shapiro 			}
278006f25ae9SGregory Neil Shapiro 
278142e5d165SGregory Neil Shapiro 			/*
278242e5d165SGregory Neil Shapiro 			**  print EHLO features list
278342e5d165SGregory Neil Shapiro 			**
278442e5d165SGregory Neil Shapiro 			**  Note: If you change this list,
278542e5d165SGregory Neil Shapiro 			**	  remember to update 'helpfile'
278642e5d165SGregory Neil Shapiro 			*/
278742e5d165SGregory Neil Shapiro 
278806f25ae9SGregory Neil Shapiro 			message("250-ENHANCEDSTATUSCODES");
278940266059SGregory Neil Shapiro #if PIPELINING
279040266059SGregory Neil Shapiro 			if (bitset(SRV_OFFER_PIPE, features))
279140266059SGregory Neil Shapiro 				message("250-PIPELINING");
27925b0945b5SGregory Neil Shapiro #endif
279340266059SGregory Neil Shapiro 			if (bitset(SRV_OFFER_EXPN, features))
2794c2aa98e2SPeter Wemm 			{
2795c2aa98e2SPeter Wemm 				message("250-EXPN");
279640266059SGregory Neil Shapiro 				if (bitset(SRV_OFFER_VERB, features))
2797c2aa98e2SPeter Wemm 					message("250-VERB");
2798c2aa98e2SPeter Wemm 			}
2799c2aa98e2SPeter Wemm #if MIME8TO7
2800c2aa98e2SPeter Wemm 			message("250-8BITMIME");
28015b0945b5SGregory Neil Shapiro #endif
2802c2aa98e2SPeter Wemm 			if (MaxMessageSize > 0)
2803c2aa98e2SPeter Wemm 				message("250-SIZE %ld", MaxMessageSize);
2804c2aa98e2SPeter Wemm 			else
2805c2aa98e2SPeter Wemm 				message("250-SIZE");
2806c2aa98e2SPeter Wemm #if DSN
280740266059SGregory Neil Shapiro 			if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
2808c2aa98e2SPeter Wemm 				message("250-DSN");
28095b0945b5SGregory Neil Shapiro #endif
28102fb4f839SGregory Neil Shapiro #if USE_EAI
28115b0945b5SGregory Neil Shapiro 			if (bitset(SRV_OFFER_EAI, features))
28125b0945b5SGregory Neil Shapiro 				message("250-SMTPUTF8");
28132fb4f839SGregory Neil Shapiro #endif
281440266059SGregory Neil Shapiro 			if (bitset(SRV_OFFER_ETRN, features))
2815c2aa98e2SPeter Wemm 				message("250-ETRN");
281606f25ae9SGregory Neil Shapiro #if SASL
281706f25ae9SGregory Neil Shapiro 			if (sasl_ok && mechlist != NULL && *mechlist != '\0')
281806f25ae9SGregory Neil Shapiro 				message("250-AUTH %s", mechlist);
28195b0945b5SGregory Neil Shapiro #endif
282006f25ae9SGregory Neil Shapiro #if STARTTLS
28216f9c8e5bSGregory Neil Shapiro 			if (tls_ok_srv && bitset(SRV_OFFER_TLS, features))
282206f25ae9SGregory Neil Shapiro 				message("250-STARTTLS");
28235b0945b5SGregory Neil Shapiro #endif
282440266059SGregory Neil Shapiro 			if (DeliverByMin > 0)
282540266059SGregory Neil Shapiro 				message("250-DELIVERBY %ld",
282640266059SGregory Neil Shapiro 					(long) DeliverByMin);
282740266059SGregory Neil Shapiro 			else if (DeliverByMin == 0)
282840266059SGregory Neil Shapiro 				message("250-DELIVERBY");
282940266059SGregory Neil Shapiro 
283040266059SGregory Neil Shapiro 			/* < 0: no deliver-by */
283140266059SGregory Neil Shapiro 
2832c2aa98e2SPeter Wemm 			message("250 HELP");
2833c2aa98e2SPeter Wemm 			break;
2834c2aa98e2SPeter Wemm 
2835c2aa98e2SPeter Wemm 		  case CMDMAIL:		/* mail -- designate sender */
2836c2aa98e2SPeter Wemm 			SmtpPhase = "server MAIL";
283740266059SGregory Neil Shapiro 			DELAY_CONN("MAIL");
2838c2aa98e2SPeter Wemm 
2839c2aa98e2SPeter Wemm 			/* check for validity of this command */
2840c2aa98e2SPeter Wemm 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
2841c2aa98e2SPeter Wemm 			{
284206f25ae9SGregory Neil Shapiro 				usrerr("503 5.0.0 Polite people say HELO first");
2843c2aa98e2SPeter Wemm 				break;
2844c2aa98e2SPeter Wemm 			}
284540266059SGregory Neil Shapiro 			if (smtp.sm_gotmail)
2846c2aa98e2SPeter Wemm 			{
284706f25ae9SGregory Neil Shapiro 				usrerr("503 5.5.0 Sender already specified");
2848c2aa98e2SPeter Wemm 				break;
2849c2aa98e2SPeter Wemm 			}
285006f25ae9SGregory Neil Shapiro #if SASL
285140266059SGregory Neil Shapiro 			if (bitset(SRV_REQ_AUTH, features) &&
285206f25ae9SGregory Neil Shapiro 			    authenticating != SASL_IS_AUTH)
285306f25ae9SGregory Neil Shapiro 			{
285406f25ae9SGregory Neil Shapiro 				usrerr("530 5.7.0 Authentication required");
285506f25ae9SGregory Neil Shapiro 				break;
285606f25ae9SGregory Neil Shapiro 			}
285706f25ae9SGregory Neil Shapiro #endif /* SASL */
285806f25ae9SGregory Neil Shapiro 
285906f25ae9SGregory Neil Shapiro 			p = skipword(p, "from");
286006f25ae9SGregory Neil Shapiro 			if (p == NULL)
286106f25ae9SGregory Neil Shapiro 				break;
2862*d39bd2c1SGregory Neil Shapiro 			maps_reset_chged("server:MAIL");
286306f25ae9SGregory Neil Shapiro 			if (tempfail)
286406f25ae9SGregory Neil Shapiro 			{
286506f25ae9SGregory Neil Shapiro 				if (LogLevel > 9)
286606f25ae9SGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
286713bd1963SGregory Neil Shapiro 						  "SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)",
286806f25ae9SGregory Neil Shapiro 						  p, CurSmtpClient);
28695b0945b5SGregory Neil Shapiro 				/* Can't use ("%s", ...) due to usrerr() requirements */
287040266059SGregory Neil Shapiro 				usrerr(MSG_TEMPFAIL);
287106f25ae9SGregory Neil Shapiro 				break;
287206f25ae9SGregory Neil Shapiro 			}
2873c2aa98e2SPeter Wemm 
2874c2aa98e2SPeter Wemm 			/* make sure we know who the sending host is */
2875c2aa98e2SPeter Wemm 			if (sendinghost == NULL)
2876c2aa98e2SPeter Wemm 				sendinghost = peerhostname;
2877c2aa98e2SPeter Wemm 
2878c2aa98e2SPeter Wemm 
287940266059SGregory Neil Shapiro #if SM_HEAP_CHECK
288040266059SGregory Neil Shapiro 			if (sm_debug_active(&DebugLeakSmtp, 1))
288140266059SGregory Neil Shapiro 			{
288240266059SGregory Neil Shapiro 				sm_heap_newgroup();
288340266059SGregory Neil Shapiro 				sm_dprintf("smtp() heap group #%d\n",
288440266059SGregory Neil Shapiro 					sm_heap_group());
288540266059SGregory Neil Shapiro 			}
288640266059SGregory Neil Shapiro #endif /* SM_HEAP_CHECK */
288706f25ae9SGregory Neil Shapiro 
2888c2aa98e2SPeter Wemm 			if (Errors > 0)
288940266059SGregory Neil Shapiro 				goto undo_no_pm;
2890c2aa98e2SPeter Wemm 			if (!gothello)
2891c2aa98e2SPeter Wemm 			{
289240266059SGregory Neil Shapiro 				auth_warning(e, "%s didn't use HELO protocol",
2893c2aa98e2SPeter Wemm 					     CurSmtpClient);
2894c2aa98e2SPeter Wemm 			}
2895c2aa98e2SPeter Wemm #ifdef PICKY_HELO_CHECK
289640266059SGregory Neil Shapiro 			if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
289740266059SGregory Neil Shapiro 			    (sm_strcasecmp(peerhostname, "localhost") != 0 ||
289840266059SGregory Neil Shapiro 			     sm_strcasecmp(sendinghost, MyHostName) != 0))
2899c2aa98e2SPeter Wemm 			{
2900c2aa98e2SPeter Wemm 				auth_warning(e, "Host %s claimed to be %s",
2901c2aa98e2SPeter Wemm 					     CurSmtpClient, sendinghost);
2902c2aa98e2SPeter Wemm 			}
290306f25ae9SGregory Neil Shapiro #endif /* PICKY_HELO_CHECK */
2904c2aa98e2SPeter Wemm 
2905c2aa98e2SPeter Wemm 			if (protocol == NULL)
2906c2aa98e2SPeter Wemm 				protocol = "SMTP";
290740266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM, 'r', protocol);
290840266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM, 's', sendinghost);
290906f25ae9SGregory Neil Shapiro 
2910c2aa98e2SPeter Wemm 			if (Errors > 0)
291140266059SGregory Neil Shapiro 				goto undo_no_pm;
291240266059SGregory Neil Shapiro 			smtp.sm_nrcpts = 0;
291340266059SGregory Neil Shapiro 			n_badrcpts = 0;
291440266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
291540266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
2916e92d3f3fSGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"),
2917e92d3f3fSGregory Neil Shapiro 				"0");
291806f25ae9SGregory Neil Shapiro 			e->e_flags |= EF_CLRQUEUE;
291940266059SGregory Neil Shapiro 			sm_setproctitle(true, e, "%s %s: %.80s",
292006f25ae9SGregory Neil Shapiro 					qid_printname(e),
292106f25ae9SGregory Neil Shapiro 					CurSmtpClient, inp);
2922c2aa98e2SPeter Wemm 
292340266059SGregory Neil Shapiro 			/* do the processing */
292440266059SGregory Neil Shapiro 		    SM_TRY
2925c2aa98e2SPeter Wemm 		    {
2926605302a5SGregory Neil Shapiro 			extern char *FullName;
29272fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
29282fb4f839SGregory Neil Shapiro 			char *origp;
29292fb4f839SGregory Neil Shapiro 			char iaddr[MAXLINE * 2];
29302fb4f839SGregory Neil Shapiro 			int len;
29312fb4f839SGregory Neil Shapiro #else
29322fb4f839SGregory Neil Shapiro # define origp	p
29332fb4f839SGregory Neil Shapiro #endif
2934605302a5SGregory Neil Shapiro 
293540266059SGregory Neil Shapiro 			QuickAbort = true;
29365b0945b5SGregory Neil Shapiro 			SM_FREE(FullName);
29372fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
29382fb4f839SGregory Neil Shapiro 			len = sizeof(iaddr);
29392fb4f839SGregory Neil Shapiro 			origp = p;
29402fb4f839SGregory Neil Shapiro 
29412fb4f839SGregory Neil Shapiro 			/* HACK!!!! p is more than the address! */
29422fb4f839SGregory Neil Shapiro 			p = quote_internal_chars(p, iaddr, &len, NULL);
29432fb4f839SGregory Neil Shapiro #endif
2944c2aa98e2SPeter Wemm 
2945c2aa98e2SPeter Wemm 			/* must parse sender first */
2946c2aa98e2SPeter Wemm 			delimptr = NULL;
294740266059SGregory Neil Shapiro 			setsender(p, e, &delimptr, ' ', false);
2948c2aa98e2SPeter Wemm 			if (delimptr != NULL && *delimptr != '\0')
29492fb4f839SGregory Neil Shapiro 			{
2950c2aa98e2SPeter Wemm 				*delimptr++ = '\0';
29512fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
29522fb4f839SGregory Neil Shapiro 				len = sizeof(iaddr) - (delimptr - iaddr);
29532fb4f839SGregory Neil Shapiro 				(void) dequote_internal_chars(delimptr, delimptr, len);
2954*d39bd2c1SGregory Neil Shapiro 				sep_args(delimptr, origp, e->e_id, p);
29552fb4f839SGregory Neil Shapiro #endif
29562fb4f839SGregory Neil Shapiro 			}
2957c2aa98e2SPeter Wemm 			if (Errors > 0)
295840266059SGregory Neil Shapiro 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2959c2aa98e2SPeter Wemm 
296006f25ae9SGregory Neil Shapiro 			/* Successfully set e_from, allow logging */
296106f25ae9SGregory Neil Shapiro 			e->e_flags |= EF_LOGSENDER;
296206f25ae9SGregory Neil Shapiro 
296306f25ae9SGregory Neil Shapiro 			/* put resulting triple from parseaddr() into macros */
296406f25ae9SGregory Neil Shapiro 			if (e->e_from.q_mailer != NULL)
296540266059SGregory Neil Shapiro 				 macdefine(&e->e_macro, A_PERM,
296640266059SGregory Neil Shapiro 					macid("{mail_mailer}"),
296740266059SGregory Neil Shapiro 					e->e_from.q_mailer->m_name);
296806f25ae9SGregory Neil Shapiro 			else
296940266059SGregory Neil Shapiro 				 macdefine(&e->e_macro, A_PERM,
297040266059SGregory Neil Shapiro 					macid("{mail_mailer}"), NULL);
297106f25ae9SGregory Neil Shapiro 			if (e->e_from.q_host != NULL)
297240266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
297340266059SGregory Neil Shapiro 					macid("{mail_host}"),
297440266059SGregory Neil Shapiro 					e->e_from.q_host);
297506f25ae9SGregory Neil Shapiro 			else
297640266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
297740266059SGregory Neil Shapiro 					macid("{mail_host}"), "localhost");
297806f25ae9SGregory Neil Shapiro 			if (e->e_from.q_user != NULL)
297940266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
298040266059SGregory Neil Shapiro 					macid("{mail_addr}"),
298140266059SGregory Neil Shapiro 					e->e_from.q_user);
298206f25ae9SGregory Neil Shapiro 			else
298340266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
298440266059SGregory Neil Shapiro 					macid("{mail_addr}"), NULL);
298506f25ae9SGregory Neil Shapiro 			if (Errors > 0)
298640266059SGregory Neil Shapiro 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2987c2aa98e2SPeter Wemm 
2988c2aa98e2SPeter Wemm 			/* check for possible spoofing */
2989c2aa98e2SPeter Wemm 			if (RealUid != 0 && OpMode == MD_SMTP &&
2990c2aa98e2SPeter Wemm 			    !wordinclass(RealUserName, 't') &&
299106f25ae9SGregory Neil Shapiro 			    (!bitnset(M_LOCALMAILER,
299206f25ae9SGregory Neil Shapiro 				      e->e_from.q_mailer->m_flags) ||
299306f25ae9SGregory Neil Shapiro 			     strcmp(e->e_from.q_user, RealUserName) != 0))
2994c2aa98e2SPeter Wemm 			{
2995c2aa98e2SPeter Wemm 				auth_warning(e, "%s owned process doing -bs",
2996c2aa98e2SPeter Wemm 					RealUserName);
2997c2aa98e2SPeter Wemm 			}
2998c2aa98e2SPeter Wemm 
2999a7ec597cSGregory Neil Shapiro 			/* reset to default value */
3000*d39bd2c1SGregory Neil Shapiro 			e->e_flags &= ~EF_7BITBODY;
3001a7ec597cSGregory Neil Shapiro 
3002c2aa98e2SPeter Wemm 			/* now parse ESMTP arguments */
3003c2aa98e2SPeter Wemm 			e->e_msgsize = 0;
300406f25ae9SGregory Neil Shapiro 			addr = p;
30052fb4f839SGregory Neil Shapiro 			parse_esmtp_args(e, NULL, origp, delimptr, "MAIL", args,
3006d0cef73dSGregory Neil Shapiro 					mail_esmtp_args);
3007c2aa98e2SPeter Wemm 			if (Errors > 0)
300840266059SGregory Neil Shapiro 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
300940266059SGregory Neil Shapiro 
30102fb4f839SGregory Neil Shapiro #if USE_EAI
30115b0945b5SGregory Neil Shapiro 			if (e->e_smtputf8)
30125b0945b5SGregory Neil Shapiro 			{
30135b0945b5SGregory Neil Shapiro 				protocol = GET_PROTOCOL();
30145b0945b5SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM, 'r', protocol);
30155b0945b5SGregory Neil Shapiro 			}
30165b0945b5SGregory Neil Shapiro 
30175b0945b5SGregory Neil Shapiro 			/* UTF8 addresses are only legal with SMTPUTF8 */
30182fb4f839SGregory Neil Shapiro /* XXX different error if SMTPUTF8 is not enabled? */
30192fb4f839SGregory Neil Shapiro 			CHECK_UTF8_ADDR(e->e_from.q_paddr, q);
30202fb4f839SGregory Neil Shapiro 			if (q != NULL)
30215b0945b5SGregory Neil Shapiro 			{
30222fb4f839SGregory Neil Shapiro 				usrerr(q);
30235b0945b5SGregory Neil Shapiro 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
30245b0945b5SGregory Neil Shapiro 			}
30255b0945b5SGregory Neil Shapiro #endif
30265b0945b5SGregory Neil Shapiro 
302740266059SGregory Neil Shapiro #if SASL
302840266059SGregory Neil Shapiro # if _FFR_AUTH_PASSING
302940266059SGregory Neil Shapiro 			/* set the default AUTH= if the sender didn't */
303040266059SGregory Neil Shapiro 			if (e->e_auth_param == NULL)
303140266059SGregory Neil Shapiro 			{
303240266059SGregory Neil Shapiro 				/* XXX only do this for an MSA? */
303340266059SGregory Neil Shapiro 				e->e_auth_param = macvalue(macid("{auth_authen}"),
303440266059SGregory Neil Shapiro 							   e);
303540266059SGregory Neil Shapiro 				if (e->e_auth_param == NULL)
303640266059SGregory Neil Shapiro 					e->e_auth_param = "<>";
303740266059SGregory Neil Shapiro 
303840266059SGregory Neil Shapiro 				/*
303940266059SGregory Neil Shapiro 				**  XXX should we invoke Strust_auth now?
304040266059SGregory Neil Shapiro 				**  authorizing as the client that just
304140266059SGregory Neil Shapiro 				**  authenticated, so we'll trust implicitly
304240266059SGregory Neil Shapiro 				*/
304340266059SGregory Neil Shapiro 			}
304440266059SGregory Neil Shapiro # endif /* _FFR_AUTH_PASSING */
304540266059SGregory Neil Shapiro #endif /* SASL */
3046c2aa98e2SPeter Wemm 
304706f25ae9SGregory Neil Shapiro 			/* do config file checking of the sender */
304840266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
304940266059SGregory Neil Shapiro 				macid("{addr_type}"), "e s");
305040266059SGregory Neil Shapiro #if _FFR_MAIL_MACRO
305140266059SGregory Neil Shapiro 			/* make the "real" sender address available */
305240266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
305340266059SGregory Neil Shapiro 				  e->e_from.q_paddr);
30545b0945b5SGregory Neil Shapiro #endif
305506f25ae9SGregory Neil Shapiro 			if (rscheck("check_mail", addr,
3056959366dcSGregory Neil Shapiro 				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
3057da7d7b9cSGregory Neil Shapiro 				    NULL, e->e_id, NULL, NULL) != EX_OK ||
305806f25ae9SGregory Neil Shapiro 			    Errors > 0)
305940266059SGregory Neil Shapiro 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
306040266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
306140266059SGregory Neil Shapiro 				  macid("{addr_type}"), NULL);
306206f25ae9SGregory Neil Shapiro 
306342e5d165SGregory Neil Shapiro 			if (MaxMessageSize > 0 &&
30648774250cSGregory Neil Shapiro 			    (e->e_msgsize > MaxMessageSize ||
30658774250cSGregory Neil Shapiro 			     e->e_msgsize < 0))
3066c2aa98e2SPeter Wemm 			{
306706f25ae9SGregory Neil Shapiro 				usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
3068c2aa98e2SPeter Wemm 					MaxMessageSize);
306940266059SGregory Neil Shapiro 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
3070c2aa98e2SPeter Wemm 			}
3071c2aa98e2SPeter Wemm 
307240266059SGregory Neil Shapiro 			/*
307340266059SGregory Neil Shapiro 			**  XXX always check whether there is at least one fs
307440266059SGregory Neil Shapiro 			**  with enough space?
307540266059SGregory Neil Shapiro 			**  However, this may not help much: the queue group
307640266059SGregory Neil Shapiro 			**  selection may later on select a FS that hasn't
307740266059SGregory Neil Shapiro 			**  enough space.
307840266059SGregory Neil Shapiro 			*/
307940266059SGregory Neil Shapiro 
308040266059SGregory Neil Shapiro 			if ((NumFileSys == 1 || NumQueue == 1) &&
308140266059SGregory Neil Shapiro 			    !enoughdiskspace(e->e_msgsize, e)
308240266059SGregory Neil Shapiro #if _FFR_ANY_FREE_FS
308340266059SGregory Neil Shapiro 			    && !filesys_free(e->e_msgsize)
30845b0945b5SGregory Neil Shapiro #endif
308540266059SGregory Neil Shapiro 			   )
3086c2aa98e2SPeter Wemm 			{
308740266059SGregory Neil Shapiro 				/*
308840266059SGregory Neil Shapiro 				**  We perform this test again when the
308940266059SGregory Neil Shapiro 				**  queue directory is selected, in collect.
309040266059SGregory Neil Shapiro 				*/
309140266059SGregory Neil Shapiro 
309206f25ae9SGregory Neil Shapiro 				usrerr("452 4.4.5 Insufficient disk space; try again later");
309340266059SGregory Neil Shapiro 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
3094c2aa98e2SPeter Wemm 			}
3095c2aa98e2SPeter Wemm 			if (Errors > 0)
309640266059SGregory Neil Shapiro 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
309706f25ae9SGregory Neil Shapiro 
309840266059SGregory Neil Shapiro 			LogUsrErrs = true;
309940266059SGregory Neil Shapiro #if MILTER
310040266059SGregory Neil Shapiro 			if (smtp.sm_milterlist && smtp.sm_milterize &&
310140266059SGregory Neil Shapiro 			    !bitset(EF_DISCARD, e->e_flags))
310206f25ae9SGregory Neil Shapiro 			{
310306f25ae9SGregory Neil Shapiro 				char state;
310406f25ae9SGregory Neil Shapiro 				char *response;
310506f25ae9SGregory Neil Shapiro 
310606f25ae9SGregory Neil Shapiro 				response = milter_envfrom(args, e, &state);
310740266059SGregory Neil Shapiro 				MILTER_REPLY("from");
310806f25ae9SGregory Neil Shapiro 			}
310940266059SGregory Neil Shapiro #endif /* MILTER */
311006f25ae9SGregory Neil Shapiro 			if (Errors > 0)
311140266059SGregory Neil Shapiro 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
311206f25ae9SGregory Neil Shapiro 
311306f25ae9SGregory Neil Shapiro 			message("250 2.1.0 Sender ok");
311440266059SGregory Neil Shapiro 			smtp.sm_gotmail = true;
311540266059SGregory Neil Shapiro 		    }
311640266059SGregory Neil Shapiro 		    SM_EXCEPT(exc, "[!F]*")
311740266059SGregory Neil Shapiro 		    {
311840266059SGregory Neil Shapiro 			/*
311940266059SGregory Neil Shapiro 			**  An error occurred while processing a MAIL command.
312040266059SGregory Neil Shapiro 			**  Jump to the common error handling code.
312140266059SGregory Neil Shapiro 			*/
312240266059SGregory Neil Shapiro 
312340266059SGregory Neil Shapiro 			sm_exc_free(exc);
312440266059SGregory Neil Shapiro 			goto undo_no_pm;
312540266059SGregory Neil Shapiro 		    }
312640266059SGregory Neil Shapiro 		    SM_END_TRY
312740266059SGregory Neil Shapiro 			break;
312840266059SGregory Neil Shapiro 
312940266059SGregory Neil Shapiro 		  undo_no_pm:
313040266059SGregory Neil Shapiro 			e->e_flags &= ~EF_PM_NOTIFY;
313140266059SGregory Neil Shapiro 		  undo:
3132c2aa98e2SPeter Wemm 			break;
3133c2aa98e2SPeter Wemm 
3134c2aa98e2SPeter Wemm 		  case CMDRCPT:		/* rcpt -- designate recipient */
313540266059SGregory Neil Shapiro 			DELAY_CONN("RCPT");
3136d0cef73dSGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
3137d0cef73dSGregory Neil Shapiro 				macid("{rcpt_mailer}"), NULL);
3138d0cef73dSGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
3139d0cef73dSGregory Neil Shapiro 				macid("{rcpt_host}"), NULL);
3140d0cef73dSGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
3141d0cef73dSGregory Neil Shapiro 				macid("{rcpt_addr}"), NULL);
3142d0cef73dSGregory Neil Shapiro #if MILTER
3143d0cef73dSGregory Neil Shapiro 			(void) memset(&addr_st, '\0', sizeof(addr_st));
3144d0cef73dSGregory Neil Shapiro 			a = NULL;
3145d0cef73dSGregory Neil Shapiro 			milter_rcpt_added = false;
3146ffb83623SGregory Neil Shapiro 			smtp.sm_e_nrcpts_orig = e->e_nrcpts;
3147d0cef73dSGregory Neil Shapiro #endif
3148e3793f76SGregory Neil Shapiro #if _FFR_BADRCPT_SHUTDOWN
3149e3793f76SGregory Neil Shapiro 			/*
3150e3793f76SGregory Neil Shapiro 			**  hack to deal with hack, see below:
31519bd497b8SGregory Neil Shapiro 			**  n_badrcpts is increased if limit is reached.
3152e3793f76SGregory Neil Shapiro 			*/
3153e3793f76SGregory Neil Shapiro 
3154e3793f76SGregory Neil Shapiro 			n_badrcpts_adj = (BadRcptThrottle > 0 &&
3155e3793f76SGregory Neil Shapiro 					  n_badrcpts > BadRcptThrottle &&
3156e3793f76SGregory Neil Shapiro 					  LogLevel > 5)
3157e3793f76SGregory Neil Shapiro 					  ? n_badrcpts - 1 : n_badrcpts;
3158e3793f76SGregory Neil Shapiro 			if (BadRcptShutdown > 0 &&
3159e3793f76SGregory Neil Shapiro 			    n_badrcpts_adj >= BadRcptShutdown &&
3160e3793f76SGregory Neil Shapiro 			    (BadRcptShutdownGood == 0 ||
3161e3793f76SGregory Neil Shapiro 			     smtp.sm_nrcpts == 0 ||
3162e3793f76SGregory Neil Shapiro 			     (n_badrcpts_adj * 100 /
3163e3793f76SGregory Neil Shapiro 			      (smtp.sm_nrcpts + n_badrcpts) >=
3164e3793f76SGregory Neil Shapiro 			      BadRcptShutdownGood)))
3165e3793f76SGregory Neil Shapiro 			{
3166e3793f76SGregory Neil Shapiro 				if (LogLevel > 5)
3167e3793f76SGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
3168e3793f76SGregory Neil Shapiro 						  "%s: Possible SMTP RCPT flood, shutting down connection.",
3169e3793f76SGregory Neil Shapiro 						  CurSmtpClient);
3170e3793f76SGregory Neil Shapiro 				message("421 4.7.0 %s Too many bad recipients; closing connection",
3171e3793f76SGregory Neil Shapiro 				MyHostName);
3172e3793f76SGregory Neil Shapiro 
3173e3793f76SGregory Neil Shapiro 				/* arrange to ignore any current send list */
3174e3793f76SGregory Neil Shapiro 				e->e_sendqueue = NULL;
3175e3793f76SGregory Neil Shapiro 				goto doquit;
3176e3793f76SGregory Neil Shapiro 			}
3177e3793f76SGregory Neil Shapiro #endif /* _FFR_BADRCPT_SHUTDOWN */
3178323f6dcbSGregory Neil Shapiro 			if (BadRcptThrottle > 0 &&
3179323f6dcbSGregory Neil Shapiro 			    n_badrcpts >= BadRcptThrottle)
3180323f6dcbSGregory Neil Shapiro 			{
3181323f6dcbSGregory Neil Shapiro 				if (LogLevel > 5 &&
3182323f6dcbSGregory Neil Shapiro 				    n_badrcpts == BadRcptThrottle)
3183323f6dcbSGregory Neil Shapiro 				{
3184323f6dcbSGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
3185323f6dcbSGregory Neil Shapiro 						  "%s: Possible SMTP RCPT flood, throttling.",
3186323f6dcbSGregory Neil Shapiro 						  CurSmtpClient);
3187323f6dcbSGregory Neil Shapiro 
3188323f6dcbSGregory Neil Shapiro 					/* To avoid duplicated message */
3189323f6dcbSGregory Neil Shapiro 					n_badrcpts++;
3190323f6dcbSGregory Neil Shapiro 				}
3191e92d3f3fSGregory Neil Shapiro 				NBADRCPTS;
3192323f6dcbSGregory Neil Shapiro 
3193323f6dcbSGregory Neil Shapiro 				/*
3194323f6dcbSGregory Neil Shapiro 				**  Don't use exponential backoff for now.
31959bd497b8SGregory Neil Shapiro 				**  Some systems will open more connections
3196323f6dcbSGregory Neil Shapiro 				**  and actually overload the receiver even
3197323f6dcbSGregory Neil Shapiro 				**  more.
3198323f6dcbSGregory Neil Shapiro 				*/
3199323f6dcbSGregory Neil Shapiro 
32009bd497b8SGregory Neil Shapiro 				(void) sleep(BadRcptThrottleDelay);
3201323f6dcbSGregory Neil Shapiro 			}
320240266059SGregory Neil Shapiro 			if (!smtp.sm_gotmail)
3203c2aa98e2SPeter Wemm 			{
320406f25ae9SGregory Neil Shapiro 				usrerr("503 5.0.0 Need MAIL before RCPT");
3205c2aa98e2SPeter Wemm 				break;
3206c2aa98e2SPeter Wemm 			}
3207c2aa98e2SPeter Wemm 			SmtpPhase = "server RCPT";
320840266059SGregory Neil Shapiro 		    SM_TRY
3209c2aa98e2SPeter Wemm 		    {
32102fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
32112fb4f839SGregory Neil Shapiro 			char iaddr[MAXLINE * 2];
32122fb4f839SGregory Neil Shapiro 			int len;
32132fb4f839SGregory Neil Shapiro 			char *origp;
32142fb4f839SGregory Neil Shapiro #endif
32152fb4f839SGregory Neil Shapiro 
321640266059SGregory Neil Shapiro 			QuickAbort = true;
321740266059SGregory Neil Shapiro 			LogUsrErrs = true;
3218c2aa98e2SPeter Wemm 
3219c2aa98e2SPeter Wemm 			/* limit flooding of our machine */
322040266059SGregory Neil Shapiro 			if (MaxRcptPerMsg > 0 &&
322140266059SGregory Neil Shapiro 			    smtp.sm_nrcpts >= MaxRcptPerMsg)
3222c2aa98e2SPeter Wemm 			{
322340266059SGregory Neil Shapiro 				/* sleep(1); / * slow down? */
322406f25ae9SGregory Neil Shapiro 				usrerr("452 4.5.3 Too many recipients");
322540266059SGregory Neil Shapiro 				goto rcpt_done;
3226c2aa98e2SPeter Wemm 			}
3227c2aa98e2SPeter Wemm 
32286f9c8e5bSGregory Neil Shapiro 			if (!SM_IS_INTERACTIVE(e->e_sendmode)
32294e4196cbSGregory Neil Shapiro #if _FFR_DM_ONE
32304e4196cbSGregory Neil Shapiro 			    && (NotFirstDelivery || SM_DM_ONE != e->e_sendmode)
32315b0945b5SGregory Neil Shapiro #endif
32324e4196cbSGregory Neil Shapiro 			   )
3233c2aa98e2SPeter Wemm 				e->e_flags |= EF_VRFYONLY;
3234c2aa98e2SPeter Wemm 
323540266059SGregory Neil Shapiro #if MILTER
323606f25ae9SGregory Neil Shapiro 			/*
3237d0cef73dSGregory Neil Shapiro 			**  Do not expand recipients at RCPT time (in the call
3238ffb83623SGregory Neil Shapiro 			**  to recipient()) if a milter can delete or reject
3239ffb83623SGregory Neil Shapiro 			**  a RCPT.  If they are expanded, it is impossible
3240ffb83623SGregory Neil Shapiro 			**  for removefromlist() to figure out the expanded
3241ffb83623SGregory Neil Shapiro 			**  members of the original recipient and mark them
3242ffb83623SGregory Neil Shapiro 			**  as QS_DONTSEND.
324306f25ae9SGregory Neil Shapiro 			*/
324406f25ae9SGregory Neil Shapiro 
32455b0945b5SGregory Neil Shapiro 			if (smtp.sm_milterlist && smtp.sm_milterize &&
32465b0945b5SGregory Neil Shapiro 			      !bitset(EF_DISCARD, e->e_flags) &&
3247ffb83623SGregory Neil Shapiro 			    (smtp.sm_milters.mis_flags &
3248ffb83623SGregory Neil Shapiro 			     (MIS_FL_DEL_RCPT|MIS_FL_REJ_RCPT)) != 0)
324906f25ae9SGregory Neil Shapiro 				e->e_flags |= EF_VRFYONLY;
3250d0cef73dSGregory Neil Shapiro 			milter_cmd_done = false;
3251d0cef73dSGregory Neil Shapiro 			milter_cmd_safe = false;
325240266059SGregory Neil Shapiro #endif /* MILTER */
325306f25ae9SGregory Neil Shapiro 
3254c2aa98e2SPeter Wemm 			p = skipword(p, "to");
3255c2aa98e2SPeter Wemm 			if (p == NULL)
325640266059SGregory Neil Shapiro 				goto rcpt_done;
325740266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
325840266059SGregory Neil Shapiro 				macid("{addr_type}"), "e r");
32592fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
32602fb4f839SGregory Neil Shapiro 			len = sizeof(iaddr);
32612fb4f839SGregory Neil Shapiro 			origp = p;
32622fb4f839SGregory Neil Shapiro 
32632fb4f839SGregory Neil Shapiro 			/* HACK!!!! p is more than the address! */
32642fb4f839SGregory Neil Shapiro 			p = quote_internal_chars(p, iaddr, &len, NULL);
32652fb4f839SGregory Neil Shapiro #endif
326640266059SGregory Neil Shapiro 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
326740266059SGregory Neil Shapiro 				      e, true);
326840266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
326940266059SGregory Neil Shapiro 				macid("{addr_type}"), NULL);
3270065a643dSPeter Wemm 			if (Errors > 0)
327140266059SGregory Neil Shapiro 				goto rcpt_done;
3272065a643dSPeter Wemm 			if (a == NULL)
3273065a643dSPeter Wemm 			{
327406f25ae9SGregory Neil Shapiro 				usrerr("501 5.0.0 Missing recipient");
327540266059SGregory Neil Shapiro 				goto rcpt_done;
3276065a643dSPeter Wemm 			}
32772fb4f839SGregory Neil Shapiro #if USE_EAI
32782fb4f839SGregory Neil Shapiro 			CHECK_UTF8_ADDR(a->q_paddr, q);
32792fb4f839SGregory Neil Shapiro 			if (q != NULL)
32805b0945b5SGregory Neil Shapiro 			{
32812fb4f839SGregory Neil Shapiro 				usrerr(q);
32825b0945b5SGregory Neil Shapiro 				goto rcpt_done;
32835b0945b5SGregory Neil Shapiro 			}
32845b0945b5SGregory Neil Shapiro #endif
3285065a643dSPeter Wemm 
3286c2aa98e2SPeter Wemm 			if (delimptr != NULL && *delimptr != '\0')
32872fb4f839SGregory Neil Shapiro 			{
3288c2aa98e2SPeter Wemm 				*delimptr++ = '\0';
32892fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
32902fb4f839SGregory Neil Shapiro 				len = sizeof(iaddr) - (delimptr - iaddr);
32912fb4f839SGregory Neil Shapiro 				(void) dequote_internal_chars(delimptr, delimptr, len);
3292*d39bd2c1SGregory Neil Shapiro 				sep_args(delimptr, origp, e->e_id, p);
32932fb4f839SGregory Neil Shapiro #endif
32942fb4f839SGregory Neil Shapiro 			}
3295c2aa98e2SPeter Wemm 
329606f25ae9SGregory Neil Shapiro 			/* put resulting triple from parseaddr() into macros */
329706f25ae9SGregory Neil Shapiro 			if (a->q_mailer != NULL)
329840266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
329940266059SGregory Neil Shapiro 					macid("{rcpt_mailer}"),
330040266059SGregory Neil Shapiro 					a->q_mailer->m_name);
330106f25ae9SGregory Neil Shapiro 			else
330240266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
330340266059SGregory Neil Shapiro 					macid("{rcpt_mailer}"), NULL);
330406f25ae9SGregory Neil Shapiro 			if (a->q_host != NULL)
330540266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
330640266059SGregory Neil Shapiro 					macid("{rcpt_host}"), a->q_host);
330706f25ae9SGregory Neil Shapiro 			else
330840266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
330940266059SGregory Neil Shapiro 					macid("{rcpt_host}"), "localhost");
331006f25ae9SGregory Neil Shapiro 			if (a->q_user != NULL)
331140266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
331240266059SGregory Neil Shapiro 					macid("{rcpt_addr}"), a->q_user);
331306f25ae9SGregory Neil Shapiro 			else
331440266059SGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
331540266059SGregory Neil Shapiro 					macid("{rcpt_addr}"), NULL);
331606f25ae9SGregory Neil Shapiro 			if (Errors > 0)
331740266059SGregory Neil Shapiro 				goto rcpt_done;
3318c2aa98e2SPeter Wemm 
3319c2aa98e2SPeter Wemm 			/* now parse ESMTP arguments */
33202fb4f839SGregory Neil Shapiro 			addr = sm_rpool_strdup_x(e->e_rpool, p);
33212fb4f839SGregory Neil Shapiro 			parse_esmtp_args(e, a, origp, delimptr, "RCPT", args,
3322d0cef73dSGregory Neil Shapiro 					rcpt_esmtp_args);
332306f25ae9SGregory Neil Shapiro 			if (Errors > 0)
332440266059SGregory Neil Shapiro 				goto rcpt_done;
332506f25ae9SGregory Neil Shapiro 
3326d0cef73dSGregory Neil Shapiro #if MILTER
3327d0cef73dSGregory Neil Shapiro 			/*
3328d0cef73dSGregory Neil Shapiro 			**  rscheck() can trigger an "exception"
3329d0cef73dSGregory Neil Shapiro 			**  in which case the execution continues at
3330d0cef73dSGregory Neil Shapiro 			**  SM_EXCEPT(exc, "[!F]*")
3331d0cef73dSGregory Neil Shapiro 			**  This means milter_cmd_safe is not set
3332d0cef73dSGregory Neil Shapiro 			**  and hence milter is not invoked.
3333d0cef73dSGregory Neil Shapiro 			**  Would it be "safe" to change that, i.e., use
3334d0cef73dSGregory Neil Shapiro 			**  milter_cmd_safe = true;
3335d0cef73dSGregory Neil Shapiro 			**  here so a milter is informed (if requested)
3336d0cef73dSGregory Neil Shapiro 			**  about RCPTs that are rejected by check_rcpt?
3337d0cef73dSGregory Neil Shapiro 			*/
3338d0cef73dSGregory Neil Shapiro # if _FFR_MILTER_CHECK_REJECTIONS_TOO
3339d0cef73dSGregory Neil Shapiro 			milter_cmd_safe = true;
3340d0cef73dSGregory Neil Shapiro # endif
3341d0cef73dSGregory Neil Shapiro #endif
3342d0cef73dSGregory Neil Shapiro 
334306f25ae9SGregory Neil Shapiro 			/* do config file checking of the recipient */
334440266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
334540266059SGregory Neil Shapiro 				macid("{addr_type}"), "e r");
334606f25ae9SGregory Neil Shapiro 			if (rscheck("check_rcpt", addr,
3347959366dcSGregory Neil Shapiro 				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
3348da7d7b9cSGregory Neil Shapiro 				    NULL, e->e_id, p_addr_st, NULL) != EX_OK ||
334906f25ae9SGregory Neil Shapiro 			    Errors > 0)
335040266059SGregory Neil Shapiro 				goto rcpt_done;
335140266059SGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
335240266059SGregory Neil Shapiro 				macid("{addr_type}"), NULL);
335306f25ae9SGregory Neil Shapiro 
3354959366dcSGregory Neil Shapiro 			/* If discarding, don't bother to verify user */
3355959366dcSGregory Neil Shapiro 			if (bitset(EF_DISCARD, e->e_flags))
3356959366dcSGregory Neil Shapiro 				a->q_state = QS_VERIFIED;
3357d0cef73dSGregory Neil Shapiro #if MILTER
3358d0cef73dSGregory Neil Shapiro 			milter_cmd_safe = true;
3359d0cef73dSGregory Neil Shapiro #endif
3360d0cef73dSGregory Neil Shapiro 
3361da7d7b9cSGregory Neil Shapiro 			addbcc(a, e);
3362da7d7b9cSGregory Neil Shapiro 			rcptmods(a, e);
3363da7d7b9cSGregory Neil Shapiro 
3364d0cef73dSGregory Neil Shapiro 			/* save in recipient list after ESMTP mods */
3365d0cef73dSGregory Neil Shapiro 			a = recipient(a, &e->e_sendqueue, 0, e);
3366d0cef73dSGregory Neil Shapiro 			/* may trigger exception... */
3367959366dcSGregory Neil Shapiro 
336840266059SGregory Neil Shapiro #if MILTER
3369d0cef73dSGregory Neil Shapiro 			milter_rcpt_added = true;
3370d0cef73dSGregory Neil Shapiro #endif
3371d0cef73dSGregory Neil Shapiro 
3372d0cef73dSGregory Neil Shapiro 			if(!(Errors > 0) && QS_IS_BADADDR(a->q_state))
3373d0cef73dSGregory Neil Shapiro 			{
3374d0cef73dSGregory Neil Shapiro 				/* punt -- should keep message in ADDRESS.... */
3375d0cef73dSGregory Neil Shapiro 				usrerr("550 5.1.1 Addressee unknown");
3376d0cef73dSGregory Neil Shapiro 			}
3377d0cef73dSGregory Neil Shapiro 
3378d0cef73dSGregory Neil Shapiro #if MILTER
3379d0cef73dSGregory Neil Shapiro 		rcpt_done:
338040266059SGregory Neil Shapiro 			if (smtp.sm_milterlist && smtp.sm_milterize &&
338140266059SGregory Neil Shapiro 			    !bitset(EF_DISCARD, e->e_flags))
338206f25ae9SGregory Neil Shapiro 			{
338306f25ae9SGregory Neil Shapiro 				char state;
338406f25ae9SGregory Neil Shapiro 				char *response;
338506f25ae9SGregory Neil Shapiro 
3386d0cef73dSGregory Neil Shapiro 				/* how to get the error codes? */
3387d0cef73dSGregory Neil Shapiro 				if (Errors > 0)
3388d0cef73dSGregory Neil Shapiro 				{
3389d0cef73dSGregory Neil Shapiro 					macdefine(&e->e_macro, A_PERM,
3390d0cef73dSGregory Neil Shapiro 						macid("{rcpt_mailer}"),
3391d0cef73dSGregory Neil Shapiro 						"error");
3392d0cef73dSGregory Neil Shapiro 					if (a != NULL &&
3393d0cef73dSGregory Neil Shapiro 					    a->q_status != NULL &&
3394d0cef73dSGregory Neil Shapiro 					    a->q_rstatus != NULL)
3395d0cef73dSGregory Neil Shapiro 					{
3396d0cef73dSGregory Neil Shapiro 						macdefine(&e->e_macro, A_PERM,
3397d0cef73dSGregory Neil Shapiro 							macid("{rcpt_host}"),
3398d0cef73dSGregory Neil Shapiro 							a->q_status);
3399d0cef73dSGregory Neil Shapiro 						macdefine(&e->e_macro, A_PERM,
3400d0cef73dSGregory Neil Shapiro 							macid("{rcpt_addr}"),
3401d0cef73dSGregory Neil Shapiro 							a->q_rstatus);
3402d0cef73dSGregory Neil Shapiro 					}
3403d0cef73dSGregory Neil Shapiro 					else
3404d0cef73dSGregory Neil Shapiro 					{
3405d0cef73dSGregory Neil Shapiro 						if (addr_st.q_host != NULL)
3406d0cef73dSGregory Neil Shapiro 							macdefine(&e->e_macro,
3407d0cef73dSGregory Neil Shapiro 								A_PERM,
3408d0cef73dSGregory Neil Shapiro 								macid("{rcpt_host}"),
3409d0cef73dSGregory Neil Shapiro 								addr_st.q_host);
3410d0cef73dSGregory Neil Shapiro 						if (addr_st.q_user != NULL)
3411d0cef73dSGregory Neil Shapiro 							macdefine(&e->e_macro,
3412d0cef73dSGregory Neil Shapiro 								A_PERM,
3413d0cef73dSGregory Neil Shapiro 								macid("{rcpt_addr}"),
3414d0cef73dSGregory Neil Shapiro 								addr_st.q_user);
3415d0cef73dSGregory Neil Shapiro 					}
3416d0cef73dSGregory Neil Shapiro 				}
3417d0cef73dSGregory Neil Shapiro 
3418d0cef73dSGregory Neil Shapiro 				response = milter_envrcpt(args, e, &state,
3419d0cef73dSGregory Neil Shapiro 							Errors > 0);
3420d0cef73dSGregory Neil Shapiro 				milter_cmd_done = true;
342140266059SGregory Neil Shapiro 				MILTER_REPLY("to");
342206f25ae9SGregory Neil Shapiro 			}
342340266059SGregory Neil Shapiro #endif /* MILTER */
342406f25ae9SGregory Neil Shapiro 
3425c2aa98e2SPeter Wemm 			/* no errors during parsing, but might be a duplicate */
3426c2aa98e2SPeter Wemm 			e->e_to = a->q_paddr;
3427d0cef73dSGregory Neil Shapiro 			if (!(Errors > 0) && !QS_IS_BADADDR(a->q_state))
3428c2aa98e2SPeter Wemm 			{
342940266059SGregory Neil Shapiro 				if (smtp.sm_nrcpts == 0)
343006f25ae9SGregory Neil Shapiro 					initsys(e);
343106f25ae9SGregory Neil Shapiro 				message("250 2.1.5 Recipient ok%s",
343206f25ae9SGregory Neil Shapiro 					QS_IS_QUEUEUP(a->q_state) ?
3433c2aa98e2SPeter Wemm 						" (will queue)" : "");
343440266059SGregory Neil Shapiro 				smtp.sm_nrcpts++;
3435c2aa98e2SPeter Wemm 			}
3436d0cef73dSGregory Neil Shapiro 
3437d0cef73dSGregory Neil Shapiro 			/* Is this needed? */
3438d0cef73dSGregory Neil Shapiro #if !MILTER
343940266059SGregory Neil Shapiro 		rcpt_done:
34405b0945b5SGregory Neil Shapiro #endif
3441da7d7b9cSGregory Neil Shapiro 
3442d0cef73dSGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
3443d0cef73dSGregory Neil Shapiro 				macid("{rcpt_mailer}"), NULL);
3444d0cef73dSGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
3445d0cef73dSGregory Neil Shapiro 				macid("{rcpt_host}"), NULL);
3446d0cef73dSGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
3447d0cef73dSGregory Neil Shapiro 				macid("{rcpt_addr}"), NULL);
3448d0cef73dSGregory Neil Shapiro 			macdefine(&e->e_macro, A_PERM,
3449d0cef73dSGregory Neil Shapiro 				macid("{dsn_notify}"), NULL);
3450d0cef73dSGregory Neil Shapiro 
345140266059SGregory Neil Shapiro 			if (Errors > 0)
3452e92d3f3fSGregory Neil Shapiro 			{
345340266059SGregory Neil Shapiro 				++n_badrcpts;
3454e92d3f3fSGregory Neil Shapiro 				NBADRCPTS;
3455e92d3f3fSGregory Neil Shapiro 			}
345640266059SGregory Neil Shapiro 		    }
345740266059SGregory Neil Shapiro 		    SM_EXCEPT(exc, "[!F]*")
345840266059SGregory Neil Shapiro 		    {
345940266059SGregory Neil Shapiro 			/* An exception occurred while processing RCPT */
346040266059SGregory Neil Shapiro 			e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
346140266059SGregory Neil Shapiro 			++n_badrcpts;
3462e92d3f3fSGregory Neil Shapiro 			NBADRCPTS;
3463d0cef73dSGregory Neil Shapiro #if MILTER
3464d0cef73dSGregory Neil Shapiro 			if (smtp.sm_milterlist && smtp.sm_milterize &&
3465d0cef73dSGregory Neil Shapiro 			    !bitset(EF_DISCARD, e->e_flags) &&
3466d0cef73dSGregory Neil Shapiro 			    !milter_cmd_done && milter_cmd_safe)
3467d0cef73dSGregory Neil Shapiro 			{
3468d0cef73dSGregory Neil Shapiro 				char state;
3469d0cef73dSGregory Neil Shapiro 				char *response;
3470d0cef73dSGregory Neil Shapiro 
3471d0cef73dSGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
3472d0cef73dSGregory Neil Shapiro 					macid("{rcpt_mailer}"), "error");
3473d0cef73dSGregory Neil Shapiro 
3474d0cef73dSGregory Neil Shapiro 				/* how to get the error codes? */
3475d0cef73dSGregory Neil Shapiro 				if (addr_st.q_host != NULL)
3476d0cef73dSGregory Neil Shapiro 					macdefine(&e->e_macro, A_PERM,
3477d0cef73dSGregory Neil Shapiro 						macid("{rcpt_host}"),
3478d0cef73dSGregory Neil Shapiro 						addr_st.q_host);
3479d0cef73dSGregory Neil Shapiro 				else if (a != NULL && a->q_status != NULL)
3480d0cef73dSGregory Neil Shapiro 					macdefine(&e->e_macro, A_PERM,
3481d0cef73dSGregory Neil Shapiro 						macid("{rcpt_host}"),
3482d0cef73dSGregory Neil Shapiro 						a->q_status);
3483d0cef73dSGregory Neil Shapiro 
3484d0cef73dSGregory Neil Shapiro 				if (addr_st.q_user != NULL)
3485d0cef73dSGregory Neil Shapiro 					macdefine(&e->e_macro, A_PERM,
3486d0cef73dSGregory Neil Shapiro 						macid("{rcpt_addr}"),
3487d0cef73dSGregory Neil Shapiro 						addr_st.q_user);
3488d0cef73dSGregory Neil Shapiro 				else if (a != NULL && a->q_rstatus != NULL)
3489d0cef73dSGregory Neil Shapiro 					macdefine(&e->e_macro, A_PERM,
3490d0cef73dSGregory Neil Shapiro 						macid("{rcpt_addr}"),
3491d0cef73dSGregory Neil Shapiro 						a->q_rstatus);
3492d0cef73dSGregory Neil Shapiro 
3493d0cef73dSGregory Neil Shapiro 				response = milter_envrcpt(args, e, &state,
3494d0cef73dSGregory Neil Shapiro 							true);
3495d0cef73dSGregory Neil Shapiro 				milter_cmd_done = true;
3496d0cef73dSGregory Neil Shapiro 				MILTER_REPLY("to");
3497d0cef73dSGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
3498d0cef73dSGregory Neil Shapiro 					macid("{rcpt_mailer}"), NULL);
3499d0cef73dSGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
3500d0cef73dSGregory Neil Shapiro 					macid("{rcpt_host}"), NULL);
3501d0cef73dSGregory Neil Shapiro 				macdefine(&e->e_macro, A_PERM,
3502d0cef73dSGregory Neil Shapiro 					macid("{rcpt_addr}"), NULL);
3503d0cef73dSGregory Neil Shapiro 			}
3504d0cef73dSGregory Neil Shapiro 			if (smtp.sm_milterlist && smtp.sm_milterize &&
3505d0cef73dSGregory Neil Shapiro 			    milter_rcpt_added && milter_cmd_done &&
3506d0cef73dSGregory Neil Shapiro 			    milter_cmd_fail)
3507d0cef73dSGregory Neil Shapiro 			{
3508d0cef73dSGregory Neil Shapiro 				(void) removefromlist(addr, &e->e_sendqueue, e);
3509d0cef73dSGregory Neil Shapiro 				milter_cmd_fail = false;
3510ffb83623SGregory Neil Shapiro 				if (smtp.sm_e_nrcpts_orig < e->e_nrcpts)
3511ffb83623SGregory Neil Shapiro 					e->e_nrcpts = smtp.sm_e_nrcpts_orig;
3512d0cef73dSGregory Neil Shapiro 			}
3513d0cef73dSGregory Neil Shapiro #endif /* MILTER */
351440266059SGregory Neil Shapiro 		    }
351540266059SGregory Neil Shapiro 		    SM_END_TRY
3516c2aa98e2SPeter Wemm 			break;
3517c2aa98e2SPeter Wemm 
3518c2aa98e2SPeter Wemm 		  case CMDDATA:		/* data -- text of mail */
351940266059SGregory Neil Shapiro 			DELAY_CONN("DATA");
3520*d39bd2c1SGregory Neil Shapiro 			if (!smtp_data(&smtp, e,
3521*d39bd2c1SGregory Neil Shapiro 					bitset(SRV_BAD_PIPELINE, features)))
3522e92d3f3fSGregory Neil Shapiro 				goto doquit;
3523c2aa98e2SPeter Wemm 			break;
3524c2aa98e2SPeter Wemm 
3525c2aa98e2SPeter Wemm 		  case CMDRSET:		/* rset -- reset state */
3526c2aa98e2SPeter Wemm 			if (tTd(94, 100))
352706f25ae9SGregory Neil Shapiro 				message("451 4.0.0 Test failure");
3528c2aa98e2SPeter Wemm 			else
352906f25ae9SGregory Neil Shapiro 				message("250 2.0.0 Reset state");
353040266059SGregory Neil Shapiro 			CLEAR_STATE(cmdbuf);
3531c2aa98e2SPeter Wemm 			break;
3532c2aa98e2SPeter Wemm 
3533c2aa98e2SPeter Wemm 		  case CMDVRFY:		/* vrfy -- verify address */
3534c2aa98e2SPeter Wemm 		  case CMDEXPN:		/* expn -- expand address */
353540266059SGregory Neil Shapiro 			vrfy = c->cmd_code == CMDVRFY;
353640266059SGregory Neil Shapiro 			DELAY_CONN(vrfy ? "VRFY" : "EXPN");
353706f25ae9SGregory Neil Shapiro 			if (tempfail)
353806f25ae9SGregory Neil Shapiro 			{
353906f25ae9SGregory Neil Shapiro 				if (LogLevel > 9)
354006f25ae9SGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
354113bd1963SGregory Neil Shapiro 						  "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)",
354240266059SGregory Neil Shapiro 						  vrfy ? "VRFY" : "EXPN",
354306f25ae9SGregory Neil Shapiro 						  p, CurSmtpClient);
354440266059SGregory Neil Shapiro 
354540266059SGregory Neil Shapiro 				/* RFC 821 doesn't allow 4xy reply code */
354606f25ae9SGregory Neil Shapiro 				usrerr("550 5.7.1 Please try again later");
354706f25ae9SGregory Neil Shapiro 				break;
354806f25ae9SGregory Neil Shapiro 			}
354940266059SGregory Neil Shapiro 			wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
355040266059SGregory Neil Shapiro 					     false, vrfy ? "VRFY" : "EXPN", e);
3551e92d3f3fSGregory Neil Shapiro 			STOP_IF_ATTACK(wt);
355206f25ae9SGregory Neil Shapiro 			previous = curtime();
355313bd1963SGregory Neil Shapiro 			if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) ||
355413bd1963SGregory Neil Shapiro 			    (!vrfy && !bitset(SRV_OFFER_EXPN, features)))
3555c2aa98e2SPeter Wemm 			{
3556c2aa98e2SPeter Wemm 				if (vrfy)
355706f25ae9SGregory Neil Shapiro 					message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
3558c2aa98e2SPeter Wemm 				else
355906f25ae9SGregory Neil Shapiro 					message("502 5.7.0 Sorry, we do not allow this operation");
3560c2aa98e2SPeter Wemm 				if (LogLevel > 5)
3561c2aa98e2SPeter Wemm 					sm_syslog(LOG_INFO, e->e_id,
356213bd1963SGregory Neil Shapiro 						  "%s: %s [rejected]",
3563c2aa98e2SPeter Wemm 						  CurSmtpClient,
3564c2aa98e2SPeter Wemm 						  shortenstring(inp, MAXSHORTSTR));
3565c2aa98e2SPeter Wemm 				break;
3566c2aa98e2SPeter Wemm 			}
3567c2aa98e2SPeter Wemm 			else if (!gothello &&
3568c2aa98e2SPeter Wemm 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
3569c2aa98e2SPeter Wemm 						PrivacyFlags))
3570c2aa98e2SPeter Wemm 			{
357106f25ae9SGregory Neil Shapiro 				usrerr("503 5.0.0 I demand that you introduce yourself first");
3572c2aa98e2SPeter Wemm 				break;
3573c2aa98e2SPeter Wemm 			}
3574c2aa98e2SPeter Wemm 			if (Errors > 0)
357540266059SGregory Neil Shapiro 				break;
3576c2aa98e2SPeter Wemm 			if (LogLevel > 5)
357713bd1963SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id, "%s: %s",
3578c2aa98e2SPeter Wemm 					  CurSmtpClient,
3579c2aa98e2SPeter Wemm 					  shortenstring(inp, MAXSHORTSTR));
358040266059SGregory Neil Shapiro 		    SM_TRY
358140266059SGregory Neil Shapiro 		    {
358240266059SGregory Neil Shapiro 			QuickAbort = true;
3583c2aa98e2SPeter Wemm 			vrfyqueue = NULL;
3584c2aa98e2SPeter Wemm 			if (vrfy)
3585c2aa98e2SPeter Wemm 				e->e_flags |= EF_VRFYONLY;
35865b0945b5SGregory Neil Shapiro 			while (*p != '\0' && SM_ISSPACE(*p))
3587c2aa98e2SPeter Wemm 				p++;
3588c2aa98e2SPeter Wemm 			if (*p == '\0')
3589c2aa98e2SPeter Wemm 			{
359006f25ae9SGregory Neil Shapiro 				usrerr("501 5.5.2 Argument required");
3591c2aa98e2SPeter Wemm 			}
3592c2aa98e2SPeter Wemm 			else
3593c2aa98e2SPeter Wemm 			{
359406f25ae9SGregory Neil Shapiro 				/* do config file checking of the address */
359506f25ae9SGregory Neil Shapiro 				if (rscheck(vrfy ? "check_vrfy" : "check_expn",
3596da7d7b9cSGregory Neil Shapiro 					    p, NULL, e, RSF_RMCOMM, 3, NULL,
3597da7d7b9cSGregory Neil Shapiro 					    NOQID, NULL, NULL) != EX_OK ||
359840266059SGregory Neil Shapiro 				    Errors > 0)
359940266059SGregory Neil Shapiro 					sm_exc_raisenew_x(&EtypeQuickAbort, 1);
3600c2aa98e2SPeter Wemm 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
3601c2aa98e2SPeter Wemm 			}
360206f25ae9SGregory Neil Shapiro 			if (wt > 0)
3603193538b7SGregory Neil Shapiro 			{
3604193538b7SGregory Neil Shapiro 				time_t t;
3605193538b7SGregory Neil Shapiro 
3606193538b7SGregory Neil Shapiro 				t = wt - (curtime() - previous);
3607193538b7SGregory Neil Shapiro 				if (t > 0)
3608193538b7SGregory Neil Shapiro 					(void) sleep(t);
3609193538b7SGregory Neil Shapiro 			}
3610c2aa98e2SPeter Wemm 			if (Errors > 0)
361140266059SGregory Neil Shapiro 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
3612c2aa98e2SPeter Wemm 			if (vrfyqueue == NULL)
3613c2aa98e2SPeter Wemm 			{
361406f25ae9SGregory Neil Shapiro 				usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
3615c2aa98e2SPeter Wemm 			}
3616c2aa98e2SPeter Wemm 			while (vrfyqueue != NULL)
3617c2aa98e2SPeter Wemm 			{
361806f25ae9SGregory Neil Shapiro 				if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
361906f25ae9SGregory Neil Shapiro 				{
362006f25ae9SGregory Neil Shapiro 					vrfyqueue = vrfyqueue->q_next;
362106f25ae9SGregory Neil Shapiro 					continue;
362206f25ae9SGregory Neil Shapiro 				}
3623c2aa98e2SPeter Wemm 
362406f25ae9SGregory Neil Shapiro 				/* see if there is more in the vrfy list */
3625c2aa98e2SPeter Wemm 				a = vrfyqueue;
3626c2aa98e2SPeter Wemm 				while ((a = a->q_next) != NULL &&
362742e5d165SGregory Neil Shapiro 				       (!QS_IS_UNDELIVERED(a->q_state)))
3628c2aa98e2SPeter Wemm 					continue;
3629c2aa98e2SPeter Wemm 				printvrfyaddr(vrfyqueue, a == NULL, vrfy);
363006f25ae9SGregory Neil Shapiro 				vrfyqueue = a;
3631c2aa98e2SPeter Wemm 			}
363240266059SGregory Neil Shapiro 		    }
363340266059SGregory Neil Shapiro 		    SM_EXCEPT(exc, "[!F]*")
363440266059SGregory Neil Shapiro 		    {
363540266059SGregory Neil Shapiro 			/*
363640266059SGregory Neil Shapiro 			**  An exception occurred while processing VRFY/EXPN
363740266059SGregory Neil Shapiro 			*/
363840266059SGregory Neil Shapiro 
363940266059SGregory Neil Shapiro 			sm_exc_free(exc);
364040266059SGregory Neil Shapiro 			goto undo;
364140266059SGregory Neil Shapiro 		    }
364240266059SGregory Neil Shapiro 		    SM_END_TRY
3643c2aa98e2SPeter Wemm 			break;
3644c2aa98e2SPeter Wemm 
3645c2aa98e2SPeter Wemm 		  case CMDETRN:		/* etrn -- force queue flush */
364640266059SGregory Neil Shapiro 			DELAY_CONN("ETRN");
364740266059SGregory Neil Shapiro 
364840266059SGregory Neil Shapiro 			/* Don't leak queue information via debug flags */
364940266059SGregory Neil Shapiro 			if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
365040266059SGregory Neil Shapiro 			    (RealUid != 0 && RealUid != TrustedUid &&
365140266059SGregory Neil Shapiro 			     OpMode == MD_SMTP))
3652c2aa98e2SPeter Wemm 			{
365306f25ae9SGregory Neil Shapiro 				/* different message for MSA ? */
365406f25ae9SGregory Neil Shapiro 				message("502 5.7.0 Sorry, we do not allow this operation");
3655c2aa98e2SPeter Wemm 				if (LogLevel > 5)
3656c2aa98e2SPeter Wemm 					sm_syslog(LOG_INFO, e->e_id,
365713bd1963SGregory Neil Shapiro 						  "%s: %s [rejected]",
3658c2aa98e2SPeter Wemm 						  CurSmtpClient,
3659c2aa98e2SPeter Wemm 						  shortenstring(inp, MAXSHORTSTR));
3660c2aa98e2SPeter Wemm 				break;
3661c2aa98e2SPeter Wemm 			}
366206f25ae9SGregory Neil Shapiro 			if (tempfail)
366306f25ae9SGregory Neil Shapiro 			{
366406f25ae9SGregory Neil Shapiro 				if (LogLevel > 9)
366506f25ae9SGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
366613bd1963SGregory Neil Shapiro 						  "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)",
366706f25ae9SGregory Neil Shapiro 						  p, CurSmtpClient);
36685b0945b5SGregory Neil Shapiro 				/* Can't use ("%s", ...) due to usrerr() requirements */
366940266059SGregory Neil Shapiro 				usrerr(MSG_TEMPFAIL);
367006f25ae9SGregory Neil Shapiro 				break;
367106f25ae9SGregory Neil Shapiro 			}
3672c2aa98e2SPeter Wemm 
3673c2aa98e2SPeter Wemm 			if (strlen(p) <= 0)
3674c2aa98e2SPeter Wemm 			{
367506f25ae9SGregory Neil Shapiro 				usrerr("500 5.5.2 Parameter required");
3676c2aa98e2SPeter Wemm 				break;
3677c2aa98e2SPeter Wemm 			}
3678c2aa98e2SPeter Wemm 
3679c2aa98e2SPeter Wemm 			/* crude way to avoid denial-of-service attacks */
3680e92d3f3fSGregory Neil Shapiro 			STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS,
3681e92d3f3fSGregory Neil Shapiro 							true, "ETRN", e));
368206f25ae9SGregory Neil Shapiro 
368340266059SGregory Neil Shapiro 			/*
368440266059SGregory Neil Shapiro 			**  Do config file checking of the parameter.
368540266059SGregory Neil Shapiro 			**  Even though we have srv_features now, we still
368640266059SGregory Neil Shapiro 			**  need this ruleset because the former is called
368740266059SGregory Neil Shapiro 			**  when the connection has been established, while
368840266059SGregory Neil Shapiro 			**  this ruleset is called when the command is
368940266059SGregory Neil Shapiro 			**  actually issued and therefore has all information
369040266059SGregory Neil Shapiro 			**  available to make a decision.
369140266059SGregory Neil Shapiro 			*/
369240266059SGregory Neil Shapiro 
3693da7d7b9cSGregory Neil Shapiro 			if (rscheck("check_etrn", p, NULL, e, RSF_RMCOMM, 3,
3694da7d7b9cSGregory Neil Shapiro 				    NULL, NOQID, NULL, NULL) != EX_OK ||
3695959366dcSGregory Neil Shapiro 			    Errors > 0)
369606f25ae9SGregory Neil Shapiro 				break;
3697c2aa98e2SPeter Wemm 
3698c2aa98e2SPeter Wemm 			if (LogLevel > 5)
3699c2aa98e2SPeter Wemm 				sm_syslog(LOG_INFO, e->e_id,
370013bd1963SGregory Neil Shapiro 					  "%s: ETRN %s", CurSmtpClient,
3701c2aa98e2SPeter Wemm 					  shortenstring(p, MAXSHORTSTR));
3702c2aa98e2SPeter Wemm 
3703c2aa98e2SPeter Wemm 			id = p;
370440266059SGregory Neil Shapiro 			if (*id == '#')
370540266059SGregory Neil Shapiro 			{
3706d9986b26SGregory Neil Shapiro 				int i, qgrp;
370740266059SGregory Neil Shapiro 
370840266059SGregory Neil Shapiro 				id++;
3709d9986b26SGregory Neil Shapiro 				qgrp = name2qid(id);
3710d9986b26SGregory Neil Shapiro 				if (!ISVALIDQGRP(qgrp))
371140266059SGregory Neil Shapiro 				{
371240266059SGregory Neil Shapiro 					usrerr("459 4.5.4 Queue %s unknown",
371340266059SGregory Neil Shapiro 					       id);
371440266059SGregory Neil Shapiro 					break;
371540266059SGregory Neil Shapiro 				}
3716d9986b26SGregory Neil Shapiro 				for (i = 0; i < NumQueue && Queue[i] != NULL;
3717d9986b26SGregory Neil Shapiro 				     i++)
3718d9986b26SGregory Neil Shapiro 					Queue[i]->qg_nextrun = (time_t) -1;
3719d9986b26SGregory Neil Shapiro 				Queue[qgrp]->qg_nextrun = 0;
3720d9986b26SGregory Neil Shapiro 				ok = run_work_group(Queue[qgrp]->qg_wgrp,
3721d9986b26SGregory Neil Shapiro 						    RWG_FORK|RWG_FORCE);
372240266059SGregory Neil Shapiro 				if (ok && Errors == 0)
372340266059SGregory Neil Shapiro 					message("250 2.0.0 Queuing for queue group %s started", id);
372440266059SGregory Neil Shapiro 				break;
372540266059SGregory Neil Shapiro 			}
372640266059SGregory Neil Shapiro 
3727c2aa98e2SPeter Wemm 			if (*id == '@')
3728c2aa98e2SPeter Wemm 				id++;
3729c2aa98e2SPeter Wemm 			else
3730c2aa98e2SPeter Wemm 				*--id = '@';
3731c2aa98e2SPeter Wemm 
373240266059SGregory Neil Shapiro 			new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
373340266059SGregory Neil Shapiro 			if (new == NULL)
373440266059SGregory Neil Shapiro 			{
373540266059SGregory Neil Shapiro 				syserr("500 5.5.0 ETRN out of memory");
373640266059SGregory Neil Shapiro 				break;
373740266059SGregory Neil Shapiro 			}
3738c2aa98e2SPeter Wemm 			new->queue_match = id;
373940266059SGregory Neil Shapiro 			new->queue_negate = false;
3740c2aa98e2SPeter Wemm 			new->queue_next = NULL;
3741c2aa98e2SPeter Wemm 			QueueLimitRecipient = new;
374240266059SGregory Neil Shapiro 			ok = runqueue(true, false, false, true);
374340266059SGregory Neil Shapiro 			sm_free(QueueLimitRecipient); /* XXX */
3744c2aa98e2SPeter Wemm 			QueueLimitRecipient = NULL;
3745c2aa98e2SPeter Wemm 			if (ok && Errors == 0)
374606f25ae9SGregory Neil Shapiro 				message("250 2.0.0 Queuing for node %s started", p);
3747c2aa98e2SPeter Wemm 			break;
3748c2aa98e2SPeter Wemm 
3749c2aa98e2SPeter Wemm 		  case CMDHELP:		/* help -- give user info */
375040266059SGregory Neil Shapiro 			DELAY_CONN("HELP");
375106f25ae9SGregory Neil Shapiro 			help(p, e);
3752c2aa98e2SPeter Wemm 			break;
3753c2aa98e2SPeter Wemm 
37542fb4f839SGregory Neil Shapiro #define CHECK_OTHER(type) do	\
37552fb4f839SGregory Neil Shapiro 	{							\
37562fb4f839SGregory Neil Shapiro 		bool saveQuickAbort = QuickAbort;		\
37572fb4f839SGregory Neil Shapiro 		extern char MsgBuf[];				\
37582fb4f839SGregory Neil Shapiro 		int rsc;					\
37592fb4f839SGregory Neil Shapiro 		QuickAbort = false;				\
37602fb4f839SGregory Neil Shapiro 		if ((rsc = rscheck("check_other", inp, type, e,	\
37612fb4f839SGregory Neil Shapiro 			    RSF_UNSTRUCTURED, 3, NULL, NOQID, NULL, NULL)) \
37622fb4f839SGregory Neil Shapiro 			!= EX_OK ||				\
37632fb4f839SGregory Neil Shapiro 		    Errors > 0)					\
37642fb4f839SGregory Neil Shapiro 		{						\
37652fb4f839SGregory Neil Shapiro 			if (strncmp(MsgBuf, "421 ", 4) == 0)	\
37662fb4f839SGregory Neil Shapiro 			{					\
37672fb4f839SGregory Neil Shapiro 				e->e_sendqueue = NULL;		\
37682fb4f839SGregory Neil Shapiro 				goto doquit;			\
37692fb4f839SGregory Neil Shapiro 			}					\
37702fb4f839SGregory Neil Shapiro 		}						\
37712fb4f839SGregory Neil Shapiro 		QuickAbort = saveQuickAbort;			\
37722fb4f839SGregory Neil Shapiro 	} while (0)
37732fb4f839SGregory Neil Shapiro 
3774c2aa98e2SPeter Wemm 		  case CMDNOOP:		/* noop -- do nothing */
377540266059SGregory Neil Shapiro 			DELAY_CONN("NOOP");
37764e4196cbSGregory Neil Shapiro 			STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
3777e92d3f3fSGregory Neil Shapiro 							true, "NOOP", e));
37782fb4f839SGregory Neil Shapiro 			CHECK_OTHER("2");
377906f25ae9SGregory Neil Shapiro 			message("250 2.0.0 OK");
3780c2aa98e2SPeter Wemm 			break;
3781c2aa98e2SPeter Wemm 
3782c2aa98e2SPeter Wemm 		  case CMDQUIT:		/* quit -- leave mail */
378306f25ae9SGregory Neil Shapiro 			message("221 2.0.0 %s closing connection", MyHostName);
378440266059SGregory Neil Shapiro #if PIPELINING
378540266059SGregory Neil Shapiro 			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
37865b0945b5SGregory Neil Shapiro #endif
378740266059SGregory Neil Shapiro 
378840266059SGregory Neil Shapiro 			if (smtp.sm_nrcpts > 0)
378940266059SGregory Neil Shapiro 				logundelrcpts(e, "aborted by sender", 9, false);
3790c2aa98e2SPeter Wemm 
3791c2aa98e2SPeter Wemm 			/* arrange to ignore any current send list */
3792c2aa98e2SPeter Wemm 			e->e_sendqueue = NULL;
3793c2aa98e2SPeter Wemm 
379406f25ae9SGregory Neil Shapiro #if STARTTLS
379506f25ae9SGregory Neil Shapiro 			/* shutdown TLS connection */
379606f25ae9SGregory Neil Shapiro 			if (tls_active)
379706f25ae9SGregory Neil Shapiro 			{
37985b0945b5SGregory Neil Shapiro 				(void) endtls(&srv_ssl, "server");
379940266059SGregory Neil Shapiro 				tls_active = false;
380006f25ae9SGregory Neil Shapiro 			}
380106f25ae9SGregory Neil Shapiro #endif /* STARTTLS */
380206f25ae9SGregory Neil Shapiro #if SASL
3803*d39bd2c1SGregory Neil Shapiro 			if (auth_active)
380406f25ae9SGregory Neil Shapiro 			{
380506f25ae9SGregory Neil Shapiro 				sasl_dispose(&conn);
380606f25ae9SGregory Neil Shapiro 				authenticating = SASL_NOT_AUTH;
380740266059SGregory Neil Shapiro 				/* XXX sasl_done(); this is a child */
380806f25ae9SGregory Neil Shapiro 			}
380906f25ae9SGregory Neil Shapiro #endif /* SASL */
381006f25ae9SGregory Neil Shapiro 
381106f25ae9SGregory Neil Shapiro   doquit:
3812c2aa98e2SPeter Wemm 			/* avoid future 050 messages */
3813c2aa98e2SPeter Wemm 			disconnect(1, e);
3814c2aa98e2SPeter Wemm 
381540266059SGregory Neil Shapiro #if MILTER
381606f25ae9SGregory Neil Shapiro 			/* close out milter filters */
381706f25ae9SGregory Neil Shapiro 			milter_quit(e);
38185b0945b5SGregory Neil Shapiro #endif
381906f25ae9SGregory Neil Shapiro 
38209bd497b8SGregory Neil Shapiro 			if (tTd(92, 2))
38219bd497b8SGregory Neil Shapiro 				sm_dprintf("QUIT: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",
38229bd497b8SGregory Neil Shapiro 					e->e_id,
38239bd497b8SGregory Neil Shapiro 					bitset(EF_LOGSENDER, e->e_flags),
38249bd497b8SGregory Neil Shapiro 					LogLevel);
382506f25ae9SGregory Neil Shapiro 			if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
382606f25ae9SGregory Neil Shapiro 				logsender(e, NULL);
382706f25ae9SGregory Neil Shapiro 			e->e_flags &= ~EF_LOGSENDER;
382806f25ae9SGregory Neil Shapiro 
382994c01205SGregory Neil Shapiro 			if (lognullconnection && LogLevel > 5 &&
383094c01205SGregory Neil Shapiro 			    nullserver == NULL)
383106f25ae9SGregory Neil Shapiro 			{
383206f25ae9SGregory Neil Shapiro 				char *d;
383306f25ae9SGregory Neil Shapiro 
383440266059SGregory Neil Shapiro 				d = macvalue(macid("{daemon_name}"), e);
383506f25ae9SGregory Neil Shapiro 				if (d == NULL)
383606f25ae9SGregory Neil Shapiro 					d = "stdin";
383740266059SGregory Neil Shapiro 
383840266059SGregory Neil Shapiro 				/*
383940266059SGregory Neil Shapiro 				**  even though this id is "bogus", it makes
384040266059SGregory Neil Shapiro 				**  it simpler to "grep" related events, e.g.,
384140266059SGregory Neil Shapiro 				**  timeouts for the same connection.
384240266059SGregory Neil Shapiro 				*/
384340266059SGregory Neil Shapiro 
384440266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
384513bd1963SGregory Neil Shapiro 					  "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
384606f25ae9SGregory Neil Shapiro 					  CurSmtpClient, d);
384706f25ae9SGregory Neil Shapiro 			}
384813bd1963SGregory Neil Shapiro 			if (tTd(93, 100))
384913bd1963SGregory Neil Shapiro 			{
385013bd1963SGregory Neil Shapiro 				/* return to handle next connection */
38512fb4f839SGregory Neil Shapiro #if SM_HEAP_CHECK
38522fb4f839SGregory Neil Shapiro # define SM_HC_TRIGGER "heapdump"
38532fb4f839SGregory Neil Shapiro 				if (sm_debug_active(&SmHeapCheck, 2)
38542fb4f839SGregory Neil Shapiro 				    && access(SM_HC_TRIGGER, F_OK) == 0
38552fb4f839SGregory Neil Shapiro 				   )
38562fb4f839SGregory Neil Shapiro 				{
38572fb4f839SGregory Neil Shapiro 					SM_FILE_T *out;
38582fb4f839SGregory Neil Shapiro 
38592fb4f839SGregory Neil Shapiro 					remove(SM_HC_TRIGGER);
38602fb4f839SGregory Neil Shapiro 					out = sm_io_open(SmFtStdio,
38612fb4f839SGregory Neil Shapiro 						SM_TIME_DEFAULT, SM_HC_TRIGGER ".heap",
38622fb4f839SGregory Neil Shapiro 						SM_IO_APPEND, NULL);
38632fb4f839SGregory Neil Shapiro 					if (out != NULL)
38642fb4f839SGregory Neil Shapiro 					{
38652fb4f839SGregory Neil Shapiro 						(void) sm_io_fprintf(out, SM_TIME_DEFAULT, "----------------------\n");
38662fb4f839SGregory Neil Shapiro 						sm_heap_report(out,
38672fb4f839SGregory Neil Shapiro 							sm_debug_level(&SmHeapCheck) - 1);
38682fb4f839SGregory Neil Shapiro 						(void) sm_io_close(out, SM_TIME_DEFAULT);
38692fb4f839SGregory Neil Shapiro 					}
38702fb4f839SGregory Neil Shapiro 				}
38712fb4f839SGregory Neil Shapiro #endif /* SM_HEAP_CHECK */
387240266059SGregory Neil Shapiro 				return;
387313bd1963SGregory Neil Shapiro 			}
387440266059SGregory Neil Shapiro 			finis(true, true, ExitStat);
387506f25ae9SGregory Neil Shapiro 			/* NOTREACHED */
3876c2aa98e2SPeter Wemm 
38774e4196cbSGregory Neil Shapiro 			/* just to avoid bogus warning from some compilers */
38784e4196cbSGregory Neil Shapiro 			exit(EX_OSERR);
38794e4196cbSGregory Neil Shapiro 
3880c2aa98e2SPeter Wemm 		  case CMDVERB:		/* set verbose mode */
388140266059SGregory Neil Shapiro 			DELAY_CONN("VERB");
388213bd1963SGregory Neil Shapiro 			if (!bitset(SRV_OFFER_EXPN, features) ||
388313bd1963SGregory Neil Shapiro 			    !bitset(SRV_OFFER_VERB, features))
3884c2aa98e2SPeter Wemm 			{
3885c2aa98e2SPeter Wemm 				/* this would give out the same info */
388606f25ae9SGregory Neil Shapiro 				message("502 5.7.0 Verbose unavailable");
3887c2aa98e2SPeter Wemm 				break;
3888c2aa98e2SPeter Wemm 			}
38894e4196cbSGregory Neil Shapiro 			STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
3890e92d3f3fSGregory Neil Shapiro 							true, "VERB", e));
38912fb4f839SGregory Neil Shapiro 			CHECK_OTHER("2");
3892c2aa98e2SPeter Wemm 			Verbose = 1;
389306f25ae9SGregory Neil Shapiro 			set_delivery_mode(SM_DELIVER, e);
389406f25ae9SGregory Neil Shapiro 			message("250 2.0.0 Verbose mode");
3895c2aa98e2SPeter Wemm 			break;
3896c2aa98e2SPeter Wemm 
3897c2aa98e2SPeter Wemm #if SMTPDEBUG
3898c2aa98e2SPeter Wemm 		  case CMDDBGQSHOW:	/* show queues */
38992fb4f839SGregory Neil Shapiro 			CHECK_OTHER("2");
390040266059SGregory Neil Shapiro 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
390140266059SGregory Neil Shapiro 					     "Send Queue=");
3902e92d3f3fSGregory Neil Shapiro 			printaddr(smioout, e->e_sendqueue, true);
3903c2aa98e2SPeter Wemm 			break;
3904c2aa98e2SPeter Wemm 
3905c2aa98e2SPeter Wemm 		  case CMDDBGDEBUG:	/* set debug mode */
39062fb4f839SGregory Neil Shapiro 			CHECK_OTHER("2");
3907d0cef73dSGregory Neil Shapiro 			tTsetup(tTdvect, sizeof(tTdvect), "0-99.1");
3908c2aa98e2SPeter Wemm 			tTflag(p);
390906f25ae9SGregory Neil Shapiro 			message("200 2.0.0 Debug set");
3910c2aa98e2SPeter Wemm 			break;
3911c2aa98e2SPeter Wemm 
391206f25ae9SGregory Neil Shapiro #else /* SMTPDEBUG */
3913c2aa98e2SPeter Wemm 		  case CMDDBGQSHOW:	/* show queues */
3914c2aa98e2SPeter Wemm 		  case CMDDBGDEBUG:	/* set debug mode */
3915c2aa98e2SPeter Wemm #endif /* SMTPDEBUG */
3916c2aa98e2SPeter Wemm 		  case CMDLOGBOGUS:	/* bogus command */
391740266059SGregory Neil Shapiro 			DELAY_CONN("Bogus");
3918c2aa98e2SPeter Wemm 			if (LogLevel > 0)
3919c2aa98e2SPeter Wemm 				sm_syslog(LOG_CRIT, e->e_id,
392013bd1963SGregory Neil Shapiro 					  "\"%s\" command from %s (%.100s)",
392106f25ae9SGregory Neil Shapiro 					  c->cmd_name, CurSmtpClient,
3922c2aa98e2SPeter Wemm 					  anynet_ntoa(&RealHostAddr));
3923c2aa98e2SPeter Wemm 			/* FALLTHROUGH */
3924c2aa98e2SPeter Wemm 
3925c2aa98e2SPeter Wemm 		  case CMDERROR:	/* unknown command */
392640266059SGregory Neil Shapiro #if MAXBADCOMMANDS > 0
392740266059SGregory Neil Shapiro 			if (++n_badcmds > MAXBADCOMMANDS)
3928c2aa98e2SPeter Wemm 			{
3929e92d3f3fSGregory Neil Shapiro   stopattack:
393006f25ae9SGregory Neil Shapiro 				message("421 4.7.0 %s Too many bad commands; closing connection",
3931c2aa98e2SPeter Wemm 					MyHostName);
393206f25ae9SGregory Neil Shapiro 
393306f25ae9SGregory Neil Shapiro 				/* arrange to ignore any current send list */
393406f25ae9SGregory Neil Shapiro 				e->e_sendqueue = NULL;
3935c2aa98e2SPeter Wemm 				goto doquit;
3936c2aa98e2SPeter Wemm 			}
393740266059SGregory Neil Shapiro #endif /* MAXBADCOMMANDS > 0 */
3938c2aa98e2SPeter Wemm 
3939e92d3f3fSGregory Neil Shapiro #if MILTER && SMFI_VERSION > 2
3940e92d3f3fSGregory Neil Shapiro 			if (smtp.sm_milterlist && smtp.sm_milterize &&
3941e92d3f3fSGregory Neil Shapiro 			    !bitset(EF_DISCARD, e->e_flags))
3942e92d3f3fSGregory Neil Shapiro 			{
3943e92d3f3fSGregory Neil Shapiro 				char state;
3944e92d3f3fSGregory Neil Shapiro 				char *response;
3945e92d3f3fSGregory Neil Shapiro 
3946e92d3f3fSGregory Neil Shapiro 				if (MilterLogLevel > 9)
3947e92d3f3fSGregory Neil Shapiro 					sm_syslog(LOG_INFO, e->e_id,
3948e92d3f3fSGregory Neil Shapiro 						"Sending \"%s\" to Milter", inp);
3949e92d3f3fSGregory Neil Shapiro 				response = milter_unknown(inp, e, &state);
3950e92d3f3fSGregory Neil Shapiro 				MILTER_REPLY("unknown");
3951e92d3f3fSGregory Neil Shapiro 				if (state == SMFIR_REPLYCODE ||
3952e92d3f3fSGregory Neil Shapiro 				    state == SMFIR_REJECT ||
39534e4196cbSGregory Neil Shapiro 				    state == SMFIR_TEMPFAIL ||
39544e4196cbSGregory Neil Shapiro 				    state == SMFIR_SHUTDOWN)
3955e92d3f3fSGregory Neil Shapiro 				{
3956e92d3f3fSGregory Neil Shapiro 					/* MILTER_REPLY already gave an error */
3957e92d3f3fSGregory Neil Shapiro 					break;
3958e92d3f3fSGregory Neil Shapiro 				}
3959e92d3f3fSGregory Neil Shapiro 			}
3960e92d3f3fSGregory Neil Shapiro #endif /* MILTER && SMFI_VERSION > 2 */
3961e92d3f3fSGregory Neil Shapiro 
39622fb4f839SGregory Neil Shapiro 			CHECK_OTHER("5");
396306f25ae9SGregory Neil Shapiro 			usrerr("500 5.5.1 Command unrecognized: \"%s\"",
39642fb4f839SGregory Neil Shapiro 			       SHOWSHRTCMDINREPLY(inp));
396506f25ae9SGregory Neil Shapiro 			break;
396606f25ae9SGregory Neil Shapiro 
396706f25ae9SGregory Neil Shapiro 		  case CMDUNIMPL:
396840266059SGregory Neil Shapiro 			DELAY_CONN("Unimpl");
39692fb4f839SGregory Neil Shapiro 			CHECK_OTHER("5");
397006f25ae9SGregory Neil Shapiro 			usrerr("502 5.5.1 Command not implemented: \"%s\"",
39712fb4f839SGregory Neil Shapiro 			       SHOWSHRTCMDINREPLY(inp));
3972c2aa98e2SPeter Wemm 			break;
3973c2aa98e2SPeter Wemm 
3974c2aa98e2SPeter Wemm 		  default:
397540266059SGregory Neil Shapiro 			DELAY_CONN("default");
39762fb4f839SGregory Neil Shapiro 			CHECK_OTHER("5");
3977c2aa98e2SPeter Wemm 			errno = 0;
397806f25ae9SGregory Neil Shapiro 			syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
3979c2aa98e2SPeter Wemm 			break;
3980c2aa98e2SPeter Wemm 		}
398106f25ae9SGregory Neil Shapiro #if SASL
3982c2aa98e2SPeter Wemm 		}
39835b0945b5SGregory Neil Shapiro #endif
398406f25ae9SGregory Neil Shapiro 	    }
398540266059SGregory Neil Shapiro 	    SM_EXCEPT(exc, "[!F]*")
398640266059SGregory Neil Shapiro 	    {
398740266059SGregory Neil Shapiro 		/*
398840266059SGregory Neil Shapiro 		**  The only possible exception is "E:mta.quickabort".
398940266059SGregory Neil Shapiro 		**  There is nothing to do except fall through and loop.
399040266059SGregory Neil Shapiro 		*/
3991c2aa98e2SPeter Wemm 	    }
399240266059SGregory Neil Shapiro 	    SM_END_TRY
399340266059SGregory Neil Shapiro 	}
399440266059SGregory Neil Shapiro }
399540266059SGregory Neil Shapiro /*
399640266059SGregory Neil Shapiro **  SMTP_DATA -- implement the SMTP DATA command.
399740266059SGregory Neil Shapiro **
399840266059SGregory Neil Shapiro **	Parameters:
399940266059SGregory Neil Shapiro **		smtp -- status of SMTP connection.
400040266059SGregory Neil Shapiro **		e -- envelope.
4001*d39bd2c1SGregory Neil Shapiro **		check_stuffing -- check for transaction stuffing.
400240266059SGregory Neil Shapiro **
400340266059SGregory Neil Shapiro **	Returns:
4004e92d3f3fSGregory Neil Shapiro **		true iff SMTP session can continue.
400540266059SGregory Neil Shapiro **
400640266059SGregory Neil Shapiro **	Side Effects:
400740266059SGregory Neil Shapiro **		possibly sends message.
400840266059SGregory Neil Shapiro */
400940266059SGregory Neil Shapiro 
4010e92d3f3fSGregory Neil Shapiro static bool
smtp_data(smtp,e,check_stuffing)4011*d39bd2c1SGregory Neil Shapiro smtp_data(smtp, e, check_stuffing)
401240266059SGregory Neil Shapiro 	SMTP_T *smtp;
401340266059SGregory Neil Shapiro 	ENVELOPE *e;
4014*d39bd2c1SGregory Neil Shapiro 	bool check_stuffing;
401540266059SGregory Neil Shapiro {
401640266059SGregory Neil Shapiro #if MILTER
401740266059SGregory Neil Shapiro 	bool milteraccept;
40185b0945b5SGregory Neil Shapiro #endif
401940266059SGregory Neil Shapiro 	bool aborting;
402040266059SGregory Neil Shapiro 	bool doublequeue;
4021d0cef73dSGregory Neil Shapiro 	bool rv = true;
402240266059SGregory Neil Shapiro 	ADDRESS *a;
402340266059SGregory Neil Shapiro 	ENVELOPE *ee;
402440266059SGregory Neil Shapiro 	char *id;
402594c01205SGregory Neil Shapiro 	char *oldid;
4026*d39bd2c1SGregory Neil Shapiro 	unsigned long features;
402740266059SGregory Neil Shapiro 	char buf[32];
402840266059SGregory Neil Shapiro 
402940266059SGregory Neil Shapiro 	SmtpPhase = "server DATA";
403040266059SGregory Neil Shapiro 	if (!smtp->sm_gotmail)
403140266059SGregory Neil Shapiro 	{
403240266059SGregory Neil Shapiro 		usrerr("503 5.0.0 Need MAIL command");
4033e92d3f3fSGregory Neil Shapiro 		return true;
403440266059SGregory Neil Shapiro 	}
403540266059SGregory Neil Shapiro 	else if (smtp->sm_nrcpts <= 0)
403640266059SGregory Neil Shapiro 	{
403740266059SGregory Neil Shapiro 		usrerr("503 5.0.0 Need RCPT (recipient)");
4038e92d3f3fSGregory Neil Shapiro 		return true;
403940266059SGregory Neil Shapiro 	}
4040*d39bd2c1SGregory Neil Shapiro 
4041*d39bd2c1SGregory Neil Shapiro 	/* check if data is on the socket before the DATA reply */
4042*d39bd2c1SGregory Neil Shapiro 	if (check_stuffing &&
4043*d39bd2c1SGregory Neil Shapiro 	    sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
4044*d39bd2c1SGregory Neil Shapiro 	{
4045*d39bd2c1SGregory Neil Shapiro 		sm_syslog(LOG_INFO, e->e_id,
4046*d39bd2c1SGregory Neil Shapiro 			"rejecting %s from %s [%s] due to traffic before response",
4047*d39bd2c1SGregory Neil Shapiro 			SmtpPhase, CurHostName, anynet_ntoa(&RealHostAddr));
4048*d39bd2c1SGregory Neil Shapiro 		usrerr("554 5.5.0 SMTP protocol error");
4049*d39bd2c1SGregory Neil Shapiro 		return false;
4050*d39bd2c1SGregory Neil Shapiro 	}
4051*d39bd2c1SGregory Neil Shapiro 
4052d0cef73dSGregory Neil Shapiro 	(void) sm_snprintf(buf, sizeof(buf), "%u", smtp->sm_nrcpts);
405340266059SGregory Neil Shapiro 	if (rscheck("check_data", buf, NULL, e,
4054959366dcSGregory Neil Shapiro 		    RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
4055da7d7b9cSGregory Neil Shapiro 		    e->e_id, NULL, NULL) != EX_OK)
4056e92d3f3fSGregory Neil Shapiro 		return true;
4057e92d3f3fSGregory Neil Shapiro 
4058e92d3f3fSGregory Neil Shapiro #if MILTER && SMFI_VERSION > 3
4059e92d3f3fSGregory Neil Shapiro 	if (smtp->sm_milterlist && smtp->sm_milterize &&
4060e92d3f3fSGregory Neil Shapiro 	    !bitset(EF_DISCARD, e->e_flags))
4061e92d3f3fSGregory Neil Shapiro 	{
4062e92d3f3fSGregory Neil Shapiro 		char state;
4063e92d3f3fSGregory Neil Shapiro 		char *response;
4064e92d3f3fSGregory Neil Shapiro 		int savelogusrerrs = LogUsrErrs;
4065e92d3f3fSGregory Neil Shapiro 
4066e92d3f3fSGregory Neil Shapiro 		response = milter_data_cmd(e, &state);
4067e92d3f3fSGregory Neil Shapiro 		switch (state)
4068e92d3f3fSGregory Neil Shapiro 		{
4069e92d3f3fSGregory Neil Shapiro 		  case SMFIR_REPLYCODE:
4070e92d3f3fSGregory Neil Shapiro 			if (MilterLogLevel > 3)
4071e92d3f3fSGregory Neil Shapiro 			{
4072e92d3f3fSGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
4073e92d3f3fSGregory Neil Shapiro 					  "Milter: cmd=data, reject=%s",
4074e92d3f3fSGregory Neil Shapiro 					  response);
4075e92d3f3fSGregory Neil Shapiro 				LogUsrErrs = false;
4076e92d3f3fSGregory Neil Shapiro 			}
40779bd497b8SGregory Neil Shapiro # if _FFR_MILTER_ENHSC
40789bd497b8SGregory Neil Shapiro 			if (ISSMTPCODE(response))
40799bd497b8SGregory Neil Shapiro 				(void) extenhsc(response + 4, ' ', e->e_enhsc);
40805b0945b5SGregory Neil Shapiro # endif
40819bd497b8SGregory Neil Shapiro 
40825b0945b5SGregory Neil Shapiro 			/* Can't use ("%s", ...) due to usrerr() requirements */
4083e92d3f3fSGregory Neil Shapiro 			usrerr(response);
40844e4196cbSGregory Neil Shapiro 			if (strncmp(response, "421 ", 4) == 0
40854e4196cbSGregory Neil Shapiro 			    || strncmp(response, "421-", 4) == 0)
4086e92d3f3fSGregory Neil Shapiro 			{
4087e92d3f3fSGregory Neil Shapiro 				e->e_sendqueue = NULL;
4088e92d3f3fSGregory Neil Shapiro 				return false;
4089e92d3f3fSGregory Neil Shapiro 			}
4090e92d3f3fSGregory Neil Shapiro 			return true;
4091e92d3f3fSGregory Neil Shapiro 
4092e92d3f3fSGregory Neil Shapiro 		  case SMFIR_REJECT:
4093e92d3f3fSGregory Neil Shapiro 			if (MilterLogLevel > 3)
4094e92d3f3fSGregory Neil Shapiro 			{
4095e92d3f3fSGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
4096e92d3f3fSGregory Neil Shapiro 					  "Milter: cmd=data, reject=550 5.7.1 Command rejected");
4097e92d3f3fSGregory Neil Shapiro 				LogUsrErrs = false;
4098e92d3f3fSGregory Neil Shapiro 			}
40999bd497b8SGregory Neil Shapiro # if _FFR_MILTER_ENHSC
41009bd497b8SGregory Neil Shapiro 			(void) sm_strlcpy(e->e_enhsc, "5.7.1",
41019bd497b8SGregory Neil Shapiro 					 sizeof(e->e_enhsc));
41025b0945b5SGregory Neil Shapiro # endif
4103e92d3f3fSGregory Neil Shapiro 			usrerr("550 5.7.1 Command rejected");
4104e92d3f3fSGregory Neil Shapiro 			return true;
4105e92d3f3fSGregory Neil Shapiro 
4106e92d3f3fSGregory Neil Shapiro 		  case SMFIR_DISCARD:
4107e92d3f3fSGregory Neil Shapiro 			if (MilterLogLevel > 3)
4108e92d3f3fSGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
4109e92d3f3fSGregory Neil Shapiro 					  "Milter: cmd=data, discard");
4110e92d3f3fSGregory Neil Shapiro 			e->e_flags |= EF_DISCARD;
4111e92d3f3fSGregory Neil Shapiro 			break;
4112e92d3f3fSGregory Neil Shapiro 
4113e92d3f3fSGregory Neil Shapiro 		  case SMFIR_TEMPFAIL:
4114e92d3f3fSGregory Neil Shapiro 			if (MilterLogLevel > 3)
4115e92d3f3fSGregory Neil Shapiro 			{
4116e92d3f3fSGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
4117e92d3f3fSGregory Neil Shapiro 					  "Milter: cmd=data, reject=%s",
4118e92d3f3fSGregory Neil Shapiro 					  MSG_TEMPFAIL);
4119e92d3f3fSGregory Neil Shapiro 				LogUsrErrs = false;
4120e92d3f3fSGregory Neil Shapiro 			}
41219bd497b8SGregory Neil Shapiro # if _FFR_MILTER_ENHSC
41229bd497b8SGregory Neil Shapiro 			(void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
41235b0945b5SGregory Neil Shapiro # endif
41245b0945b5SGregory Neil Shapiro 			/* Can't use ("%s", ...) due to usrerr() requirements */
4125e92d3f3fSGregory Neil Shapiro 			usrerr(MSG_TEMPFAIL);
4126e92d3f3fSGregory Neil Shapiro 			return true;
41274e4196cbSGregory Neil Shapiro 
41284e4196cbSGregory Neil Shapiro 		  case SMFIR_SHUTDOWN:
41294e4196cbSGregory Neil Shapiro 			if (MilterLogLevel > 3)
41304e4196cbSGregory Neil Shapiro 			{
41314e4196cbSGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
41324e4196cbSGregory Neil Shapiro 					  "Milter: cmd=data, reject=421 4.7.0 %s closing connection",
41334e4196cbSGregory Neil Shapiro 					  MyHostName);
41344e4196cbSGregory Neil Shapiro 				LogUsrErrs = false;
41354e4196cbSGregory Neil Shapiro 			}
41364e4196cbSGregory Neil Shapiro 			usrerr("421 4.7.0 %s closing connection", MyHostName);
41374e4196cbSGregory Neil Shapiro 			e->e_sendqueue = NULL;
41384e4196cbSGregory Neil Shapiro 			return false;
4139e92d3f3fSGregory Neil Shapiro 		}
4140e92d3f3fSGregory Neil Shapiro 		LogUsrErrs = savelogusrerrs;
4141e92d3f3fSGregory Neil Shapiro 		if (response != NULL)
4142e92d3f3fSGregory Neil Shapiro 			sm_free(response); /* XXX */
4143e92d3f3fSGregory Neil Shapiro 	}
4144e92d3f3fSGregory Neil Shapiro #endif /* MILTER && SMFI_VERSION > 3 */
414540266059SGregory Neil Shapiro 
414640266059SGregory Neil Shapiro 	/* put back discard bit */
414740266059SGregory Neil Shapiro 	if (smtp->sm_discard)
414840266059SGregory Neil Shapiro 		e->e_flags |= EF_DISCARD;
414940266059SGregory Neil Shapiro 
415040266059SGregory Neil Shapiro 	/* check to see if we need to re-expand aliases */
415140266059SGregory Neil Shapiro 	/* also reset QS_BADADDR on already-diagnosted addrs */
415240266059SGregory Neil Shapiro 	doublequeue = false;
415340266059SGregory Neil Shapiro 	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
415440266059SGregory Neil Shapiro 	{
415540266059SGregory Neil Shapiro 		if (QS_IS_VERIFIED(a->q_state) &&
415640266059SGregory Neil Shapiro 		    !bitset(EF_DISCARD, e->e_flags))
415740266059SGregory Neil Shapiro 		{
415840266059SGregory Neil Shapiro 			/* need to re-expand aliases */
415940266059SGregory Neil Shapiro 			doublequeue = true;
416040266059SGregory Neil Shapiro 		}
416140266059SGregory Neil Shapiro 		if (QS_IS_BADADDR(a->q_state))
416240266059SGregory Neil Shapiro 		{
416340266059SGregory Neil Shapiro 			/* make this "go away" */
416440266059SGregory Neil Shapiro 			a->q_state = QS_DONTSEND;
416540266059SGregory Neil Shapiro 		}
416640266059SGregory Neil Shapiro 	}
416740266059SGregory Neil Shapiro 
416840266059SGregory Neil Shapiro 	/* collect the text of the message */
416940266059SGregory Neil Shapiro 	SmtpPhase = "collect";
417040266059SGregory Neil Shapiro 	buffer_errors();
417140266059SGregory Neil Shapiro 
4172*d39bd2c1SGregory Neil Shapiro 	collect(InChannel, SMTPMODE_LAX
4173*d39bd2c1SGregory Neil Shapiro 		| (bitset(SRV_BARE_LF_421, e->e_features) ? SMTPMODE_LF_421 : 0)
4174*d39bd2c1SGregory Neil Shapiro 		| (bitset(SRV_BARE_CR_421, e->e_features) ? SMTPMODE_CR_421 : 0)
4175*d39bd2c1SGregory Neil Shapiro 		| (bitset(SRV_BARE_LF_SP, e->e_features) ? SMTPMODE_LF_SP : 0)
4176*d39bd2c1SGregory Neil Shapiro 		| (bitset(SRV_BARE_CR_SP, e->e_features) ? SMTPMODE_CR_SP : 0)
4177*d39bd2c1SGregory Neil Shapiro 		| (bitset(SRV_REQ_CRLF, e->e_features) ? SMTPMODE_CRLF : 0),
4178*d39bd2c1SGregory Neil Shapiro 		NULL, e, true);
417940266059SGregory Neil Shapiro 
418040266059SGregory Neil Shapiro 	/* redefine message size */
4181ba00ec3dSGregory Neil Shapiro 	(void) sm_snprintf(buf, sizeof(buf), "%ld", PRT_NONNEGL(e->e_msgsize));
418240266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
418340266059SGregory Neil Shapiro 
418440266059SGregory Neil Shapiro 	/* rscheck() will set Errors or EF_DISCARD if it trips */
4185959366dcSGregory Neil Shapiro 	(void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT,
4186da7d7b9cSGregory Neil Shapiro 		       3, NULL, e->e_id, NULL, NULL);
418740266059SGregory Neil Shapiro 
418840266059SGregory Neil Shapiro #if MILTER
418940266059SGregory Neil Shapiro 	milteraccept = true;
419040266059SGregory Neil Shapiro 	if (smtp->sm_milterlist && smtp->sm_milterize &&
419140266059SGregory Neil Shapiro 	    Errors <= 0 &&
419240266059SGregory Neil Shapiro 	    !bitset(EF_DISCARD, e->e_flags))
419340266059SGregory Neil Shapiro 	{
419440266059SGregory Neil Shapiro 		char state;
419540266059SGregory Neil Shapiro 		char *response;
419640266059SGregory Neil Shapiro 
419740266059SGregory Neil Shapiro 		response = milter_data(e, &state);
419840266059SGregory Neil Shapiro 		switch (state)
419940266059SGregory Neil Shapiro 		{
420040266059SGregory Neil Shapiro 		  case SMFIR_REPLYCODE:
420140266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)
420240266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
420340266059SGregory Neil Shapiro 					  "Milter: data, reject=%s",
420440266059SGregory Neil Shapiro 					  response);
420540266059SGregory Neil Shapiro 			milteraccept = false;
42069bd497b8SGregory Neil Shapiro # if _FFR_MILTER_ENHSC
42079bd497b8SGregory Neil Shapiro 			if (ISSMTPCODE(response))
42089bd497b8SGregory Neil Shapiro 				(void) extenhsc(response + 4, ' ', e->e_enhsc);
42095b0945b5SGregory Neil Shapiro # endif
42105b0945b5SGregory Neil Shapiro 			/* Can't use ("%s", ...) due to usrerr() requirements */
421140266059SGregory Neil Shapiro 			usrerr(response);
42129bd497b8SGregory Neil Shapiro 			if (strncmp(response, "421 ", 4) == 0
42139bd497b8SGregory Neil Shapiro 			    || strncmp(response, "421-", 4) == 0)
42149bd497b8SGregory Neil Shapiro 				rv = false;
421540266059SGregory Neil Shapiro 			break;
421640266059SGregory Neil Shapiro 
421740266059SGregory Neil Shapiro 		  case SMFIR_REJECT:
421840266059SGregory Neil Shapiro 			milteraccept = false;
421940266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)
422040266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
422140266059SGregory Neil Shapiro 					  "Milter: data, reject=554 5.7.1 Command rejected");
422240266059SGregory Neil Shapiro 			usrerr("554 5.7.1 Command rejected");
422340266059SGregory Neil Shapiro 			break;
422440266059SGregory Neil Shapiro 
422540266059SGregory Neil Shapiro 		  case SMFIR_DISCARD:
422640266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)
422740266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
422840266059SGregory Neil Shapiro 					  "Milter: data, discard");
422940266059SGregory Neil Shapiro 			milteraccept = false;
423040266059SGregory Neil Shapiro 			e->e_flags |= EF_DISCARD;
423140266059SGregory Neil Shapiro 			break;
423240266059SGregory Neil Shapiro 
423340266059SGregory Neil Shapiro 		  case SMFIR_TEMPFAIL:
423440266059SGregory Neil Shapiro 			if (MilterLogLevel > 3)
423540266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
423640266059SGregory Neil Shapiro 					  "Milter: data, reject=%s",
423740266059SGregory Neil Shapiro 					  MSG_TEMPFAIL);
423840266059SGregory Neil Shapiro 			milteraccept = false;
42399bd497b8SGregory Neil Shapiro # if _FFR_MILTER_ENHSC
42409bd497b8SGregory Neil Shapiro 			(void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
42415b0945b5SGregory Neil Shapiro # endif
42425b0945b5SGregory Neil Shapiro 			/* Can't use ("%s", ...) due to usrerr() requirements */
424340266059SGregory Neil Shapiro 			usrerr(MSG_TEMPFAIL);
424440266059SGregory Neil Shapiro 			break;
42454e4196cbSGregory Neil Shapiro 
42464e4196cbSGregory Neil Shapiro 		  case SMFIR_SHUTDOWN:
42474e4196cbSGregory Neil Shapiro 			if (MilterLogLevel > 3)
42484e4196cbSGregory Neil Shapiro 				sm_syslog(LOG_INFO, e->e_id,
42494e4196cbSGregory Neil Shapiro 					  "Milter: data, reject=421 4.7.0 %s closing connection",
42504e4196cbSGregory Neil Shapiro 					  MyHostName);
42514e4196cbSGregory Neil Shapiro 			milteraccept = false;
42524e4196cbSGregory Neil Shapiro 			usrerr("421 4.7.0 %s closing connection", MyHostName);
42534e4196cbSGregory Neil Shapiro 			rv = false;
42544e4196cbSGregory Neil Shapiro 			break;
425540266059SGregory Neil Shapiro 		}
425640266059SGregory Neil Shapiro 		if (response != NULL)
425740266059SGregory Neil Shapiro 			sm_free(response);
425840266059SGregory Neil Shapiro 	}
425940266059SGregory Neil Shapiro 
426040266059SGregory Neil Shapiro 	/* Milter may have changed message size */
4261ba00ec3dSGregory Neil Shapiro 	(void) sm_snprintf(buf, sizeof(buf), "%ld", PRT_NONNEGL(e->e_msgsize));
426240266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
426340266059SGregory Neil Shapiro 
426440266059SGregory Neil Shapiro 	/* abort message filters that didn't get the body & log msg is OK */
426540266059SGregory Neil Shapiro 	if (smtp->sm_milterlist && smtp->sm_milterize)
426640266059SGregory Neil Shapiro 	{
426740266059SGregory Neil Shapiro 		milter_abort(e);
426840266059SGregory Neil Shapiro 		if (milteraccept && MilterLogLevel > 9)
426940266059SGregory Neil Shapiro 			sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
427040266059SGregory Neil Shapiro 	}
4271e92d3f3fSGregory Neil Shapiro 
4272e92d3f3fSGregory Neil Shapiro 	/*
4273e92d3f3fSGregory Neil Shapiro 	**  If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or
4274e92d3f3fSGregory Neil Shapiro 	**  milter accepted message, sync it now
4275e92d3f3fSGregory Neil Shapiro 	**
4276e92d3f3fSGregory Neil Shapiro 	**  XXX This is almost a copy of the code in collect(): put it into
4277e92d3f3fSGregory Neil Shapiro 	**	a function that is called from both places?
4278e92d3f3fSGregory Neil Shapiro 	*/
4279e92d3f3fSGregory Neil Shapiro 
4280e92d3f3fSGregory Neil Shapiro 	if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER)
4281e92d3f3fSGregory Neil Shapiro 	{
4282e92d3f3fSGregory Neil Shapiro 		int afd;
4283e92d3f3fSGregory Neil Shapiro 		SM_FILE_T *volatile df;
4284e92d3f3fSGregory Neil Shapiro 		char *dfname;
4285e92d3f3fSGregory Neil Shapiro 
4286e92d3f3fSGregory Neil Shapiro 		df = e->e_dfp;
4287e92d3f3fSGregory Neil Shapiro 		dfname = queuename(e, DATAFL_LETTER);
4288e92d3f3fSGregory Neil Shapiro 		if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0
4289e92d3f3fSGregory Neil Shapiro 		    && errno != EINVAL)
4290e92d3f3fSGregory Neil Shapiro 		{
4291e92d3f3fSGregory Neil Shapiro 			int save_errno;
4292e92d3f3fSGregory Neil Shapiro 
4293e92d3f3fSGregory Neil Shapiro 			save_errno = errno;
4294e92d3f3fSGregory Neil Shapiro 			if (save_errno == EEXIST)
4295e92d3f3fSGregory Neil Shapiro 			{
4296e92d3f3fSGregory Neil Shapiro 				struct stat st;
4297e92d3f3fSGregory Neil Shapiro 				int dfd;
4298e92d3f3fSGregory Neil Shapiro 
4299e92d3f3fSGregory Neil Shapiro 				if (stat(dfname, &st) < 0)
4300e92d3f3fSGregory Neil Shapiro 					st.st_size = -1;
4301e92d3f3fSGregory Neil Shapiro 				errno = EEXIST;
4302e92d3f3fSGregory Neil Shapiro 				syserr("@collect: bfcommit(%s): already on disk, size=%ld",
4303e92d3f3fSGregory Neil Shapiro 				       dfname, (long) st.st_size);
4304e92d3f3fSGregory Neil Shapiro 				dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
4305e92d3f3fSGregory Neil Shapiro 				if (dfd >= 0)
4306e92d3f3fSGregory Neil Shapiro 					dumpfd(dfd, true, true);
4307e92d3f3fSGregory Neil Shapiro 			}
4308e92d3f3fSGregory Neil Shapiro 			errno = save_errno;
4309e92d3f3fSGregory Neil Shapiro 			dferror(df, "bfcommit", e);
4310e92d3f3fSGregory Neil Shapiro 			flush_errors(true);
4311e92d3f3fSGregory Neil Shapiro 			finis(save_errno != EEXIST, true, ExitStat);
4312e92d3f3fSGregory Neil Shapiro 		}
4313e92d3f3fSGregory Neil Shapiro 		else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
4314e92d3f3fSGregory Neil Shapiro 		{
4315e92d3f3fSGregory Neil Shapiro 			dferror(df, "sm_io_getinfo", e);
4316e92d3f3fSGregory Neil Shapiro 			flush_errors(true);
4317e92d3f3fSGregory Neil Shapiro 			finis(true, true, ExitStat);
4318e92d3f3fSGregory Neil Shapiro 			/* NOTREACHED */
4319e92d3f3fSGregory Neil Shapiro 		}
4320e92d3f3fSGregory Neil Shapiro 		else if (fsync(afd) < 0)
4321e92d3f3fSGregory Neil Shapiro 		{
4322e92d3f3fSGregory Neil Shapiro 			dferror(df, "fsync", e);
4323e92d3f3fSGregory Neil Shapiro 			flush_errors(true);
4324e92d3f3fSGregory Neil Shapiro 			finis(true, true, ExitStat);
4325e92d3f3fSGregory Neil Shapiro 			/* NOTREACHED */
4326e92d3f3fSGregory Neil Shapiro 		}
4327e92d3f3fSGregory Neil Shapiro 		else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
4328e92d3f3fSGregory Neil Shapiro 		{
4329e92d3f3fSGregory Neil Shapiro 			dferror(df, "sm_io_close", e);
4330e92d3f3fSGregory Neil Shapiro 			flush_errors(true);
4331e92d3f3fSGregory Neil Shapiro 			finis(true, true, ExitStat);
4332e92d3f3fSGregory Neil Shapiro 			/* NOTREACHED */
4333e92d3f3fSGregory Neil Shapiro 		}
4334e92d3f3fSGregory Neil Shapiro 
4335e92d3f3fSGregory Neil Shapiro 		/* Now reopen the df file */
4336e92d3f3fSGregory Neil Shapiro 		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
4337e92d3f3fSGregory Neil Shapiro 					SM_IO_RDONLY, NULL);
4338e92d3f3fSGregory Neil Shapiro 		if (e->e_dfp == NULL)
4339e92d3f3fSGregory Neil Shapiro 		{
4340e92d3f3fSGregory Neil Shapiro 			/* we haven't acked receipt yet, so just chuck this */
4341e92d3f3fSGregory Neil Shapiro 			syserr("@Cannot reopen %s", dfname);
4342e92d3f3fSGregory Neil Shapiro 			finis(true, true, ExitStat);
4343e92d3f3fSGregory Neil Shapiro 			/* NOTREACHED */
4344e92d3f3fSGregory Neil Shapiro 		}
4345e92d3f3fSGregory Neil Shapiro 	}
434640266059SGregory Neil Shapiro #endif /* MILTER */
434740266059SGregory Neil Shapiro 
434840266059SGregory Neil Shapiro 	/* Check if quarantining stats should be updated */
434940266059SGregory Neil Shapiro 	if (e->e_quarmsg != NULL)
435040266059SGregory Neil Shapiro 		markstats(e, NULL, STATS_QUARANTINE);
435140266059SGregory Neil Shapiro 
435240266059SGregory Neil Shapiro 	/*
435340266059SGregory Neil Shapiro 	**  If a header/body check (header checks or milter)
435440266059SGregory Neil Shapiro 	**  set EF_DISCARD, don't queueup the message --
435540266059SGregory Neil Shapiro 	**  that would lose the EF_DISCARD bit and deliver
435640266059SGregory Neil Shapiro 	**  the message.
435740266059SGregory Neil Shapiro 	*/
435840266059SGregory Neil Shapiro 
435940266059SGregory Neil Shapiro 	if (bitset(EF_DISCARD, e->e_flags))
436040266059SGregory Neil Shapiro 		doublequeue = false;
436140266059SGregory Neil Shapiro 
436240266059SGregory Neil Shapiro 	aborting = Errors > 0;
4363323f6dcbSGregory Neil Shapiro 	if (!(aborting || bitset(EF_DISCARD, e->e_flags)) &&
436440266059SGregory Neil Shapiro 	    (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
436540266059SGregory Neil Shapiro 	    !split_by_recipient(e))
436640266059SGregory Neil Shapiro 		aborting = bitset(EF_FATALERRS, e->e_flags);
436740266059SGregory Neil Shapiro 
436840266059SGregory Neil Shapiro 	if (aborting)
436940266059SGregory Neil Shapiro 	{
4370ffb83623SGregory Neil Shapiro 		ADDRESS *q;
4371ffb83623SGregory Neil Shapiro 
437240266059SGregory Neil Shapiro 		/* Log who the mail would have gone to */
437340266059SGregory Neil Shapiro 		logundelrcpts(e, e->e_message, 8, false);
4374ffb83623SGregory Neil Shapiro 
4375ffb83623SGregory Neil Shapiro 		/*
4376ffb83623SGregory Neil Shapiro 		**  If something above refused the message, we still haven't
4377ffb83623SGregory Neil Shapiro 		**  accepted responsibility for it.  Don't send DSNs.
4378ffb83623SGregory Neil Shapiro 		*/
4379ffb83623SGregory Neil Shapiro 
4380ffb83623SGregory Neil Shapiro 		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
4381ffb83623SGregory Neil Shapiro 			q->q_flags &= ~Q_PINGFLAGS;
4382ffb83623SGregory Neil Shapiro 
438340266059SGregory Neil Shapiro 		flush_errors(true);
438440266059SGregory Neil Shapiro 		buffer_errors();
438540266059SGregory Neil Shapiro 		goto abortmessage;
438640266059SGregory Neil Shapiro 	}
438740266059SGregory Neil Shapiro 
438840266059SGregory Neil Shapiro 	/* from now on, we have to operate silently */
438940266059SGregory Neil Shapiro 	buffer_errors();
439040266059SGregory Neil Shapiro 
439140266059SGregory Neil Shapiro #if 0
439240266059SGregory Neil Shapiro 	/*
439340266059SGregory Neil Shapiro 	**  Clear message, it may contain an error from the SMTP dialogue.
439440266059SGregory Neil Shapiro 	**  This error must not show up in the queue.
439540266059SGregory Neil Shapiro 	**	Some error message should show up, e.g., alias database
439640266059SGregory Neil Shapiro 	**	not available, but others shouldn't, e.g., from check_rcpt.
439740266059SGregory Neil Shapiro 	*/
439840266059SGregory Neil Shapiro 
439940266059SGregory Neil Shapiro 	e->e_message = NULL;
440040266059SGregory Neil Shapiro #endif /* 0 */
440140266059SGregory Neil Shapiro 
440240266059SGregory Neil Shapiro 	/*
440340266059SGregory Neil Shapiro 	**  Arrange to send to everyone.
440440266059SGregory Neil Shapiro 	**	If sending to multiple people, mail back
440540266059SGregory Neil Shapiro 	**		errors rather than reporting directly.
440640266059SGregory Neil Shapiro 	**	In any case, don't mail back errors for
440740266059SGregory Neil Shapiro 	**		anything that has happened up to
440840266059SGregory Neil Shapiro 	**		now (the other end will do this).
440940266059SGregory Neil Shapiro 	**	Truncate our transcript -- the mail has gotten
441040266059SGregory Neil Shapiro 	**		to us successfully, and if we have
441140266059SGregory Neil Shapiro 	**		to mail this back, it will be easier
441240266059SGregory Neil Shapiro 	**		on the reader.
441340266059SGregory Neil Shapiro 	**	Then send to everyone.
441440266059SGregory Neil Shapiro 	**	Finally give a reply code.  If an error has
441540266059SGregory Neil Shapiro 	**		already been given, don't mail a
441640266059SGregory Neil Shapiro 	**		message back.
441740266059SGregory Neil Shapiro 	**	We goose error returns by clearing error bit.
441840266059SGregory Neil Shapiro 	*/
441940266059SGregory Neil Shapiro 
442040266059SGregory Neil Shapiro 	SmtpPhase = "delivery";
442140266059SGregory Neil Shapiro 	(void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
442240266059SGregory Neil Shapiro 	id = e->e_id;
442340266059SGregory Neil Shapiro 
442440266059SGregory Neil Shapiro #if NAMED_BIND
442540266059SGregory Neil Shapiro 	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
442640266059SGregory Neil Shapiro 	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
44275b0945b5SGregory Neil Shapiro #endif
442840266059SGregory Neil Shapiro 
4429da7d7b9cSGregory Neil Shapiro #if _FFR_PROXY
4430da7d7b9cSGregory Neil Shapiro 	if (SM_PROXY_REQ == e->e_sendmode)
4431da7d7b9cSGregory Neil Shapiro 	{
4432da7d7b9cSGregory Neil Shapiro 		/* is proxy mode possible? */
4433da7d7b9cSGregory Neil Shapiro 		if (e->e_sibling == NULL && e->e_nrcpts == 1
4434da7d7b9cSGregory Neil Shapiro 		    && smtp->sm_nrcpts == 1
4435da7d7b9cSGregory Neil Shapiro 		    && (a = e->e_sendqueue) != NULL && a->q_next == NULL)
4436da7d7b9cSGregory Neil Shapiro 		{
4437da7d7b9cSGregory Neil Shapiro 			a->q_flags &= ~(QPINGONFAILURE|QPINGONSUCCESS|
4438da7d7b9cSGregory Neil Shapiro 					QPINGONDELAY);
4439da7d7b9cSGregory Neil Shapiro 			e->e_errormode = EM_QUIET;
4440da7d7b9cSGregory Neil Shapiro 			e->e_sendmode = SM_PROXY;
4441da7d7b9cSGregory Neil Shapiro 		}
4442da7d7b9cSGregory Neil Shapiro 		else
4443da7d7b9cSGregory Neil Shapiro 		{
4444da7d7b9cSGregory Neil Shapiro 			if (tTd(87, 2))
4445da7d7b9cSGregory Neil Shapiro 			{
4446da7d7b9cSGregory Neil Shapiro 				a = e->e_sendqueue;
4447da7d7b9cSGregory Neil Shapiro 				sm_dprintf("srv: mode=%c, e=%p, sibling=%p, nrcpts=%d, sm_nrcpts=%d, sendqueue=%p, next=%p\n",
4448da7d7b9cSGregory Neil Shapiro 				e->e_sendmode, e, e->e_sibling, e->e_nrcpts,
4449da7d7b9cSGregory Neil Shapiro 				smtp->sm_nrcpts, a,
4450da7d7b9cSGregory Neil Shapiro 				(a == NULL) ? (void *)0 : a->q_next);
4451da7d7b9cSGregory Neil Shapiro 			}
4452da7d7b9cSGregory Neil Shapiro 
4453da7d7b9cSGregory Neil Shapiro 			/* switch to interactive mode */
4454da7d7b9cSGregory Neil Shapiro 			e->e_sendmode = SM_DELIVER;
4455da7d7b9cSGregory Neil Shapiro 			if (LogLevel > 9)
4456da7d7b9cSGregory Neil Shapiro 				sm_syslog(LOG_DEBUG, e->e_id,
4457da7d7b9cSGregory Neil Shapiro 					  "proxy mode requested but not possible");
4458da7d7b9cSGregory Neil Shapiro 		}
4459da7d7b9cSGregory Neil Shapiro 	}
4460da7d7b9cSGregory Neil Shapiro #endif /* _FFR_PROXY */
44616f9c8e5bSGregory Neil Shapiro 
44622fb4f839SGregory Neil Shapiro #if _FFR_DMTRIGGER
44632fb4f839SGregory Neil Shapiro 	if (SM_TRIGGER == e->e_sendmode)
44642fb4f839SGregory Neil Shapiro 		doublequeue = true;
44652fb4f839SGregory Neil Shapiro #endif
446640266059SGregory Neil Shapiro 	for (ee = e; ee != NULL; ee = ee->e_sibling)
446740266059SGregory Neil Shapiro 	{
446840266059SGregory Neil Shapiro 		/* make sure we actually do delivery */
446940266059SGregory Neil Shapiro 		ee->e_flags &= ~EF_CLRQUEUE;
447040266059SGregory Neil Shapiro 
447140266059SGregory Neil Shapiro 		/* from now on, operate silently */
447240266059SGregory Neil Shapiro 		ee->e_errormode = EM_MAIL;
447340266059SGregory Neil Shapiro 
447440266059SGregory Neil Shapiro 		if (doublequeue)
447540266059SGregory Neil Shapiro 		{
44762fb4f839SGregory Neil Shapiro 			unsigned int qup_flags;
44772fb4f839SGregory Neil Shapiro 
44782fb4f839SGregory Neil Shapiro 			qup_flags = QUP_FL_MSYNC;
44792fb4f839SGregory Neil Shapiro #if _FFR_DMTRIGGER
44802fb4f839SGregory Neil Shapiro 			if (IS_SM_TRIGGER(ee->e_sendmode))
44812fb4f839SGregory Neil Shapiro 				qup_flags |= QUP_FL_UNLOCK;
44822fb4f839SGregory Neil Shapiro #endif
448340266059SGregory Neil Shapiro 			/* make sure it is in the queue */
44842fb4f839SGregory Neil Shapiro 			queueup(ee, qup_flags);
448540266059SGregory Neil Shapiro 		}
448640266059SGregory Neil Shapiro 		else
448740266059SGregory Neil Shapiro 		{
44884e4196cbSGregory Neil Shapiro 			int mode;
44894e4196cbSGregory Neil Shapiro 
449040266059SGregory Neil Shapiro 			/* send to all recipients */
44914e4196cbSGregory Neil Shapiro 			mode = SM_DEFAULT;
44924e4196cbSGregory Neil Shapiro #if _FFR_DM_ONE
44934e4196cbSGregory Neil Shapiro 			if (SM_DM_ONE == e->e_sendmode)
44944e4196cbSGregory Neil Shapiro 			{
44954e4196cbSGregory Neil Shapiro 				if (NotFirstDelivery)
44964e4196cbSGregory Neil Shapiro 				{
44974e4196cbSGregory Neil Shapiro 					mode = SM_QUEUE;
44984e4196cbSGregory Neil Shapiro 					e->e_sendmode = SM_QUEUE;
44994e4196cbSGregory Neil Shapiro 				}
45004e4196cbSGregory Neil Shapiro 				else
45014e4196cbSGregory Neil Shapiro 				{
45024e4196cbSGregory Neil Shapiro 					mode = SM_FORK;
45034e4196cbSGregory Neil Shapiro 					NotFirstDelivery = true;
45044e4196cbSGregory Neil Shapiro 				}
45054e4196cbSGregory Neil Shapiro 			}
45064e4196cbSGregory Neil Shapiro #endif /* _FFR_DM_ONE */
45074e4196cbSGregory Neil Shapiro 			sendall(ee, mode);
450840266059SGregory Neil Shapiro 		}
450940266059SGregory Neil Shapiro 		ee->e_to = NULL;
451040266059SGregory Neil Shapiro 	}
451140266059SGregory Neil Shapiro 
451294c01205SGregory Neil Shapiro 	/* put back id for SMTP logging in putoutmsg() */
451394c01205SGregory Neil Shapiro 	oldid = CurEnv->e_id;
451494c01205SGregory Neil Shapiro 	CurEnv->e_id = id;
451594c01205SGregory Neil Shapiro 
4516da7d7b9cSGregory Neil Shapiro #if _FFR_PROXY
4517da7d7b9cSGregory Neil Shapiro 	a = e->e_sendqueue;
4518da7d7b9cSGregory Neil Shapiro 	if (tTd(87, 1))
4519da7d7b9cSGregory Neil Shapiro 	{
4520da7d7b9cSGregory Neil Shapiro 		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",
4521da7d7b9cSGregory Neil Shapiro 		e->e_sendmode, e, e->e_sibling, e->e_nrcpts, e->e_message, a,
4522da7d7b9cSGregory Neil Shapiro 		(a == NULL) ? (void *)0 : a->q_next,
4523da7d7b9cSGregory Neil Shapiro 		(a == NULL) ? -1 : a->q_state, SmtpError, e->e_rcode,
4524da7d7b9cSGregory Neil Shapiro 		e->e_renhsc, e->e_text);
4525da7d7b9cSGregory Neil Shapiro 	}
4526da7d7b9cSGregory Neil Shapiro 
4527da7d7b9cSGregory Neil Shapiro 	if (SM_PROXY == e->e_sendmode && a->q_state != QS_SENT &&
4528da7d7b9cSGregory Neil Shapiro 	    a->q_state != QS_VERIFIED) /* discarded! */
4529da7d7b9cSGregory Neil Shapiro 	{
4530da7d7b9cSGregory Neil Shapiro 		char *m, *errtext;
4531da7d7b9cSGregory Neil Shapiro 		char replycode[4];
4532da7d7b9cSGregory Neil Shapiro 		char enhsc[10];
4533da7d7b9cSGregory Neil Shapiro 		int offset;
4534da7d7b9cSGregory Neil Shapiro 
4535da7d7b9cSGregory Neil Shapiro #define NN_MSG(e)	(((e)->e_message != NULL) ? (e)->e_message : "")
4536da7d7b9cSGregory Neil Shapiro 		m = e->e_message;
4537da7d7b9cSGregory Neil Shapiro #define SM_MSG_DEFERRED "Deferred: "
4538da7d7b9cSGregory Neil Shapiro 		if (m != NULL && strncmp(SM_MSG_DEFERRED, m,
4539da7d7b9cSGregory Neil Shapiro 					 sizeof(SM_MSG_DEFERRED) - 1) == 0)
4540da7d7b9cSGregory Neil Shapiro 			m += sizeof(SM_MSG_DEFERRED) - 1;
4541da7d7b9cSGregory Neil Shapiro 		offset = extsc(m, ' ', replycode, enhsc);
4542da7d7b9cSGregory Neil Shapiro 
4543da7d7b9cSGregory Neil Shapiro 		if (tTd(87, 2))
4544da7d7b9cSGregory Neil Shapiro 		{
4545da7d7b9cSGregory Neil Shapiro 			sm_dprintf("srv: SmtpError=%s, rcode=%d, renhsc=%s, replycode=%s, enhsc=%s, offset=%d\n",
4546da7d7b9cSGregory Neil Shapiro 				SmtpError, e->e_rcode, e->e_renhsc,
4547da7d7b9cSGregory Neil Shapiro 				replycode, enhsc, offset);
4548da7d7b9cSGregory Neil Shapiro 		}
4549da7d7b9cSGregory Neil Shapiro 
4550da7d7b9cSGregory Neil Shapiro #define DIG2CHAR(d)	((d) + '0')
4551da7d7b9cSGregory Neil Shapiro 		if (e->e_rcode != 0 && (replycode[0] == '\0' ||
4552da7d7b9cSGregory Neil Shapiro 		    replycode[0] == DIG2CHAR(REPLYTYPE(e->e_rcode))))
4553da7d7b9cSGregory Neil Shapiro 		{
4554da7d7b9cSGregory Neil Shapiro 			replycode[0] = DIG2CHAR(REPLYTYPE(e->e_rcode));
4555da7d7b9cSGregory Neil Shapiro 			replycode[1] = DIG2CHAR(REPLYCLASS(e->e_rcode));
4556da7d7b9cSGregory Neil Shapiro 			replycode[2] = DIG2CHAR(REPLYMINOR(e->e_rcode));
4557da7d7b9cSGregory Neil Shapiro 			replycode[3] = '\0';
4558da7d7b9cSGregory Neil Shapiro 			if (e->e_renhsc[0] == replycode[0])
4559da7d7b9cSGregory Neil Shapiro 				sm_strlcpy(enhsc, e->e_renhsc, sizeof(enhsc));
4560da7d7b9cSGregory Neil Shapiro 			if (offset < 0)
4561da7d7b9cSGregory Neil Shapiro 				offset = 0;
4562da7d7b9cSGregory Neil Shapiro 		}
4563da7d7b9cSGregory Neil Shapiro 		if (e->e_text != NULL)
4564da7d7b9cSGregory Neil Shapiro 		{
4565da7d7b9cSGregory Neil Shapiro 			(void) strreplnonprt(e->e_text, '_');
4566da7d7b9cSGregory Neil Shapiro 			errtext = e->e_text;
4567da7d7b9cSGregory Neil Shapiro 		}
4568da7d7b9cSGregory Neil Shapiro 		else
4569da7d7b9cSGregory Neil Shapiro 			errtext = m + offset;
4570da7d7b9cSGregory Neil Shapiro 
4571da7d7b9cSGregory Neil Shapiro 		if (replycode[0] != '\0' && enhsc[0] != '\0')
4572da7d7b9cSGregory Neil Shapiro 			emessage(replycode, enhsc, "%s", errtext);
4573da7d7b9cSGregory Neil Shapiro 		else if (replycode[0] != '\0')
4574da7d7b9cSGregory Neil Shapiro 			emessage(replycode, smtptodsn(atoi(replycode)),
4575da7d7b9cSGregory Neil Shapiro 				 "%s", errtext);
4576da7d7b9cSGregory Neil Shapiro 		else if (QS_IS_TEMPFAIL(a->q_state))
4577da7d7b9cSGregory Neil Shapiro 		{
4578da7d7b9cSGregory Neil Shapiro 			if (m != NULL)
4579da7d7b9cSGregory Neil Shapiro 				message("450 4.5.1 %s", m);
4580da7d7b9cSGregory Neil Shapiro 			else
4581da7d7b9cSGregory Neil Shapiro 				message("450 4.5.1 Temporary error");
4582da7d7b9cSGregory Neil Shapiro 		}
4583da7d7b9cSGregory Neil Shapiro 		else
4584da7d7b9cSGregory Neil Shapiro 		{
4585da7d7b9cSGregory Neil Shapiro 			if (m != NULL)
4586da7d7b9cSGregory Neil Shapiro 				message("550 5.5.1 %s", m);
4587da7d7b9cSGregory Neil Shapiro 			else
4588da7d7b9cSGregory Neil Shapiro 				message("550 5.0.0 Permanent error");
4589da7d7b9cSGregory Neil Shapiro 		}
4590da7d7b9cSGregory Neil Shapiro 	}
4591da7d7b9cSGregory Neil Shapiro 	else
4592da7d7b9cSGregory Neil Shapiro 	{
4593da7d7b9cSGregory Neil Shapiro #endif /* _FFR_PROXY */
459440266059SGregory Neil Shapiro 		/* issue success message */
45954e4196cbSGregory Neil Shapiro #if _FFR_MSG_ACCEPT
45964e4196cbSGregory Neil Shapiro 		if (MessageAccept != NULL && *MessageAccept != '\0')
45974e4196cbSGregory Neil Shapiro 		{
45984e4196cbSGregory Neil Shapiro 			char msg[MAXLINE];
45994e4196cbSGregory Neil Shapiro 
4600d0cef73dSGregory Neil Shapiro 			expand(MessageAccept, msg, sizeof(msg), e);
46014e4196cbSGregory Neil Shapiro 			message("250 2.0.0 %s", msg);
46024e4196cbSGregory Neil Shapiro 		}
46034e4196cbSGregory Neil Shapiro 		else
46044e4196cbSGregory Neil Shapiro #endif /* _FFR_MSG_ACCEPT */
46052fb4f839SGregory Neil Shapiro 		/* "else" in #if code above */
460640266059SGregory Neil Shapiro 		message("250 2.0.0 %s Message accepted for delivery", id);
4607da7d7b9cSGregory Neil Shapiro #if _FFR_PROXY
4608da7d7b9cSGregory Neil Shapiro 	}
46095b0945b5SGregory Neil Shapiro #endif
461094c01205SGregory Neil Shapiro 	CurEnv->e_id = oldid;
461140266059SGregory Neil Shapiro 
461240266059SGregory Neil Shapiro 	/* if we just queued, poke it */
461340266059SGregory Neil Shapiro 	if (doublequeue)
461440266059SGregory Neil Shapiro 	{
461540266059SGregory Neil Shapiro 		bool anything_to_send = false;
461640266059SGregory Neil Shapiro 
461740266059SGregory Neil Shapiro 		sm_getla();
461840266059SGregory Neil Shapiro 		for (ee = e; ee != NULL; ee = ee->e_sibling)
461940266059SGregory Neil Shapiro 		{
46202fb4f839SGregory Neil Shapiro #if _FFR_DMTRIGGER
46212fb4f839SGregory Neil Shapiro 			if (SM_TRIGGER == ee->e_sendmode)
46222fb4f839SGregory Neil Shapiro 			{
46232fb4f839SGregory Neil Shapiro 				sm_syslog(LOG_DEBUG, ee->e_id,
46242fb4f839SGregory Neil Shapiro 					"smtp: doublequeue, mode=%c", ee->e_sendmode);
46252fb4f839SGregory Neil Shapiro 				ee->e_sendmode = SM_DELIVER;
46262fb4f839SGregory Neil Shapiro 
46272fb4f839SGregory Neil Shapiro 				/* close all the queue files */
46282fb4f839SGregory Neil Shapiro 				/* almost the same as below */
46292fb4f839SGregory Neil Shapiro 				closexscript(ee);
46302fb4f839SGregory Neil Shapiro 				SM_CLOSE_FP(ee->e_dfp);
46312fb4f839SGregory Neil Shapiro 				continue;
46322fb4f839SGregory Neil Shapiro 			}
46332fb4f839SGregory Neil Shapiro #endif /* _FFR_DMTRIGGER */
463440266059SGregory Neil Shapiro 			if (WILL_BE_QUEUED(ee->e_sendmode))
463540266059SGregory Neil Shapiro 				continue;
463640266059SGregory Neil Shapiro 			if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
463740266059SGregory Neil Shapiro 			{
463840266059SGregory Neil Shapiro 				ee->e_sendmode = SM_QUEUE;
463940266059SGregory Neil Shapiro 				continue;
464040266059SGregory Neil Shapiro 			}
464140266059SGregory Neil Shapiro 			else if (QueueMode != QM_QUARANTINE &&
464240266059SGregory Neil Shapiro 				 ee->e_quarmsg != NULL)
464340266059SGregory Neil Shapiro 			{
464440266059SGregory Neil Shapiro 				ee->e_sendmode = SM_QUEUE;
464540266059SGregory Neil Shapiro 				continue;
464640266059SGregory Neil Shapiro 			}
464740266059SGregory Neil Shapiro 			anything_to_send = true;
464840266059SGregory Neil Shapiro 
464940266059SGregory Neil Shapiro 			/* close all the queue files */
465040266059SGregory Neil Shapiro 			closexscript(ee);
46512fb4f839SGregory Neil Shapiro 			SM_CLOSE_FP(ee->e_dfp);
465240266059SGregory Neil Shapiro 			unlockqueue(ee);
465340266059SGregory Neil Shapiro 		}
465440266059SGregory Neil Shapiro 		if (anything_to_send)
465540266059SGregory Neil Shapiro 		{
465640266059SGregory Neil Shapiro #if PIPELINING
465740266059SGregory Neil Shapiro 			/*
465840266059SGregory Neil Shapiro 			**  XXX if we don't do this, we get 250 twice
465940266059SGregory Neil Shapiro 			**	because it is also flushed in the child.
466040266059SGregory Neil Shapiro 			*/
466140266059SGregory Neil Shapiro 
466240266059SGregory Neil Shapiro 			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
466340266059SGregory Neil Shapiro #endif /* PIPELINING */
46642fb4f839SGregory Neil Shapiro #if _FFR_DMTRIGGER
46652fb4f839SGregory Neil Shapiro 			sm_syslog(LOG_DEBUG, e->e_id, "smtp: doublequeue=send");
46662fb4f839SGregory Neil Shapiro #endif
466740266059SGregory Neil Shapiro 			(void) doworklist(e, true, true);
466840266059SGregory Neil Shapiro 		}
466940266059SGregory Neil Shapiro 	}
467040266059SGregory Neil Shapiro 
467140266059SGregory Neil Shapiro   abortmessage:
46729bd497b8SGregory Neil Shapiro 	if (tTd(92, 2))
46739bd497b8SGregory Neil Shapiro 		sm_dprintf("abortmessage: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",
46749bd497b8SGregory Neil Shapiro 			e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel);
467540266059SGregory Neil Shapiro 	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
467640266059SGregory Neil Shapiro 		logsender(e, NULL);
467740266059SGregory Neil Shapiro 	e->e_flags &= ~EF_LOGSENDER;
467840266059SGregory Neil Shapiro 
467940266059SGregory Neil Shapiro 	/* clean up a bit */
468040266059SGregory Neil Shapiro 	smtp->sm_gotmail = false;
468140266059SGregory Neil Shapiro 
468240266059SGregory Neil Shapiro 	/*
468340266059SGregory Neil Shapiro 	**  Call dropenvelope if and only if the envelope is *not*
468440266059SGregory Neil Shapiro 	**  being processed by the child process forked by doworklist().
468540266059SGregory Neil Shapiro 	*/
468640266059SGregory Neil Shapiro 
468740266059SGregory Neil Shapiro 	if (aborting || bitset(EF_DISCARD, e->e_flags))
46889bd497b8SGregory Neil Shapiro 		(void) dropenvelope(e, true, false);
468940266059SGregory Neil Shapiro 	else
469040266059SGregory Neil Shapiro 	{
469140266059SGregory Neil Shapiro 		for (ee = e; ee != NULL; ee = ee->e_sibling)
469240266059SGregory Neil Shapiro 		{
469340266059SGregory Neil Shapiro 			if (!doublequeue &&
469440266059SGregory Neil Shapiro 			    QueueMode != QM_QUARANTINE &&
469540266059SGregory Neil Shapiro 			    ee->e_quarmsg != NULL)
469640266059SGregory Neil Shapiro 			{
46979bd497b8SGregory Neil Shapiro 				(void) dropenvelope(ee, true, false);
469840266059SGregory Neil Shapiro 				continue;
469940266059SGregory Neil Shapiro 			}
470040266059SGregory Neil Shapiro 			if (WILL_BE_QUEUED(ee->e_sendmode))
47019bd497b8SGregory Neil Shapiro 				(void) dropenvelope(ee, true, false);
470240266059SGregory Neil Shapiro 		}
470340266059SGregory Neil Shapiro 	}
470440266059SGregory Neil Shapiro 
470540266059SGregory Neil Shapiro 	CurEnv = e;
4706d0cef73dSGregory Neil Shapiro 	features = e->e_features;
4707e3793f76SGregory Neil Shapiro 	sm_rpool_free(e->e_rpool);
470840266059SGregory Neil Shapiro 	newenvelope(e, e, sm_rpool_new_x(NULL));
470940266059SGregory Neil Shapiro 	e->e_flags = BlankEnvelope.e_flags;
4710d0cef73dSGregory Neil Shapiro 	e->e_features = features;
471140266059SGregory Neil Shapiro 
471240266059SGregory Neil Shapiro 	/* restore connection quarantining */
471340266059SGregory Neil Shapiro 	if (smtp->sm_quarmsg == NULL)
471440266059SGregory Neil Shapiro 	{
471540266059SGregory Neil Shapiro 		e->e_quarmsg = NULL;
471640266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
471740266059SGregory Neil Shapiro 	}
471840266059SGregory Neil Shapiro 	else
471940266059SGregory Neil Shapiro 	{
472040266059SGregory Neil Shapiro 		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
472140266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM,
472240266059SGregory Neil Shapiro 			  macid("{quarantine}"), e->e_quarmsg);
472340266059SGregory Neil Shapiro 	}
47244e4196cbSGregory Neil Shapiro 	return rv;
472540266059SGregory Neil Shapiro }
472640266059SGregory Neil Shapiro /*
472740266059SGregory Neil Shapiro **  LOGUNDELRCPTS -- log undelivered (or all) recipients.
472840266059SGregory Neil Shapiro **
472940266059SGregory Neil Shapiro **	Parameters:
473040266059SGregory Neil Shapiro **		e -- envelope.
473140266059SGregory Neil Shapiro **		msg -- message for Stat=
473240266059SGregory Neil Shapiro **		level -- log level.
473340266059SGregory Neil Shapiro **		all -- log all recipients.
473440266059SGregory Neil Shapiro **
473540266059SGregory Neil Shapiro **	Returns:
473640266059SGregory Neil Shapiro **		none.
473740266059SGregory Neil Shapiro **
473840266059SGregory Neil Shapiro **	Side Effects:
473940266059SGregory Neil Shapiro **		logs undelivered (or all) recipients
474040266059SGregory Neil Shapiro */
474140266059SGregory Neil Shapiro 
474240266059SGregory Neil Shapiro void
logundelrcpts(e,msg,level,all)474340266059SGregory Neil Shapiro logundelrcpts(e, msg, level, all)
474440266059SGregory Neil Shapiro 	ENVELOPE *e;
474540266059SGregory Neil Shapiro 	char *msg;
474640266059SGregory Neil Shapiro 	int level;
474740266059SGregory Neil Shapiro 	bool all;
474840266059SGregory Neil Shapiro {
474940266059SGregory Neil Shapiro 	ADDRESS *a;
475040266059SGregory Neil Shapiro 
475140266059SGregory Neil Shapiro 	if (LogLevel <= level || msg == NULL || *msg == '\0')
475240266059SGregory Neil Shapiro 		return;
475340266059SGregory Neil Shapiro 
475440266059SGregory Neil Shapiro 	/* Clear $h so relay= doesn't get mislogged by logdelivery() */
475540266059SGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, 'h', NULL);
475640266059SGregory Neil Shapiro 
475740266059SGregory Neil Shapiro 	/* Log who the mail would have gone to */
475840266059SGregory Neil Shapiro 	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
475940266059SGregory Neil Shapiro 	{
476040266059SGregory Neil Shapiro 		if (!QS_IS_UNDELIVERED(a->q_state) && !all)
476140266059SGregory Neil Shapiro 			continue;
476240266059SGregory Neil Shapiro 		e->e_to = a->q_paddr;
47639bd497b8SGregory Neil Shapiro 		logdelivery(NULL, NULL,
47649bd497b8SGregory Neil Shapiro #if _FFR_MILTER_ENHSC
47659bd497b8SGregory Neil Shapiro 			    (a->q_status == NULL && e->e_enhsc[0] != '\0')
47669bd497b8SGregory Neil Shapiro 			    ? e->e_enhsc :
47675b0945b5SGregory Neil Shapiro #endif
47682fb4f839SGregory Neil Shapiro /* not yet documented or tested */
47692fb4f839SGregory Neil Shapiro #if _FFR_USE_E_STATUS
47702fb4f839SGregory Neil Shapiro 			    (NULL == a->q_status) ? e->e_status :
47712fb4f839SGregory Neil Shapiro #endif
47729bd497b8SGregory Neil Shapiro 			    a->q_status,
4773da7d7b9cSGregory Neil Shapiro 			    msg, NULL, (time_t) 0, e, a, EX_OK /* ??? */);
477440266059SGregory Neil Shapiro 	}
477540266059SGregory Neil Shapiro 	e->e_to = NULL;
477640266059SGregory Neil Shapiro }
477740266059SGregory Neil Shapiro /*
4778c2aa98e2SPeter Wemm **  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
4779c2aa98e2SPeter Wemm **
4780c2aa98e2SPeter Wemm **	Parameters:
4781c2aa98e2SPeter Wemm **		pcounter -- pointer to a counter for this command.
4782c2aa98e2SPeter Wemm **		maxcount -- maximum value for this counter before we
4783c2aa98e2SPeter Wemm **			slow down.
478406f25ae9SGregory Neil Shapiro **		waitnow -- sleep now (in this routine)?
4785c2aa98e2SPeter Wemm **		cname -- command name for logging.
4786c2aa98e2SPeter Wemm **		e -- the current envelope.
4787c2aa98e2SPeter Wemm **
4788c2aa98e2SPeter Wemm **	Returns:
4789e92d3f3fSGregory Neil Shapiro **		time to wait,
4790e92d3f3fSGregory Neil Shapiro **		STOP_ATTACK if twice as many commands as allowed and
4791e92d3f3fSGregory Neil Shapiro **			MaxChildren > 0.
4792c2aa98e2SPeter Wemm **
4793c2aa98e2SPeter Wemm **	Side Effects:
4794c2aa98e2SPeter Wemm **		Slows down if we seem to be under attack.
4795c2aa98e2SPeter Wemm */
4796c2aa98e2SPeter Wemm 
479706f25ae9SGregory Neil Shapiro static time_t
checksmtpattack(pcounter,maxcount,waitnow,cname,e)479806f25ae9SGregory Neil Shapiro checksmtpattack(pcounter, maxcount, waitnow, cname, e)
479940266059SGregory Neil Shapiro 	volatile unsigned int *pcounter;
4800e92d3f3fSGregory Neil Shapiro 	unsigned int maxcount;
480106f25ae9SGregory Neil Shapiro 	bool waitnow;
4802c2aa98e2SPeter Wemm 	char *cname;
4803c2aa98e2SPeter Wemm 	ENVELOPE *e;
4804c2aa98e2SPeter Wemm {
480540266059SGregory Neil Shapiro 	if (maxcount <= 0)	/* no limit */
480640266059SGregory Neil Shapiro 		return (time_t) 0;
480740266059SGregory Neil Shapiro 
4808c2aa98e2SPeter Wemm 	if (++(*pcounter) >= maxcount)
4809c2aa98e2SPeter Wemm 	{
4810e92d3f3fSGregory Neil Shapiro 		unsigned int shift;
481106f25ae9SGregory Neil Shapiro 		time_t s;
481206f25ae9SGregory Neil Shapiro 
4813c2aa98e2SPeter Wemm 		if (*pcounter == maxcount && LogLevel > 5)
4814c2aa98e2SPeter Wemm 		{
4815c2aa98e2SPeter Wemm 			sm_syslog(LOG_INFO, e->e_id,
481613bd1963SGregory Neil Shapiro 				  "%s: possible SMTP attack: command=%.40s, count=%u",
48178774250cSGregory Neil Shapiro 				  CurSmtpClient, cname, *pcounter);
4818c2aa98e2SPeter Wemm 		}
4819e92d3f3fSGregory Neil Shapiro 		shift = *pcounter - maxcount;
4820e92d3f3fSGregory Neil Shapiro 		s = 1 << shift;
4821e92d3f3fSGregory Neil Shapiro 		if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0)
482206f25ae9SGregory Neil Shapiro 			s = MAXTIMEOUT;
482340266059SGregory Neil Shapiro 
4824e92d3f3fSGregory Neil Shapiro #define IS_ATTACK(s)	((MaxChildren > 0 && *pcounter >= maxcount * 2)	\
4825e92d3f3fSGregory Neil Shapiro 				? STOP_ATTACK : (time_t) s)
4826e92d3f3fSGregory Neil Shapiro 
482706f25ae9SGregory Neil Shapiro 		/* sleep at least 1 second before returning */
482806f25ae9SGregory Neil Shapiro 		(void) sleep(*pcounter / maxcount);
482906f25ae9SGregory Neil Shapiro 		s -= *pcounter / maxcount;
4830e92d3f3fSGregory Neil Shapiro 		if (s >= MAXTIMEOUT || s < 0)
4831e92d3f3fSGregory Neil Shapiro 			s = MAXTIMEOUT;
4832e92d3f3fSGregory Neil Shapiro 		if (waitnow && s > 0)
483306f25ae9SGregory Neil Shapiro 		{
483406f25ae9SGregory Neil Shapiro 			(void) sleep(s);
4835e92d3f3fSGregory Neil Shapiro 			return IS_ATTACK(0);
4836c2aa98e2SPeter Wemm 		}
4837e92d3f3fSGregory Neil Shapiro 		return IS_ATTACK(s);
483806f25ae9SGregory Neil Shapiro 	}
483940266059SGregory Neil Shapiro 	return (time_t) 0;
4840c2aa98e2SPeter Wemm }
484140266059SGregory Neil Shapiro /*
484240266059SGregory Neil Shapiro **  SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
484340266059SGregory Neil Shapiro **
484440266059SGregory Neil Shapiro **	Parameters:
484540266059SGregory Neil Shapiro **		none.
484640266059SGregory Neil Shapiro **
484740266059SGregory Neil Shapiro **	Returns:
484840266059SGregory Neil Shapiro **		nothing.
484940266059SGregory Neil Shapiro **
485040266059SGregory Neil Shapiro **	Side Effects:
485140266059SGregory Neil Shapiro **		may change I/O fd.
485240266059SGregory Neil Shapiro */
485340266059SGregory Neil Shapiro 
485440266059SGregory Neil Shapiro static void
setup_smtpd_io()485540266059SGregory Neil Shapiro setup_smtpd_io()
485640266059SGregory Neil Shapiro {
485740266059SGregory Neil Shapiro 	int inchfd, outchfd, outfd;
485840266059SGregory Neil Shapiro 
485940266059SGregory Neil Shapiro 	inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
486040266059SGregory Neil Shapiro 	outchfd  = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
486140266059SGregory Neil Shapiro 	outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
486240266059SGregory Neil Shapiro 	if (outchfd != outfd)
486340266059SGregory Neil Shapiro 	{
486440266059SGregory Neil Shapiro 		/* arrange for debugging output to go to remote host */
486540266059SGregory Neil Shapiro 		(void) dup2(outchfd, outfd);
486640266059SGregory Neil Shapiro 	}
486740266059SGregory Neil Shapiro 
486840266059SGregory Neil Shapiro 	/*
486940266059SGregory Neil Shapiro 	**  if InChannel and OutChannel are stdin/stdout
487040266059SGregory Neil Shapiro 	**  and connected to ttys
487140266059SGregory Neil Shapiro 	**  and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
487240266059SGregory Neil Shapiro 	**  then "chain" them together.
487340266059SGregory Neil Shapiro 	*/
487440266059SGregory Neil Shapiro 
487540266059SGregory Neil Shapiro 	if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
487640266059SGregory Neil Shapiro 	    isatty(inchfd) && isatty(outchfd))
487740266059SGregory Neil Shapiro 	{
487840266059SGregory Neil Shapiro 		int inmode, outmode;
487940266059SGregory Neil Shapiro 
488040266059SGregory Neil Shapiro 		inmode = fcntl(inchfd, F_GETFL, 0);
488140266059SGregory Neil Shapiro 		if (inmode == -1)
488240266059SGregory Neil Shapiro 		{
488340266059SGregory Neil Shapiro 			if (LogLevel > 11)
488440266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, NOQID,
488540266059SGregory Neil Shapiro 					"fcntl(inchfd, F_GETFL) failed: %s",
488640266059SGregory Neil Shapiro 					sm_errstring(errno));
488740266059SGregory Neil Shapiro 			return;
488840266059SGregory Neil Shapiro 		}
488940266059SGregory Neil Shapiro 		outmode = fcntl(outchfd, F_GETFL, 0);
489040266059SGregory Neil Shapiro 		if (outmode == -1)
489140266059SGregory Neil Shapiro 		{
489240266059SGregory Neil Shapiro 			if (LogLevel > 11)
489340266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, NOQID,
489440266059SGregory Neil Shapiro 					"fcntl(outchfd, F_GETFL) failed: %s",
489540266059SGregory Neil Shapiro 					sm_errstring(errno));
489640266059SGregory Neil Shapiro 			return;
489740266059SGregory Neil Shapiro 		}
489840266059SGregory Neil Shapiro 		if (bitset(O_NONBLOCK, inmode) ||
489940266059SGregory Neil Shapiro 		    bitset(O_NONBLOCK, outmode) ||
490040266059SGregory Neil Shapiro 		    fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
490140266059SGregory Neil Shapiro 			return;
490240266059SGregory Neil Shapiro 		outmode = fcntl(outchfd, F_GETFL, 0);
490340266059SGregory Neil Shapiro 		if (outmode != -1 && bitset(O_NONBLOCK, outmode))
490440266059SGregory Neil Shapiro 		{
490540266059SGregory Neil Shapiro 			/* changing InChannel also changes OutChannel */
490640266059SGregory Neil Shapiro 			sm_io_automode(OutChannel, InChannel);
490740266059SGregory Neil Shapiro 			if (tTd(97, 4) && LogLevel > 9)
490840266059SGregory Neil Shapiro 				sm_syslog(LOG_INFO, NOQID,
490940266059SGregory Neil Shapiro 					  "set automode for I (%d)/O (%d) in SMTP server",
491040266059SGregory Neil Shapiro 					  inchfd, outchfd);
491140266059SGregory Neil Shapiro 		}
491240266059SGregory Neil Shapiro 
491340266059SGregory Neil Shapiro 		/* undo change of inchfd */
491440266059SGregory Neil Shapiro 		(void) fcntl(inchfd, F_SETFL, inmode);
491540266059SGregory Neil Shapiro 	}
491640266059SGregory Neil Shapiro }
491740266059SGregory Neil Shapiro /*
4918c2aa98e2SPeter Wemm **  SKIPWORD -- skip a fixed word.
4919c2aa98e2SPeter Wemm **
4920c2aa98e2SPeter Wemm **	Parameters:
4921c2aa98e2SPeter Wemm **		p -- place to start looking.
4922c2aa98e2SPeter Wemm **		w -- word to skip.
4923c2aa98e2SPeter Wemm **
4924c2aa98e2SPeter Wemm **	Returns:
4925c2aa98e2SPeter Wemm **		p following w.
4926c2aa98e2SPeter Wemm **		NULL on error.
4927c2aa98e2SPeter Wemm **
4928c2aa98e2SPeter Wemm **	Side Effects:
4929c2aa98e2SPeter Wemm **		clobbers the p data area.
4930c2aa98e2SPeter Wemm */
4931c2aa98e2SPeter Wemm 
4932c2aa98e2SPeter Wemm static char *
skipword(p,w)4933c2aa98e2SPeter Wemm skipword(p, w)
4934c2aa98e2SPeter Wemm 	register char *volatile p;
4935c2aa98e2SPeter Wemm 	char *w;
4936c2aa98e2SPeter Wemm {
4937c2aa98e2SPeter Wemm 	register char *q;
4938c2aa98e2SPeter Wemm 	char *firstp = p;
4939c2aa98e2SPeter Wemm 
4940c2aa98e2SPeter Wemm 	/* find beginning of word */
494140266059SGregory Neil Shapiro 	SKIP_SPACE(p);
4942c2aa98e2SPeter Wemm 	q = p;
4943c2aa98e2SPeter Wemm 
4944c2aa98e2SPeter Wemm 	/* find end of word */
49455b0945b5SGregory Neil Shapiro 	while (*p != '\0' && *p != ':' && !(SM_ISSPACE(*p)))
4946c2aa98e2SPeter Wemm 		p++;
49475b0945b5SGregory Neil Shapiro 	while (SM_ISSPACE(*p))
4948c2aa98e2SPeter Wemm 		*p++ = '\0';
4949c2aa98e2SPeter Wemm 	if (*p != ':')
4950c2aa98e2SPeter Wemm 	{
4951c2aa98e2SPeter Wemm 	  syntax:
495206f25ae9SGregory Neil Shapiro 		usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
49532fb4f839SGregory Neil Shapiro 			SHOWSHRTCMDINREPLY(firstp));
495406f25ae9SGregory Neil Shapiro 		return NULL;
4955c2aa98e2SPeter Wemm 	}
4956c2aa98e2SPeter Wemm 	*p++ = '\0';
495740266059SGregory Neil Shapiro 	SKIP_SPACE(p);
4958c2aa98e2SPeter Wemm 
4959c2aa98e2SPeter Wemm 	if (*p == '\0')
4960c2aa98e2SPeter Wemm 		goto syntax;
4961c2aa98e2SPeter Wemm 
4962c2aa98e2SPeter Wemm 	/* see if the input word matches desired word */
496340266059SGregory Neil Shapiro 	if (sm_strcasecmp(q, w))
4964c2aa98e2SPeter Wemm 		goto syntax;
4965c2aa98e2SPeter Wemm 
496606f25ae9SGregory Neil Shapiro 	return p;
4967c2aa98e2SPeter Wemm }
4968af9557fdSGregory Neil Shapiro 
496940266059SGregory Neil Shapiro /*
4970*d39bd2c1SGregory Neil Shapiro **  RESET_MAIL_ESMTP_ARGS -- reset ESMTP arguments for MAIL
4971c2aa98e2SPeter Wemm **
4972c2aa98e2SPeter Wemm **	Parameters:
4973c2aa98e2SPeter Wemm **		e -- the envelope.
4974c2aa98e2SPeter Wemm **
4975c2aa98e2SPeter Wemm **	Returns:
4976c2aa98e2SPeter Wemm **		none.
4977c2aa98e2SPeter Wemm */
4978c2aa98e2SPeter Wemm 
4979d0cef73dSGregory Neil Shapiro void
reset_mail_esmtp_args(e)4980d0cef73dSGregory Neil Shapiro reset_mail_esmtp_args(e)
4981d0cef73dSGregory Neil Shapiro 	ENVELOPE *e;
4982d0cef73dSGregory Neil Shapiro {
4983d0cef73dSGregory Neil Shapiro 	/* "size": no reset */
4984d0cef73dSGregory Neil Shapiro 
4985d0cef73dSGregory Neil Shapiro 	/* "body" */
4986*d39bd2c1SGregory Neil Shapiro 	e->e_flags &= ~EF_7BITBODY;
4987d0cef73dSGregory Neil Shapiro 	e->e_bodytype = NULL;
4988d0cef73dSGregory Neil Shapiro 
4989d0cef73dSGregory Neil Shapiro 	/* "envid" */
4990d0cef73dSGregory Neil Shapiro 	e->e_envid = NULL;
4991d0cef73dSGregory Neil Shapiro 	macdefine(&e->e_macro, A_PERM, macid("{dsn_envid}"), NULL);
4992d0cef73dSGregory Neil Shapiro 
4993d0cef73dSGregory Neil Shapiro 	/* "ret" */
4994ffb83623SGregory Neil Shapiro 	e->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
4995d0cef73dSGregory Neil Shapiro 	macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), NULL);
4996d0cef73dSGregory Neil Shapiro 
4997d0cef73dSGregory Neil Shapiro #if SASL
4998d0cef73dSGregory Neil Shapiro 	/* "auth" */
4999d0cef73dSGregory Neil Shapiro 	macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), NULL);
5000d0cef73dSGregory Neil Shapiro 	e->e_auth_param = "";
5001d0cef73dSGregory Neil Shapiro # if _FFR_AUTH_PASSING
5002d0cef73dSGregory Neil Shapiro 	macdefine(&BlankEnvelope.e_macro, A_PERM,
5003d0cef73dSGregory Neil Shapiro 				  macid("{auth_author}"), NULL);
50045b0945b5SGregory Neil Shapiro # endif
5005d0cef73dSGregory Neil Shapiro #endif /* SASL */
5006d0cef73dSGregory Neil Shapiro 
5007d0cef73dSGregory Neil Shapiro 	/* "by" */
5008d0cef73dSGregory Neil Shapiro 	e->e_deliver_by = 0;
5009d0cef73dSGregory Neil Shapiro 	e->e_dlvr_flag = 0;
5010d0cef73dSGregory Neil Shapiro }
5011d0cef73dSGregory Neil Shapiro 
5012d0cef73dSGregory Neil Shapiro /*
5013d0cef73dSGregory Neil Shapiro **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
5014d0cef73dSGregory Neil Shapiro **
5015d0cef73dSGregory Neil Shapiro **	Parameters:
5016d0cef73dSGregory Neil Shapiro **		a -- address (unused, for compatibility with rcpt_esmtp_args)
5017d0cef73dSGregory Neil Shapiro **		kp -- the parameter key.
5018d0cef73dSGregory Neil Shapiro **		vp -- the value of that parameter.
5019d0cef73dSGregory Neil Shapiro **		e -- the envelope.
5020d0cef73dSGregory Neil Shapiro **
5021d0cef73dSGregory Neil Shapiro **	Returns:
5022d0cef73dSGregory Neil Shapiro **		none.
5023d0cef73dSGregory Neil Shapiro */
5024d0cef73dSGregory Neil Shapiro 
5025d0cef73dSGregory Neil Shapiro void
mail_esmtp_args(a,kp,vp,e)5026d0cef73dSGregory Neil Shapiro mail_esmtp_args(a, kp, vp, e)
5027d0cef73dSGregory Neil Shapiro 	ADDRESS *a;
5028c2aa98e2SPeter Wemm 	char *kp;
5029c2aa98e2SPeter Wemm 	char *vp;
5030c2aa98e2SPeter Wemm 	ENVELOPE *e;
5031c2aa98e2SPeter Wemm {
50322fb4f839SGregory Neil Shapiro 	if (SM_STRCASEEQ(kp, "size"))
5033c2aa98e2SPeter Wemm 	{
5034c2aa98e2SPeter Wemm 		if (vp == NULL)
5035c2aa98e2SPeter Wemm 		{
503606f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.2 SIZE requires a value");
5037c2aa98e2SPeter Wemm 			/* NOTREACHED */
5038c2aa98e2SPeter Wemm 		}
503940266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
504040266059SGregory Neil Shapiro 		errno = 0;
5041c2aa98e2SPeter Wemm 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
504242e5d165SGregory Neil Shapiro 		if (e->e_msgsize == LONG_MAX && errno == ERANGE)
504342e5d165SGregory Neil Shapiro 		{
504442e5d165SGregory Neil Shapiro 			usrerr("552 5.2.3 Message size exceeds maximum value");
504542e5d165SGregory Neil Shapiro 			/* NOTREACHED */
504642e5d165SGregory Neil Shapiro 		}
504740266059SGregory Neil Shapiro 		if (e->e_msgsize < 0)
504840266059SGregory Neil Shapiro 		{
504940266059SGregory Neil Shapiro 			usrerr("552 5.2.3 Message size invalid");
505040266059SGregory Neil Shapiro 			/* NOTREACHED */
5051c2aa98e2SPeter Wemm 		}
505240266059SGregory Neil Shapiro 	}
50532fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(kp, "body"))
5054c2aa98e2SPeter Wemm 	{
5055c2aa98e2SPeter Wemm 		if (vp == NULL)
5056c2aa98e2SPeter Wemm 		{
505706f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.2 BODY requires a value");
5058c2aa98e2SPeter Wemm 			/* NOTREACHED */
5059c2aa98e2SPeter Wemm 		}
50602fb4f839SGregory Neil Shapiro 		else if (SM_STRCASEEQ(vp, "8bitmime"))
5061*d39bd2c1SGregory Neil Shapiro 			;
50622fb4f839SGregory Neil Shapiro 		else if (SM_STRCASEEQ(vp, "7bit"))
5063*d39bd2c1SGregory Neil Shapiro 			e->e_flags |= EF_7BITBODY;
5064c2aa98e2SPeter Wemm 		else
5065c2aa98e2SPeter Wemm 		{
50662fb4f839SGregory Neil Shapiro 			usrerr("501 5.5.4 Unknown BODY type %s",
50672fb4f839SGregory Neil Shapiro 				SHOWCMDINREPLY(vp));
5068c2aa98e2SPeter Wemm 			/* NOTREACHED */
5069c2aa98e2SPeter Wemm 		}
507040266059SGregory Neil Shapiro 		e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
5071c2aa98e2SPeter Wemm 	}
50722fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(kp, "envid"))
5073c2aa98e2SPeter Wemm 	{
5074d0cef73dSGregory Neil Shapiro 		if (!bitset(SRV_OFFER_DSN, e->e_features))
507506f25ae9SGregory Neil Shapiro 		{
507606f25ae9SGregory Neil Shapiro 			usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
507706f25ae9SGregory Neil Shapiro 			/* NOTREACHED */
507806f25ae9SGregory Neil Shapiro 		}
5079c2aa98e2SPeter Wemm 		if (vp == NULL)
5080c2aa98e2SPeter Wemm 		{
508106f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.2 ENVID requires a value");
5082c2aa98e2SPeter Wemm 			/* NOTREACHED */
5083c2aa98e2SPeter Wemm 		}
5084c2aa98e2SPeter Wemm 		if (!xtextok(vp))
5085c2aa98e2SPeter Wemm 		{
508606f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.4 Syntax error in ENVID parameter value");
5087c2aa98e2SPeter Wemm 			/* NOTREACHED */
5088c2aa98e2SPeter Wemm 		}
5089c2aa98e2SPeter Wemm 		if (e->e_envid != NULL)
5090c2aa98e2SPeter Wemm 		{
509106f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.0 Duplicate ENVID parameter");
5092c2aa98e2SPeter Wemm 			/* NOTREACHED */
5093c2aa98e2SPeter Wemm 		}
509440266059SGregory Neil Shapiro 		e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
509540266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_PERM,
509640266059SGregory Neil Shapiro 			macid("{dsn_envid}"), e->e_envid);
5097c2aa98e2SPeter Wemm 	}
50982fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(kp, "ret"))
5099c2aa98e2SPeter Wemm 	{
5100d0cef73dSGregory Neil Shapiro 		if (!bitset(SRV_OFFER_DSN, e->e_features))
510106f25ae9SGregory Neil Shapiro 		{
510206f25ae9SGregory Neil Shapiro 			usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
510306f25ae9SGregory Neil Shapiro 			/* NOTREACHED */
510406f25ae9SGregory Neil Shapiro 		}
5105c2aa98e2SPeter Wemm 		if (vp == NULL)
5106c2aa98e2SPeter Wemm 		{
510706f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.2 RET requires a value");
5108c2aa98e2SPeter Wemm 			/* NOTREACHED */
5109c2aa98e2SPeter Wemm 		}
5110c2aa98e2SPeter Wemm 		if (bitset(EF_RET_PARAM, e->e_flags))
5111c2aa98e2SPeter Wemm 		{
511206f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.0 Duplicate RET parameter");
5113c2aa98e2SPeter Wemm 			/* NOTREACHED */
5114c2aa98e2SPeter Wemm 		}
5115c2aa98e2SPeter Wemm 		e->e_flags |= EF_RET_PARAM;
51162fb4f839SGregory Neil Shapiro 		if (SM_STRCASEEQ(vp, "hdrs"))
5117c2aa98e2SPeter Wemm 			e->e_flags |= EF_NO_BODY_RETN;
511840266059SGregory Neil Shapiro 		else if (sm_strcasecmp(vp, "full") != 0)
5119c2aa98e2SPeter Wemm 		{
51202fb4f839SGregory Neil Shapiro 			usrerr("501 5.5.2 Bad argument \"%s\" to RET",
51212fb4f839SGregory Neil Shapiro 				SHOWCMDINREPLY(vp));
5122c2aa98e2SPeter Wemm 			/* NOTREACHED */
5123c2aa98e2SPeter Wemm 		}
512440266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
512506f25ae9SGregory Neil Shapiro 	}
512606f25ae9SGregory Neil Shapiro #if SASL
51272fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(kp, "auth"))
512806f25ae9SGregory Neil Shapiro 	{
512906f25ae9SGregory Neil Shapiro 		int len;
513006f25ae9SGregory Neil Shapiro 		char *q;
513106f25ae9SGregory Neil Shapiro 		char *auth_param;	/* the value of the AUTH=x */
513206f25ae9SGregory Neil Shapiro 		bool saveQuickAbort = QuickAbort;
513306f25ae9SGregory Neil Shapiro 		bool saveSuprErrs = SuprErrs;
513440266059SGregory Neil Shapiro 		bool saveExitStat = ExitStat;
513506f25ae9SGregory Neil Shapiro 
513606f25ae9SGregory Neil Shapiro 		if (vp == NULL)
513706f25ae9SGregory Neil Shapiro 		{
513806f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.2 AUTH= requires a value");
513906f25ae9SGregory Neil Shapiro 			/* NOTREACHED */
514006f25ae9SGregory Neil Shapiro 		}
514106f25ae9SGregory Neil Shapiro 		if (e->e_auth_param != NULL)
514206f25ae9SGregory Neil Shapiro 		{
514306f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.0 Duplicate AUTH parameter");
514406f25ae9SGregory Neil Shapiro 			/* NOTREACHED */
514506f25ae9SGregory Neil Shapiro 		}
514606f25ae9SGregory Neil Shapiro 		if ((q = strchr(vp, ' ')) != NULL)
514706f25ae9SGregory Neil Shapiro 			len = q - vp + 1;
514806f25ae9SGregory Neil Shapiro 		else
514906f25ae9SGregory Neil Shapiro 			len = strlen(vp) + 1;
515006f25ae9SGregory Neil Shapiro 		auth_param = xalloc(len);
515140266059SGregory Neil Shapiro 		(void) sm_strlcpy(auth_param, vp, len);
515206f25ae9SGregory Neil Shapiro 		if (!xtextok(auth_param))
515306f25ae9SGregory Neil Shapiro 		{
515406f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.4 Syntax error in AUTH parameter value");
515506f25ae9SGregory Neil Shapiro 			/* just a warning? */
515606f25ae9SGregory Neil Shapiro 			/* NOTREACHED */
515706f25ae9SGregory Neil Shapiro 		}
515806f25ae9SGregory Neil Shapiro 
515906f25ae9SGregory Neil Shapiro 		/* XXX define this always or only if trusted? */
5160e92d3f3fSGregory Neil Shapiro 		macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"),
5161e92d3f3fSGregory Neil Shapiro 			  auth_param);
516206f25ae9SGregory Neil Shapiro 
516306f25ae9SGregory Neil Shapiro 		/*
516406f25ae9SGregory Neil Shapiro 		**  call Strust_auth to find out whether
516506f25ae9SGregory Neil Shapiro 		**  auth_param is acceptable (trusted)
516606f25ae9SGregory Neil Shapiro 		**  we shouldn't trust it if not authenticated
516706f25ae9SGregory Neil Shapiro 		**  (required by RFC, leave it to ruleset?)
516806f25ae9SGregory Neil Shapiro 		*/
516906f25ae9SGregory Neil Shapiro 
517040266059SGregory Neil Shapiro 		SuprErrs = true;
517140266059SGregory Neil Shapiro 		QuickAbort = false;
517206f25ae9SGregory Neil Shapiro 		if (strcmp(auth_param, "<>") != 0 &&
5173da7d7b9cSGregory Neil Shapiro 		     (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM, 9,
5174da7d7b9cSGregory Neil Shapiro 			      NULL, NOQID, NULL, NULL) != EX_OK || Errors > 0))
517506f25ae9SGregory Neil Shapiro 		{
517606f25ae9SGregory Neil Shapiro 			if (tTd(95, 8))
517706f25ae9SGregory Neil Shapiro 			{
517806f25ae9SGregory Neil Shapiro 				q = e->e_auth_param;
517940266059SGregory Neil Shapiro 				sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
5180e92d3f3fSGregory Neil Shapiro 					auth_param, (q == NULL) ? "" : q);
518106f25ae9SGregory Neil Shapiro 			}
518240266059SGregory Neil Shapiro 
518306f25ae9SGregory Neil Shapiro 			/* not trusted */
518440266059SGregory Neil Shapiro 			e->e_auth_param = "<>";
518540266059SGregory Neil Shapiro # if _FFR_AUTH_PASSING
518640266059SGregory Neil Shapiro 			macdefine(&BlankEnvelope.e_macro, A_PERM,
518740266059SGregory Neil Shapiro 				  macid("{auth_author}"), NULL);
51885b0945b5SGregory Neil Shapiro # endif
5189c2aa98e2SPeter Wemm 		}
5190c2aa98e2SPeter Wemm 		else
5191c2aa98e2SPeter Wemm 		{
519206f25ae9SGregory Neil Shapiro 			if (tTd(95, 8))
5193e92d3f3fSGregory Neil Shapiro 				sm_dprintf("auth=\"%.100s\" trusted\n", auth_param);
519440266059SGregory Neil Shapiro 			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
519540266059SGregory Neil Shapiro 							    auth_param);
519606f25ae9SGregory Neil Shapiro 		}
519740266059SGregory Neil Shapiro 		sm_free(auth_param); /* XXX */
51988774250cSGregory Neil Shapiro 
519906f25ae9SGregory Neil Shapiro 		/* reset values */
520006f25ae9SGregory Neil Shapiro 		Errors = 0;
520106f25ae9SGregory Neil Shapiro 		QuickAbort = saveQuickAbort;
520206f25ae9SGregory Neil Shapiro 		SuprErrs = saveSuprErrs;
520340266059SGregory Neil Shapiro 		ExitStat = saveExitStat;
520406f25ae9SGregory Neil Shapiro 	}
520506f25ae9SGregory Neil Shapiro #endif /* SASL */
520640266059SGregory Neil Shapiro #define PRTCHAR(c)	((isascii(c) && isprint(c)) ? (c) : '?')
520740266059SGregory Neil Shapiro 
520840266059SGregory Neil Shapiro 	/*
520940266059SGregory Neil Shapiro 	**  "by" is only accepted if DeliverByMin >= 0.
521040266059SGregory Neil Shapiro 	**  We maybe could add this to the list of server_features.
521140266059SGregory Neil Shapiro 	*/
521240266059SGregory Neil Shapiro 
52132fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(kp, "by") && DeliverByMin >= 0)
521440266059SGregory Neil Shapiro 	{
521540266059SGregory Neil Shapiro 		char *s;
521640266059SGregory Neil Shapiro 
521740266059SGregory Neil Shapiro 		if (vp == NULL)
521840266059SGregory Neil Shapiro 		{
521940266059SGregory Neil Shapiro 			usrerr("501 5.5.2 BY= requires a value");
522040266059SGregory Neil Shapiro 			/* NOTREACHED */
522140266059SGregory Neil Shapiro 		}
522240266059SGregory Neil Shapiro 		errno = 0;
522340266059SGregory Neil Shapiro 		e->e_deliver_by = strtol(vp, &s, 10);
522440266059SGregory Neil Shapiro 		if (e->e_deliver_by == LONG_MIN ||
522540266059SGregory Neil Shapiro 		    e->e_deliver_by == LONG_MAX ||
522640266059SGregory Neil Shapiro 		    e->e_deliver_by > 999999999l ||
522740266059SGregory Neil Shapiro 		    e->e_deliver_by < -999999999l)
522840266059SGregory Neil Shapiro 		{
52292fb4f839SGregory Neil Shapiro 			usrerr("501 5.5.2 BY=%s out of range",
52302fb4f839SGregory Neil Shapiro 				SHOWCMDINREPLY(vp));
523140266059SGregory Neil Shapiro 			/* NOTREACHED */
523240266059SGregory Neil Shapiro 		}
523340266059SGregory Neil Shapiro 		if (s == NULL || *s != ';')
523440266059SGregory Neil Shapiro 		{
523540266059SGregory Neil Shapiro 			usrerr("501 5.5.2 BY= missing ';'");
523640266059SGregory Neil Shapiro 			/* NOTREACHED */
523740266059SGregory Neil Shapiro 		}
523840266059SGregory Neil Shapiro 		e->e_dlvr_flag = 0;
523940266059SGregory Neil Shapiro 		++s;	/* XXX: spaces allowed? */
524040266059SGregory Neil Shapiro 		SKIP_SPACE(s);
524140266059SGregory Neil Shapiro 		switch (tolower(*s))
524240266059SGregory Neil Shapiro 		{
524340266059SGregory Neil Shapiro 		  case 'n':
524440266059SGregory Neil Shapiro 			e->e_dlvr_flag = DLVR_NOTIFY;
524540266059SGregory Neil Shapiro 			break;
524640266059SGregory Neil Shapiro 		  case 'r':
524740266059SGregory Neil Shapiro 			e->e_dlvr_flag = DLVR_RETURN;
524840266059SGregory Neil Shapiro 			if (e->e_deliver_by <= 0)
524940266059SGregory Neil Shapiro 			{
525040266059SGregory Neil Shapiro 				usrerr("501 5.5.4 mode R requires BY time > 0");
525140266059SGregory Neil Shapiro 				/* NOTREACHED */
525240266059SGregory Neil Shapiro 			}
525340266059SGregory Neil Shapiro 			if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
525440266059SGregory Neil Shapiro 			    e->e_deliver_by < DeliverByMin)
525540266059SGregory Neil Shapiro 			{
525640266059SGregory Neil Shapiro 				usrerr("555 5.5.2 time %ld less than %ld",
525740266059SGregory Neil Shapiro 					e->e_deliver_by, (long) DeliverByMin);
525840266059SGregory Neil Shapiro 				/* NOTREACHED */
525940266059SGregory Neil Shapiro 			}
526040266059SGregory Neil Shapiro 			break;
526140266059SGregory Neil Shapiro 		  default:
526240266059SGregory Neil Shapiro 			usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
526340266059SGregory Neil Shapiro 			/* NOTREACHED */
526440266059SGregory Neil Shapiro 		}
526540266059SGregory Neil Shapiro 		++s;	/* XXX: spaces allowed? */
526640266059SGregory Neil Shapiro 		SKIP_SPACE(s);
526740266059SGregory Neil Shapiro 		switch (tolower(*s))
526840266059SGregory Neil Shapiro 		{
526940266059SGregory Neil Shapiro 		  case 't':
527040266059SGregory Neil Shapiro 			e->e_dlvr_flag |= DLVR_TRACE;
527140266059SGregory Neil Shapiro 			break;
527240266059SGregory Neil Shapiro 		  case '\0':
527340266059SGregory Neil Shapiro 			break;
527440266059SGregory Neil Shapiro 		  default:
527540266059SGregory Neil Shapiro 			usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
527640266059SGregory Neil Shapiro 			/* NOTREACHED */
527740266059SGregory Neil Shapiro 		}
527840266059SGregory Neil Shapiro 
527940266059SGregory Neil Shapiro 		/* XXX: check whether more characters follow? */
528040266059SGregory Neil Shapiro 	}
52812fb4f839SGregory Neil Shapiro #if USE_EAI
52822fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(kp, "smtputf8"))
52835b0945b5SGregory Neil Shapiro 	{
52845b0945b5SGregory Neil Shapiro 		if (!bitset(SRV_OFFER_EAI, e->e_features))
52855b0945b5SGregory Neil Shapiro 		{
52862fb4f839SGregory Neil Shapiro 			usrerr("504 5.7.0 Sorry, SMTPUTF8 not supported");
52875b0945b5SGregory Neil Shapiro 			/* NOTREACHED */
52885b0945b5SGregory Neil Shapiro 		}
52895b0945b5SGregory Neil Shapiro 		e->e_smtputf8 = true;
52905b0945b5SGregory Neil Shapiro 	}
52915b0945b5SGregory Neil Shapiro #endif
529206f25ae9SGregory Neil Shapiro 	else
529306f25ae9SGregory Neil Shapiro 	{
52942fb4f839SGregory Neil Shapiro 		usrerr("555 5.5.4 %s parameter unrecognized",
52952fb4f839SGregory Neil Shapiro 			SHOWCMDINREPLY(kp));
5296c2aa98e2SPeter Wemm 		/* NOTREACHED */
5297c2aa98e2SPeter Wemm 	}
5298c2aa98e2SPeter Wemm }
5299d0cef73dSGregory Neil Shapiro 
530040266059SGregory Neil Shapiro /*
5301c2aa98e2SPeter Wemm **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
5302c2aa98e2SPeter Wemm **
5303c2aa98e2SPeter Wemm **	Parameters:
5304c2aa98e2SPeter Wemm **		a -- the address corresponding to the To: parameter.
5305c2aa98e2SPeter Wemm **		kp -- the parameter key.
5306c2aa98e2SPeter Wemm **		vp -- the value of that parameter.
5307c2aa98e2SPeter Wemm **		e -- the envelope.
5308c2aa98e2SPeter Wemm **
5309c2aa98e2SPeter Wemm **	Returns:
5310c2aa98e2SPeter Wemm **		none.
5311c2aa98e2SPeter Wemm */
5312c2aa98e2SPeter Wemm 
5313d0cef73dSGregory Neil Shapiro void
rcpt_esmtp_args(a,kp,vp,e)5314d0cef73dSGregory Neil Shapiro rcpt_esmtp_args(a, kp, vp, e)
5315c2aa98e2SPeter Wemm 	ADDRESS *a;
5316c2aa98e2SPeter Wemm 	char *kp;
5317c2aa98e2SPeter Wemm 	char *vp;
5318c2aa98e2SPeter Wemm 	ENVELOPE *e;
5319c2aa98e2SPeter Wemm {
53202fb4f839SGregory Neil Shapiro 	if (SM_STRCASEEQ(kp, "notify"))
5321c2aa98e2SPeter Wemm 	{
5322c2aa98e2SPeter Wemm 		char *p;
5323c2aa98e2SPeter Wemm 
5324d0cef73dSGregory Neil Shapiro 		if (!bitset(SRV_OFFER_DSN, e->e_features))
532506f25ae9SGregory Neil Shapiro 		{
532606f25ae9SGregory Neil Shapiro 			usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
532706f25ae9SGregory Neil Shapiro 			/* NOTREACHED */
532806f25ae9SGregory Neil Shapiro 		}
5329c2aa98e2SPeter Wemm 		if (vp == NULL)
5330c2aa98e2SPeter Wemm 		{
533106f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.2 NOTIFY requires a value");
5332c2aa98e2SPeter Wemm 			/* NOTREACHED */
5333c2aa98e2SPeter Wemm 		}
5334c2aa98e2SPeter Wemm 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
5335c2aa98e2SPeter Wemm 		a->q_flags |= QHASNOTIFY;
533640266059SGregory Neil Shapiro 		macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
533706f25ae9SGregory Neil Shapiro 
53382fb4f839SGregory Neil Shapiro 		if (SM_STRCASEEQ(vp, "never"))
5339c2aa98e2SPeter Wemm 			return;
5340c2aa98e2SPeter Wemm 		for (p = vp; p != NULL; vp = p)
5341c2aa98e2SPeter Wemm 		{
5342b6bacd31SGregory Neil Shapiro 			char *s;
5343b6bacd31SGregory Neil Shapiro 
5344b6bacd31SGregory Neil Shapiro 			s = p = strchr(p, ',');
5345c2aa98e2SPeter Wemm 			if (p != NULL)
5346c2aa98e2SPeter Wemm 				*p++ = '\0';
53472fb4f839SGregory Neil Shapiro 			if (SM_STRCASEEQ(vp, "success"))
5348c2aa98e2SPeter Wemm 				a->q_flags |= QPINGONSUCCESS;
53492fb4f839SGregory Neil Shapiro 			else if (SM_STRCASEEQ(vp, "failure"))
5350c2aa98e2SPeter Wemm 				a->q_flags |= QPINGONFAILURE;
53512fb4f839SGregory Neil Shapiro 			else if (SM_STRCASEEQ(vp, "delay"))
5352c2aa98e2SPeter Wemm 				a->q_flags |= QPINGONDELAY;
5353c2aa98e2SPeter Wemm 			else
5354c2aa98e2SPeter Wemm 			{
535506f25ae9SGregory Neil Shapiro 				usrerr("501 5.5.4 Bad argument \"%s\"  to NOTIFY",
53562fb4f839SGregory Neil Shapiro 					SHOWCMDINREPLY(vp));
5357c2aa98e2SPeter Wemm 				/* NOTREACHED */
5358c2aa98e2SPeter Wemm 			}
5359b6bacd31SGregory Neil Shapiro 			if (s != NULL)
5360b6bacd31SGregory Neil Shapiro 				*s = ',';
5361c2aa98e2SPeter Wemm 		}
5362c2aa98e2SPeter Wemm 	}
53632fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(kp, "orcpt"))
5364c2aa98e2SPeter Wemm 	{
5365552d4955SGregory Neil Shapiro 		char *p;
5366552d4955SGregory Neil Shapiro 
5367d0cef73dSGregory Neil Shapiro 		if (!bitset(SRV_OFFER_DSN, e->e_features))
536806f25ae9SGregory Neil Shapiro 		{
536906f25ae9SGregory Neil Shapiro 			usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
537006f25ae9SGregory Neil Shapiro 			/* NOTREACHED */
537106f25ae9SGregory Neil Shapiro 		}
5372c2aa98e2SPeter Wemm 		if (vp == NULL)
5373c2aa98e2SPeter Wemm 		{
537406f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.2 ORCPT requires a value");
5375c2aa98e2SPeter Wemm 			/* NOTREACHED */
5376c2aa98e2SPeter Wemm 		}
5377c2aa98e2SPeter Wemm 		if (a->q_orcpt != NULL)
5378c2aa98e2SPeter Wemm 		{
537906f25ae9SGregory Neil Shapiro 			usrerr("501 5.5.0 Duplicate ORCPT parameter");
5380c2aa98e2SPeter Wemm 			/* NOTREACHED */
5381c2aa98e2SPeter Wemm 		}
5382552d4955SGregory Neil Shapiro 		p = strchr(vp, ';');
5383552d4955SGregory Neil Shapiro 		if (p == NULL)
5384552d4955SGregory Neil Shapiro 		{
5385552d4955SGregory Neil Shapiro 			usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
5386552d4955SGregory Neil Shapiro 			/* NOTREACHED */
5387552d4955SGregory Neil Shapiro 		}
5388552d4955SGregory Neil Shapiro 		*p = '\0';
53892fb4f839SGregory Neil Shapiro #if USE_EAI
53902fb4f839SGregory Neil Shapiro 		if (SM_STRCASEEQ(vp, "utf-8"))
53912fb4f839SGregory Neil Shapiro 		{
53922fb4f839SGregory Neil Shapiro 			/* XXX check syntax of p+1 ! */
53932fb4f839SGregory Neil Shapiro 			if (!xtextok(p + 1) &&
53942fb4f839SGregory Neil Shapiro 			    uxtext_unquote(p + 1, NULL, MAXNAME_I) <= 0)
53952fb4f839SGregory Neil Shapiro 			{
53962fb4f839SGregory Neil Shapiro 				*p = ';';
53972fb4f839SGregory Neil Shapiro 				usrerr("501 5.5.4 Syntax error in UTF-8 ORCPT parameter value");
53982fb4f839SGregory Neil Shapiro 				/* NOTREACHED */
53992fb4f839SGregory Neil Shapiro 			}
54002fb4f839SGregory Neil Shapiro # if 0
54012fb4f839SGregory Neil Shapiro complicated... see grammar!
54022fb4f839SGregory Neil Shapiro RFC 6533 Internationalized Delivery Status and Disposition Notifications
54032fb4f839SGregory Neil Shapiro utf-8-enc-addr = utf-8-addr-xtext / utf-8-addr-unitext / utf-8-address
54042fb4f839SGregory Neil Shapiro # endif
54052fb4f839SGregory Neil Shapiro 		}
54062fb4f839SGregory Neil Shapiro 		else
54072fb4f839SGregory Neil Shapiro #endif /* USE_EAI */
54082fb4f839SGregory Neil Shapiro 		/* "else" in #if code above */
5409552d4955SGregory Neil Shapiro 		if (!isatom(vp) || !xtextok(p + 1))
5410552d4955SGregory Neil Shapiro 		{
5411552d4955SGregory Neil Shapiro 			*p = ';';
5412552d4955SGregory Neil Shapiro 			usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
5413552d4955SGregory Neil Shapiro 			/* NOTREACHED */
5414552d4955SGregory Neil Shapiro 		}
5415552d4955SGregory Neil Shapiro 		*p = ';';
541640266059SGregory Neil Shapiro 		a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
5417c2aa98e2SPeter Wemm 	}
5418c2aa98e2SPeter Wemm 	else
5419c2aa98e2SPeter Wemm 	{
54202fb4f839SGregory Neil Shapiro 		usrerr("555 5.5.4 %s parameter unrecognized",
54212fb4f839SGregory Neil Shapiro 			SHOWCMDINREPLY(kp));
5422c2aa98e2SPeter Wemm 		/* NOTREACHED */
5423c2aa98e2SPeter Wemm 	}
5424c2aa98e2SPeter Wemm }
542540266059SGregory Neil Shapiro /*
5426c2aa98e2SPeter Wemm **  PRINTVRFYADDR -- print an entry in the verify queue
5427c2aa98e2SPeter Wemm **
5428c2aa98e2SPeter Wemm **	Parameters:
542940266059SGregory Neil Shapiro **		a -- the address to print.
5430c2aa98e2SPeter Wemm **		last -- set if this is the last one.
5431c2aa98e2SPeter Wemm **		vrfy -- set if this is a VRFY command.
5432c2aa98e2SPeter Wemm **
5433c2aa98e2SPeter Wemm **	Returns:
5434c2aa98e2SPeter Wemm **		none.
5435c2aa98e2SPeter Wemm **
5436c2aa98e2SPeter Wemm **	Side Effects:
5437c2aa98e2SPeter Wemm **		Prints the appropriate 250 codes.
5438c2aa98e2SPeter Wemm */
543906f25ae9SGregory Neil Shapiro #define OFFF	(3 + 1 + 5 + 1)	/* offset in fmt: SMTP reply + enh. code */
5440c2aa98e2SPeter Wemm 
544106f25ae9SGregory Neil Shapiro static void
printvrfyaddr(a,last,vrfy)5442c2aa98e2SPeter Wemm printvrfyaddr(a, last, vrfy)
5443c2aa98e2SPeter Wemm 	register ADDRESS *a;
5444c2aa98e2SPeter Wemm 	bool last;
5445c2aa98e2SPeter Wemm 	bool vrfy;
5446c2aa98e2SPeter Wemm {
544706f25ae9SGregory Neil Shapiro 	char fmtbuf[30];
5448c2aa98e2SPeter Wemm 
5449c2aa98e2SPeter Wemm 	if (vrfy && a->q_mailer != NULL &&
5450c2aa98e2SPeter Wemm 	    !bitnset(M_VRFY250, a->q_mailer->m_flags))
5451d0cef73dSGregory Neil Shapiro 		(void) sm_strlcpy(fmtbuf, "252", sizeof(fmtbuf));
5452c2aa98e2SPeter Wemm 	else
5453d0cef73dSGregory Neil Shapiro 		(void) sm_strlcpy(fmtbuf, "250", sizeof(fmtbuf));
5454c2aa98e2SPeter Wemm 	fmtbuf[3] = last ? ' ' : '-';
5455d0cef73dSGregory Neil Shapiro 	(void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof(fmtbuf) - 4);
5456c2aa98e2SPeter Wemm 	if (a->q_fullname == NULL)
5457c2aa98e2SPeter Wemm 	{
545806f25ae9SGregory Neil Shapiro 		if ((a->q_mailer == NULL ||
545906f25ae9SGregory Neil Shapiro 		     a->q_mailer->m_addrtype == NULL ||
54602fb4f839SGregory Neil Shapiro 		     SM_STRCASEEQ(a->q_mailer->m_addrtype, "rfc822")) &&
546106f25ae9SGregory Neil Shapiro 		    strchr(a->q_user, '@') == NULL)
546240266059SGregory Neil Shapiro 			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
5463d0cef73dSGregory Neil Shapiro 				       sizeof(fmtbuf) - OFFF);
5464c2aa98e2SPeter Wemm 		else
546540266059SGregory Neil Shapiro 			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
5466d0cef73dSGregory Neil Shapiro 				       sizeof(fmtbuf) - OFFF);
5467c2aa98e2SPeter Wemm 		message(fmtbuf, a->q_user, MyHostName);
5468c2aa98e2SPeter Wemm 	}
5469c2aa98e2SPeter Wemm 	else
5470c2aa98e2SPeter Wemm 	{
547106f25ae9SGregory Neil Shapiro 		if ((a->q_mailer == NULL ||
547206f25ae9SGregory Neil Shapiro 		     a->q_mailer->m_addrtype == NULL ||
54732fb4f839SGregory Neil Shapiro 		     SM_STRCASEEQ(a->q_mailer->m_addrtype, "rfc822")) &&
547406f25ae9SGregory Neil Shapiro 		    strchr(a->q_user, '@') == NULL)
547540266059SGregory Neil Shapiro 			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
5476d0cef73dSGregory Neil Shapiro 				       sizeof(fmtbuf) - OFFF);
5477c2aa98e2SPeter Wemm 		else
547840266059SGregory Neil Shapiro 			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
5479d0cef73dSGregory Neil Shapiro 				       sizeof(fmtbuf) - OFFF);
5480c2aa98e2SPeter Wemm 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
5481c2aa98e2SPeter Wemm 	}
5482c2aa98e2SPeter Wemm }
5483c2aa98e2SPeter Wemm 
548406f25ae9SGregory Neil Shapiro #if SASL
548540266059SGregory Neil Shapiro /*
548606f25ae9SGregory Neil Shapiro **  SASLMECHS -- get list of possible AUTH mechanisms
548706f25ae9SGregory Neil Shapiro **
548806f25ae9SGregory Neil Shapiro **	Parameters:
548940266059SGregory Neil Shapiro **		conn -- SASL connection info.
549040266059SGregory Neil Shapiro **		mechlist -- output parameter for list of mechanisms.
549106f25ae9SGregory Neil Shapiro **
549206f25ae9SGregory Neil Shapiro **	Returns:
549340266059SGregory Neil Shapiro **		number of mechs.
549406f25ae9SGregory Neil Shapiro */
549506f25ae9SGregory Neil Shapiro 
549606f25ae9SGregory Neil Shapiro static int
saslmechs(conn,mechlist)549706f25ae9SGregory Neil Shapiro saslmechs(conn, mechlist)
549806f25ae9SGregory Neil Shapiro 	sasl_conn_t *conn;
549906f25ae9SGregory Neil Shapiro 	char **mechlist;
550006f25ae9SGregory Neil Shapiro {
550106f25ae9SGregory Neil Shapiro 	int len, num, result;
550206f25ae9SGregory Neil Shapiro 
550306f25ae9SGregory Neil Shapiro 	/* "user" is currently unused */
550494c01205SGregory Neil Shapiro # if SASL >= 20000
550594c01205SGregory Neil Shapiro 	result = sasl_listmech(conn, NULL,
550694c01205SGregory Neil Shapiro 			       "", " ", "", (const char **) mechlist,
5507a7ec597cSGregory Neil Shapiro 			       (unsigned int *)&len, &num);
550894c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
550906f25ae9SGregory Neil Shapiro 	result = sasl_listmech(conn, "user", /* XXX */
551006f25ae9SGregory Neil Shapiro 			       "", " ", "", mechlist,
551140266059SGregory Neil Shapiro 			       (unsigned int *)&len, (unsigned int *)&num);
551294c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
551340266059SGregory Neil Shapiro 	if (result != SASL_OK)
551406f25ae9SGregory Neil Shapiro 	{
551506f25ae9SGregory Neil Shapiro 		if (LogLevel > 9)
551606f25ae9SGregory Neil Shapiro 			sm_syslog(LOG_WARNING, NOQID,
551740266059SGregory Neil Shapiro 				  "AUTH error: listmech=%d, num=%d",
551806f25ae9SGregory Neil Shapiro 				  result, num);
5519193538b7SGregory Neil Shapiro 		num = 0;
552006f25ae9SGregory Neil Shapiro 	}
552140266059SGregory Neil Shapiro 	if (num > 0)
552240266059SGregory Neil Shapiro 	{
552340266059SGregory Neil Shapiro 		if (LogLevel > 11)
552440266059SGregory Neil Shapiro 			sm_syslog(LOG_INFO, NOQID,
552540266059SGregory Neil Shapiro 				  "AUTH: available mech=%s, allowed mech=%s",
552640266059SGregory Neil Shapiro 				  *mechlist, AuthMechanisms);
552740266059SGregory Neil Shapiro 		*mechlist = intersect(AuthMechanisms, *mechlist, NULL);
552840266059SGregory Neil Shapiro 	}
552940266059SGregory Neil Shapiro 	else
553040266059SGregory Neil Shapiro 	{
553140266059SGregory Neil Shapiro 		*mechlist = NULL;	/* be paranoid... */
553240266059SGregory Neil Shapiro 		if (result == SASL_OK && LogLevel > 9)
553340266059SGregory Neil Shapiro 			sm_syslog(LOG_WARNING, NOQID,
553440266059SGregory Neil Shapiro 				  "AUTH warning: no mechanisms");
553540266059SGregory Neil Shapiro 	}
553606f25ae9SGregory Neil Shapiro 	return num;
553706f25ae9SGregory Neil Shapiro }
553894c01205SGregory Neil Shapiro 
553994c01205SGregory Neil Shapiro # if SASL >= 20000
554094c01205SGregory Neil Shapiro /*
554194c01205SGregory Neil Shapiro **  PROXY_POLICY -- define proxy policy for AUTH
554294c01205SGregory Neil Shapiro **
554394c01205SGregory Neil Shapiro **	Parameters:
554494c01205SGregory Neil Shapiro **		conn -- unused.
554594c01205SGregory Neil Shapiro **		context -- unused.
554694c01205SGregory Neil Shapiro **		requested_user -- authorization identity.
554794c01205SGregory Neil Shapiro **		rlen -- authorization identity length.
554894c01205SGregory Neil Shapiro **		auth_identity -- authentication identity.
554994c01205SGregory Neil Shapiro **		alen -- authentication identity length.
555094c01205SGregory Neil Shapiro **		def_realm -- default user realm.
555194c01205SGregory Neil Shapiro **		urlen -- user realm length.
555294c01205SGregory Neil Shapiro **		propctx -- unused.
555394c01205SGregory Neil Shapiro **
555494c01205SGregory Neil Shapiro **	Returns:
555594c01205SGregory Neil Shapiro **		ok?
555694c01205SGregory Neil Shapiro **
555794c01205SGregory Neil Shapiro **	Side Effects:
555894c01205SGregory Neil Shapiro **		sets {auth_authen} macro.
555994c01205SGregory Neil Shapiro */
556094c01205SGregory Neil Shapiro 
556194c01205SGregory Neil Shapiro int
proxy_policy(conn,context,requested_user,rlen,auth_identity,alen,def_realm,urlen,propctx)556294c01205SGregory Neil Shapiro proxy_policy(conn, context, requested_user, rlen, auth_identity, alen,
556394c01205SGregory Neil Shapiro 	     def_realm, urlen, propctx)
556494c01205SGregory Neil Shapiro 	sasl_conn_t *conn;
556594c01205SGregory Neil Shapiro 	void *context;
556694c01205SGregory Neil Shapiro 	const char *requested_user;
556794c01205SGregory Neil Shapiro 	unsigned rlen;
556894c01205SGregory Neil Shapiro 	const char *auth_identity;
556994c01205SGregory Neil Shapiro 	unsigned alen;
557094c01205SGregory Neil Shapiro 	const char *def_realm;
557194c01205SGregory Neil Shapiro 	unsigned urlen;
557294c01205SGregory Neil Shapiro 	struct propctx *propctx;
557394c01205SGregory Neil Shapiro {
557494c01205SGregory Neil Shapiro 	if (auth_identity == NULL)
557594c01205SGregory Neil Shapiro 		return SASL_FAIL;
557694c01205SGregory Neil Shapiro 
557794c01205SGregory Neil Shapiro 	macdefine(&BlankEnvelope.e_macro, A_TEMP,
55786f9c8e5bSGregory Neil Shapiro 		  macid("{auth_authen}"),
55796f9c8e5bSGregory Neil Shapiro 		  xtextify((char *) auth_identity, "=<>\")"));
558094c01205SGregory Neil Shapiro 
558194c01205SGregory Neil Shapiro 	return SASL_OK;
558294c01205SGregory Neil Shapiro }
558394c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
558494c01205SGregory Neil Shapiro 
558540266059SGregory Neil Shapiro /*
558606f25ae9SGregory Neil Shapiro **  PROXY_POLICY -- define proxy policy for AUTH
558706f25ae9SGregory Neil Shapiro **
558806f25ae9SGregory Neil Shapiro **	Parameters:
558940266059SGregory Neil Shapiro **		context -- unused.
559040266059SGregory Neil Shapiro **		auth_identity -- authentication identity.
559140266059SGregory Neil Shapiro **		requested_user -- authorization identity.
559240266059SGregory Neil Shapiro **		user -- allowed user (output).
559340266059SGregory Neil Shapiro **		errstr -- possible error string (output).
559406f25ae9SGregory Neil Shapiro **
559506f25ae9SGregory Neil Shapiro **	Returns:
559606f25ae9SGregory Neil Shapiro **		ok?
559706f25ae9SGregory Neil Shapiro */
559806f25ae9SGregory Neil Shapiro 
559906f25ae9SGregory Neil Shapiro int
proxy_policy(context,auth_identity,requested_user,user,errstr)560006f25ae9SGregory Neil Shapiro proxy_policy(context, auth_identity, requested_user, user, errstr)
560106f25ae9SGregory Neil Shapiro 	void *context;
560206f25ae9SGregory Neil Shapiro 	const char *auth_identity;
560306f25ae9SGregory Neil Shapiro 	const char *requested_user;
560406f25ae9SGregory Neil Shapiro 	const char **user;
560506f25ae9SGregory Neil Shapiro 	const char **errstr;
560606f25ae9SGregory Neil Shapiro {
560706f25ae9SGregory Neil Shapiro 	if (user == NULL || auth_identity == NULL)
560806f25ae9SGregory Neil Shapiro 		return SASL_FAIL;
560906f25ae9SGregory Neil Shapiro 	*user = newstr(auth_identity);
561006f25ae9SGregory Neil Shapiro 	return SASL_OK;
561106f25ae9SGregory Neil Shapiro }
561294c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
561306f25ae9SGregory Neil Shapiro #endif /* SASL */
561406f25ae9SGregory Neil Shapiro 
561506f25ae9SGregory Neil Shapiro #if STARTTLS
561606f25ae9SGregory Neil Shapiro /*
561706f25ae9SGregory Neil Shapiro **  INITSRVTLS -- initialize server side TLS
561806f25ae9SGregory Neil Shapiro **
561906f25ae9SGregory Neil Shapiro **	Parameters:
562040266059SGregory Neil Shapiro **		tls_ok -- should tls initialization be done?
562106f25ae9SGregory Neil Shapiro **
562206f25ae9SGregory Neil Shapiro **	Returns:
562306f25ae9SGregory Neil Shapiro **		succeeded?
56248774250cSGregory Neil Shapiro **
56258774250cSGregory Neil Shapiro **	Side Effects:
562640266059SGregory Neil Shapiro **		sets tls_ok_srv which is a static variable in this module.
562740266059SGregory Neil Shapiro **		Do NOT remove assignments to it!
562806f25ae9SGregory Neil Shapiro */
562906f25ae9SGregory Neil Shapiro 
563006f25ae9SGregory Neil Shapiro bool
initsrvtls(tls_ok)563140266059SGregory Neil Shapiro initsrvtls(tls_ok)
563240266059SGregory Neil Shapiro 	bool tls_ok;
563306f25ae9SGregory Neil Shapiro {
563440266059SGregory Neil Shapiro 	if (!tls_ok)
563540266059SGregory Neil Shapiro 		return false;
563606f25ae9SGregory Neil Shapiro 
563740266059SGregory Neil Shapiro 	/* do NOT remove assignment */
56389bd497b8SGregory Neil Shapiro 	tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, Srv_SSL_Options, true,
56399bd497b8SGregory Neil Shapiro 			     SrvCertFile, SrvKeyFile,
56409bd497b8SGregory Neil Shapiro 			     CACertPath, CACertFile, DHParams);
56418774250cSGregory Neil Shapiro 	return tls_ok_srv;
564206f25ae9SGregory Neil Shapiro }
564306f25ae9SGregory Neil Shapiro #endif /* STARTTLS */
564440266059SGregory Neil Shapiro /*
564540266059SGregory Neil Shapiro **  SRVFEATURES -- get features for SMTP server
564640266059SGregory Neil Shapiro **
564740266059SGregory Neil Shapiro **	Parameters:
564840266059SGregory Neil Shapiro **		e -- envelope (should be session context).
564940266059SGregory Neil Shapiro **		clientname -- name of client.
565040266059SGregory Neil Shapiro **		features -- default features for this invocation.
565140266059SGregory Neil Shapiro **
565240266059SGregory Neil Shapiro **	Returns:
565340266059SGregory Neil Shapiro **		server features.
565440266059SGregory Neil Shapiro */
565540266059SGregory Neil Shapiro 
565640266059SGregory Neil Shapiro /* table with options: it uses just one character, how about strings? */
565740266059SGregory Neil Shapiro static struct
565840266059SGregory Neil Shapiro {
565940266059SGregory Neil Shapiro 	char		srvf_opt;
5660*d39bd2c1SGregory Neil Shapiro 	unsigned long	srvf_flag;
5661*d39bd2c1SGregory Neil Shapiro 	unsigned long	srvf_flag2;
566240266059SGregory Neil Shapiro } srv_feat_table[] =
566340266059SGregory Neil Shapiro {
5664*d39bd2c1SGregory Neil Shapiro 	{ 'A',	SRV_OFFER_AUTH	, 0	},
5665*d39bd2c1SGregory Neil Shapiro 	{ 'B',	SRV_OFFER_VERB	, 0	},
5666*d39bd2c1SGregory Neil Shapiro 	{ 'C',	SRV_REQ_SEC	, 0	},
5667*d39bd2c1SGregory Neil Shapiro 	{ 'D',	SRV_OFFER_DSN	, 0	},
5668*d39bd2c1SGregory Neil Shapiro 	{ 'E',	SRV_OFFER_ETRN	, 0	},
5669*d39bd2c1SGregory Neil Shapiro 	{ 'F',	SRV_BAD_PIPELINE	, 0	},
5670*d39bd2c1SGregory Neil Shapiro 	{ 'G',	SRV_BARE_LF_421 , SRV_BARE_LF_SP	},
5671*d39bd2c1SGregory Neil Shapiro 	{ 'H',	SRV_NO_HTTP_CMD	, 0	},
56722fb4f839SGregory Neil Shapiro #if USE_EAI
5673*d39bd2c1SGregory Neil Shapiro 	{ 'I',	SRV_OFFER_EAI	, 0	},
56745b0945b5SGregory Neil Shapiro #endif
5675*d39bd2c1SGregory Neil Shapiro /*	{ 'J',	0	, 0	},	*/
5676*d39bd2c1SGregory Neil Shapiro /*	{ 'K',	0	, 0	},	*/
5677*d39bd2c1SGregory Neil Shapiro 	{ 'L',	SRV_REQ_AUTH	, 0	},
5678*d39bd2c1SGregory Neil Shapiro /*	{ 'M',	0	, 0	},	*/
5679*d39bd2c1SGregory Neil Shapiro #if PIPELINING && _FFR_NO_PIPE
5680*d39bd2c1SGregory Neil Shapiro 	{ 'N',	SRV_NO_PIPE	, 0	},
5681*d39bd2c1SGregory Neil Shapiro #endif
5682*d39bd2c1SGregory Neil Shapiro 	{ 'O',	SRV_REQ_CRLF	, 0	},	/* eOl */
568340266059SGregory Neil Shapiro #if PIPELINING
5684*d39bd2c1SGregory Neil Shapiro 	{ 'P',	SRV_OFFER_PIPE	, 0	},
56855b0945b5SGregory Neil Shapiro #endif
5686*d39bd2c1SGregory Neil Shapiro /*	{ 'Q',	0	, 0	},	*/
5687*d39bd2c1SGregory Neil Shapiro 	{ 'R',	SRV_VRFY_CLT	, 0	},	/* same as V; not documented */
5688*d39bd2c1SGregory Neil Shapiro 	{ 'S',	SRV_OFFER_TLS	, 0	},
5689*d39bd2c1SGregory Neil Shapiro /*	{ 'T',	SRV_TMP_FAIL	, 0	},	*/
5690*d39bd2c1SGregory Neil Shapiro 	{ 'U',	SRV_BARE_CR_421 , SRV_BARE_CR_SP	},
5691*d39bd2c1SGregory Neil Shapiro 	{ 'V',	SRV_VRFY_CLT	, 0	},
5692*d39bd2c1SGregory Neil Shapiro /*	{ 'W',	0	, 0	},	*/
5693*d39bd2c1SGregory Neil Shapiro 	{ 'X',	SRV_OFFER_EXPN	, 0	},
5694*d39bd2c1SGregory Neil Shapiro /*	{ 'Y',	SRV_OFFER_VRFY	, 0	},	*/
5695*d39bd2c1SGregory Neil Shapiro /*	{ 'Z',	0	, 0	},	*/
5696*d39bd2c1SGregory Neil Shapiro 	{ '\0',	SRV_NONE	, 0	}
569740266059SGregory Neil Shapiro };
569840266059SGregory Neil Shapiro 
5699*d39bd2c1SGregory Neil Shapiro static unsigned long
srvfeatures(e,clientname,features)570040266059SGregory Neil Shapiro srvfeatures(e, clientname, features)
570140266059SGregory Neil Shapiro 	ENVELOPE *e;
570240266059SGregory Neil Shapiro 	char *clientname;
5703*d39bd2c1SGregory Neil Shapiro 	unsigned long features;
570440266059SGregory Neil Shapiro {
570540266059SGregory Neil Shapiro 	int r, i, j;
570640266059SGregory Neil Shapiro 	char **pvp, c, opt;
570740266059SGregory Neil Shapiro 	char pvpbuf[PSBUFSIZE];
570840266059SGregory Neil Shapiro 
570940266059SGregory Neil Shapiro 	pvp = NULL;
571040266059SGregory Neil Shapiro 	r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
571140266059SGregory Neil Shapiro 		  sizeof(pvpbuf));
571240266059SGregory Neil Shapiro 	if (r != EX_OK)
571340266059SGregory Neil Shapiro 		return features;
571440266059SGregory Neil Shapiro 	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
571540266059SGregory Neil Shapiro 		return features;
571640266059SGregory Neil Shapiro 	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
571740266059SGregory Neil Shapiro 		return SRV_TMP_FAIL;
571840266059SGregory Neil Shapiro 
571940266059SGregory Neil Shapiro 	/*
572040266059SGregory Neil Shapiro 	**  General rule (see sendmail.h, d_flags):
572140266059SGregory Neil Shapiro 	**  lower case: required/offered, upper case: Not required/available
572240266059SGregory Neil Shapiro 	**
572340266059SGregory Neil Shapiro 	**  Since we can change some features per daemon, we have both
572440266059SGregory Neil Shapiro 	**  cases here: turn on/off a feature.
572540266059SGregory Neil Shapiro 	*/
572640266059SGregory Neil Shapiro 
572740266059SGregory Neil Shapiro 	for (i = 1; pvp[i] != NULL; i++)
572840266059SGregory Neil Shapiro 	{
572940266059SGregory Neil Shapiro 		c = pvp[i][0];
573040266059SGregory Neil Shapiro 		j = 0;
573140266059SGregory Neil Shapiro 		for (;;)
573240266059SGregory Neil Shapiro 		{
573340266059SGregory Neil Shapiro 			if ((opt = srv_feat_table[j].srvf_opt) == '\0')
573440266059SGregory Neil Shapiro 			{
573540266059SGregory Neil Shapiro 				if (LogLevel > 9)
573640266059SGregory Neil Shapiro 					sm_syslog(LOG_WARNING, e->e_id,
5737*d39bd2c1SGregory Neil Shapiro 						  "srv_features: unknown feature %s",
573840266059SGregory Neil Shapiro 						  pvp[i]);
573940266059SGregory Neil Shapiro 				break;
574040266059SGregory Neil Shapiro 			}
574140266059SGregory Neil Shapiro 			if (c == opt)
574240266059SGregory Neil Shapiro 			{
574340266059SGregory Neil Shapiro 				features &= ~(srv_feat_table[j].srvf_flag);
574440266059SGregory Neil Shapiro 				break;
574540266059SGregory Neil Shapiro 			}
5746*d39bd2c1SGregory Neil Shapiro 
5747*d39bd2c1SGregory Neil Shapiro 			/*
5748*d39bd2c1SGregory Neil Shapiro 			**  Note: the "noflag" code below works ONLY for
5749*d39bd2c1SGregory Neil Shapiro 			**  the current situation:
5750*d39bd2c1SGregory Neil Shapiro 			**  - _flag itself is set by default
5751*d39bd2c1SGregory Neil Shapiro 			**    (drop session if bare CR or LF is found)
5752*d39bd2c1SGregory Neil Shapiro 			**  - _flag2 is only "effective" if _flag is not set,
5753*d39bd2c1SGregory Neil Shapiro 			**    hence using it turns off _flag.
5754*d39bd2c1SGregory Neil Shapiro 			**  If that situation changes, the code must be changed!
5755*d39bd2c1SGregory Neil Shapiro 			*/
5756*d39bd2c1SGregory Neil Shapiro 
575740266059SGregory Neil Shapiro 			if (c == tolower(opt))
575840266059SGregory Neil Shapiro 			{
5759*d39bd2c1SGregory Neil Shapiro 				unsigned long flag, noflag;
5760*d39bd2c1SGregory Neil Shapiro 
5761*d39bd2c1SGregory Neil Shapiro 				c = pvp[i][1];
5762*d39bd2c1SGregory Neil Shapiro 				flag = noflag = 0;
5763*d39bd2c1SGregory Neil Shapiro 				if ('2' == c)
5764*d39bd2c1SGregory Neil Shapiro 				{
5765*d39bd2c1SGregory Neil Shapiro 					flag = srv_feat_table[j].srvf_flag2;
5766*d39bd2c1SGregory Neil Shapiro 					noflag = srv_feat_table[j].srvf_flag;
5767*d39bd2c1SGregory Neil Shapiro 				}
5768*d39bd2c1SGregory Neil Shapiro 				else if ('\0' == c)
5769*d39bd2c1SGregory Neil Shapiro 					flag = srv_feat_table[j].srvf_flag;
5770*d39bd2c1SGregory Neil Shapiro 				if (0 != flag)
5771*d39bd2c1SGregory Neil Shapiro 				{
5772*d39bd2c1SGregory Neil Shapiro 					features |= flag;
5773*d39bd2c1SGregory Neil Shapiro 					if (0 != noflag)
5774*d39bd2c1SGregory Neil Shapiro 						features &= ~noflag;
5775*d39bd2c1SGregory Neil Shapiro 				}
5776*d39bd2c1SGregory Neil Shapiro 				else if (LogLevel > 9)
5777*d39bd2c1SGregory Neil Shapiro 					sm_syslog(LOG_WARNING, e->e_id,
5778*d39bd2c1SGregory Neil Shapiro 						  "srv_features: unknown variant %s",
5779*d39bd2c1SGregory Neil Shapiro 						  pvp[i]);
578040266059SGregory Neil Shapiro 				break;
578140266059SGregory Neil Shapiro 			}
578240266059SGregory Neil Shapiro 			++j;
578340266059SGregory Neil Shapiro 		}
578440266059SGregory Neil Shapiro 	}
578540266059SGregory Neil Shapiro 	return features;
578640266059SGregory Neil Shapiro }
578740266059SGregory Neil Shapiro 
578840266059SGregory Neil Shapiro /*
5789c2aa98e2SPeter Wemm **  HELP -- implement the HELP command.
5790c2aa98e2SPeter Wemm **
5791c2aa98e2SPeter Wemm **	Parameters:
5792c2aa98e2SPeter Wemm **		topic -- the topic we want help for.
579340266059SGregory Neil Shapiro **		e -- envelope.
5794c2aa98e2SPeter Wemm **
5795c2aa98e2SPeter Wemm **	Returns:
5796c2aa98e2SPeter Wemm **		none.
5797c2aa98e2SPeter Wemm **
5798c2aa98e2SPeter Wemm **	Side Effects:
5799c2aa98e2SPeter Wemm **		outputs the help file to message output.
5800c2aa98e2SPeter Wemm */
580106f25ae9SGregory Neil Shapiro #define HELPVSTR	"#vers	"
580206f25ae9SGregory Neil Shapiro #define HELPVERSION	2
5803c2aa98e2SPeter Wemm 
5804c2aa98e2SPeter Wemm void
help(topic,e)580506f25ae9SGregory Neil Shapiro help(topic, e)
5806c2aa98e2SPeter Wemm 	char *topic;
580706f25ae9SGregory Neil Shapiro 	ENVELOPE *e;
5808c2aa98e2SPeter Wemm {
580940266059SGregory Neil Shapiro 	register SM_FILE_T *hf;
581006f25ae9SGregory Neil Shapiro 	register char *p;
58112fb4f839SGregory Neil Shapiro 	char *lstr;
5812c2aa98e2SPeter Wemm 	int len;
5813c2aa98e2SPeter Wemm 	bool noinfo;
581440266059SGregory Neil Shapiro 	bool first = true;
581506f25ae9SGregory Neil Shapiro 	long sff = SFF_OPENASROOT|SFF_REGONLY;
5816c2aa98e2SPeter Wemm 	char buf[MAXLINE];
581706f25ae9SGregory Neil Shapiro 	char inp[MAXLINE];
581806f25ae9SGregory Neil Shapiro 	static int foundvers = -1;
5819c2aa98e2SPeter Wemm 	extern char Version[];
5820c2aa98e2SPeter Wemm 
5821c2aa98e2SPeter Wemm 	if (DontLockReadFiles)
5822c2aa98e2SPeter Wemm 		sff |= SFF_NOLOCK;
582306f25ae9SGregory Neil Shapiro 	if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
5824c2aa98e2SPeter Wemm 		sff |= SFF_SAFEDIRPATH;
5825c2aa98e2SPeter Wemm 
5826c2aa98e2SPeter Wemm 	if (HelpFile == NULL ||
5827c2aa98e2SPeter Wemm 	    (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
5828c2aa98e2SPeter Wemm 	{
5829c2aa98e2SPeter Wemm 		/* no help */
5830c2aa98e2SPeter Wemm 		errno = 0;
583106f25ae9SGregory Neil Shapiro 		message("502 5.3.0 Sendmail %s -- HELP not implemented",
583206f25ae9SGregory Neil Shapiro 			Version);
5833c2aa98e2SPeter Wemm 		return;
5834c2aa98e2SPeter Wemm 	}
5835c2aa98e2SPeter Wemm 
58362fb4f839SGregory Neil Shapiro 	lstr = NULL;
58372fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(topic))
5838c2aa98e2SPeter Wemm 	{
5839c2aa98e2SPeter Wemm 		topic = "smtp";
584040266059SGregory Neil Shapiro 		noinfo = false;
5841c2aa98e2SPeter Wemm 	}
5842c2aa98e2SPeter Wemm 	else
5843c2aa98e2SPeter Wemm 	{
58442fb4f839SGregory Neil Shapiro 
58452fb4f839SGregory Neil Shapiro 		lstr = makelower_a(&topic, NULL);
58462fb4f839SGregory Neil Shapiro 		if (lstr != topic)
58472fb4f839SGregory Neil Shapiro 			topic = lstr;
58482fb4f839SGregory Neil Shapiro 		else
58492fb4f839SGregory Neil Shapiro 			lstr = NULL;
585040266059SGregory Neil Shapiro 		noinfo = true;
5851c2aa98e2SPeter Wemm 	}
5852c2aa98e2SPeter Wemm 
5853c2aa98e2SPeter Wemm 	len = strlen(topic);
5854c2aa98e2SPeter Wemm 
5855552d4955SGregory Neil Shapiro 	while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
5856c2aa98e2SPeter Wemm 	{
585706f25ae9SGregory Neil Shapiro 		if (buf[0] == '#')
585806f25ae9SGregory Neil Shapiro 		{
585906f25ae9SGregory Neil Shapiro 			if (foundvers < 0 &&
586006f25ae9SGregory Neil Shapiro 			    strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
586106f25ae9SGregory Neil Shapiro 			{
586206f25ae9SGregory Neil Shapiro 				int h;
586306f25ae9SGregory Neil Shapiro 
586440266059SGregory Neil Shapiro 				if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
586506f25ae9SGregory Neil Shapiro 						 &h) == 1)
586606f25ae9SGregory Neil Shapiro 					foundvers = h;
586706f25ae9SGregory Neil Shapiro 			}
586806f25ae9SGregory Neil Shapiro 			continue;
586906f25ae9SGregory Neil Shapiro 		}
5870c2aa98e2SPeter Wemm 		if (strncmp(buf, topic, len) == 0)
5871c2aa98e2SPeter Wemm 		{
587206f25ae9SGregory Neil Shapiro 			if (first)
587306f25ae9SGregory Neil Shapiro 			{
587440266059SGregory Neil Shapiro 				first = false;
5875c2aa98e2SPeter Wemm 
587606f25ae9SGregory Neil Shapiro 				/* print version if no/old vers# in file */
587706f25ae9SGregory Neil Shapiro 				if (foundvers < 2 && !noinfo)
587806f25ae9SGregory Neil Shapiro 					message("214-2.0.0 This is Sendmail version %s", Version);
587906f25ae9SGregory Neil Shapiro 			}
588006f25ae9SGregory Neil Shapiro 			p = strpbrk(buf, " \t");
5881c2aa98e2SPeter Wemm 			if (p == NULL)
588206f25ae9SGregory Neil Shapiro 				p = buf + strlen(buf) - 1;
5883c2aa98e2SPeter Wemm 			else
5884c2aa98e2SPeter Wemm 				p++;
588540266059SGregory Neil Shapiro 			fixcrlf(p, true);
588606f25ae9SGregory Neil Shapiro 			if (foundvers >= 2)
588706f25ae9SGregory Neil Shapiro 			{
5888d0cef73dSGregory Neil Shapiro 				char *lbp;
5889d0cef73dSGregory Neil Shapiro 				int lbs = sizeof(buf) - (p - buf);
5890d0cef73dSGregory Neil Shapiro 
5891d0cef73dSGregory Neil Shapiro 				lbp = translate_dollars(p, p, &lbs);
5892d0cef73dSGregory Neil Shapiro 				expand(lbp, inp, sizeof(inp), e);
5893d0cef73dSGregory Neil Shapiro 				if (p != lbp)
5894d0cef73dSGregory Neil Shapiro 					sm_free(lbp);
589506f25ae9SGregory Neil Shapiro 				p = inp;
589606f25ae9SGregory Neil Shapiro 			}
589706f25ae9SGregory Neil Shapiro 			message("214-2.0.0 %s", p);
589840266059SGregory Neil Shapiro 			noinfo = false;
5899c2aa98e2SPeter Wemm 		}
5900c2aa98e2SPeter Wemm 	}
5901c2aa98e2SPeter Wemm 
5902c2aa98e2SPeter Wemm 	if (noinfo)
590306f25ae9SGregory Neil Shapiro 		message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
5904c2aa98e2SPeter Wemm 	else
590506f25ae9SGregory Neil Shapiro 		message("214 2.0.0 End of HELP info");
590606f25ae9SGregory Neil Shapiro 
590706f25ae9SGregory Neil Shapiro 	if (foundvers != 0 && foundvers < HELPVERSION)
590806f25ae9SGregory Neil Shapiro 	{
590906f25ae9SGregory Neil Shapiro 		if (LogLevel > 1)
591006f25ae9SGregory Neil Shapiro 			sm_syslog(LOG_WARNING, e->e_id,
591106f25ae9SGregory Neil Shapiro 				  "%s too old (require version %d)",
591206f25ae9SGregory Neil Shapiro 				  HelpFile, HELPVERSION);
591306f25ae9SGregory Neil Shapiro 
591406f25ae9SGregory Neil Shapiro 		/* avoid log next time */
591506f25ae9SGregory Neil Shapiro 		foundvers = 0;
591606f25ae9SGregory Neil Shapiro 	}
591706f25ae9SGregory Neil Shapiro 
591840266059SGregory Neil Shapiro 	(void) sm_io_close(hf, SM_TIME_DEFAULT);
59192fb4f839SGregory Neil Shapiro 	SM_FREE(lstr);
5920c2aa98e2SPeter Wemm }
5921a7ec597cSGregory Neil Shapiro 
5922a7ec597cSGregory Neil Shapiro #if SASL
5923a7ec597cSGregory Neil Shapiro /*
5924a7ec597cSGregory Neil Shapiro **  RESET_SASLCONN -- reset SASL connection data
5925a7ec597cSGregory Neil Shapiro **
5926a7ec597cSGregory Neil Shapiro **	Parameters:
5927a7ec597cSGregory Neil Shapiro **		conn -- SASL connection context
5928a7ec597cSGregory Neil Shapiro **		hostname -- host name
5929a7ec597cSGregory Neil Shapiro **		various connection data
5930a7ec597cSGregory Neil Shapiro **
5931a7ec597cSGregory Neil Shapiro **	Returns:
5932a7ec597cSGregory Neil Shapiro **		SASL result
5933a7ec597cSGregory Neil Shapiro */
5934a7ec597cSGregory Neil Shapiro 
59352fb4f839SGregory Neil Shapiro #ifdef __STDC__
5936a7ec597cSGregory Neil Shapiro static int
reset_saslconn(sasl_conn_t ** conn,char * hostname,char * remoteip,char * localip,char * auth_id,sasl_ssf_t * ext_ssf)5937a7ec597cSGregory Neil Shapiro reset_saslconn(sasl_conn_t **conn, char *hostname,
5938a7ec597cSGregory Neil Shapiro # if SASL >= 20000
5939a7ec597cSGregory Neil Shapiro 	       char *remoteip, char *localip,
5940a7ec597cSGregory Neil Shapiro 	       char *auth_id, sasl_ssf_t * ext_ssf)
5941a7ec597cSGregory Neil Shapiro # else /* SASL >= 20000 */
5942a7ec597cSGregory Neil Shapiro 	       struct sockaddr_in *saddr_r, struct sockaddr_in *saddr_l,
5943a7ec597cSGregory Neil Shapiro 	       sasl_external_properties_t * ext_ssf)
5944a7ec597cSGregory Neil Shapiro # endif /* SASL >= 20000 */
59452fb4f839SGregory Neil Shapiro #else /* __STDC__ */
5946*d39bd2c1SGregory Neil Shapiro # error "SASL requires __STDC__"
59472fb4f839SGregory Neil Shapiro #endif /* __STDC__ */
5948a7ec597cSGregory Neil Shapiro {
5949a7ec597cSGregory Neil Shapiro 	int result;
5950a7ec597cSGregory Neil Shapiro 
5951a7ec597cSGregory Neil Shapiro 	sasl_dispose(conn);
5952a7ec597cSGregory Neil Shapiro # if SASL >= 20000
5953a7ec597cSGregory Neil Shapiro 	result = sasl_server_new("smtp", hostname, NULL, NULL, NULL,
5954a7ec597cSGregory Neil Shapiro 				 NULL, 0, conn);
5955a7ec597cSGregory Neil Shapiro # elif SASL > 10505
5956a7ec597cSGregory Neil Shapiro 	/* use empty realm: only works in SASL > 1.5.5 */
5957a7ec597cSGregory Neil Shapiro 	result = sasl_server_new("smtp", hostname, "", NULL, 0, conn);
5958a7ec597cSGregory Neil Shapiro # else /* SASL >= 20000 */
5959a7ec597cSGregory Neil Shapiro 	/* use no realm -> realm is set to hostname by SASL lib */
5960a7ec597cSGregory Neil Shapiro 	result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
5961a7ec597cSGregory Neil Shapiro 				 conn);
5962a7ec597cSGregory Neil Shapiro # endif /* SASL >= 20000 */
5963a7ec597cSGregory Neil Shapiro 	if (result != SASL_OK)
5964a7ec597cSGregory Neil Shapiro 		return result;
5965a7ec597cSGregory Neil Shapiro 
5966a7ec597cSGregory Neil Shapiro # if SASL >= 20000
5967a7ec597cSGregory Neil Shapiro #  if NETINET || NETINET6
596813d88268SGregory Neil Shapiro 	if (remoteip != NULL && *remoteip != '\0')
5969a7ec597cSGregory Neil Shapiro 		result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip);
5970a7ec597cSGregory Neil Shapiro 	if (result != SASL_OK)
5971a7ec597cSGregory Neil Shapiro 		return result;
5972a7ec597cSGregory Neil Shapiro 
597313d88268SGregory Neil Shapiro 	if (localip != NULL && *localip != '\0')
5974a7ec597cSGregory Neil Shapiro 		result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip);
5975a7ec597cSGregory Neil Shapiro 	if (result != SASL_OK)
5976a7ec597cSGregory Neil Shapiro 		return result;
5977a7ec597cSGregory Neil Shapiro #  endif /* NETINET || NETINET6 */
5978a7ec597cSGregory Neil Shapiro 
5979a7ec597cSGregory Neil Shapiro 	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
5980a7ec597cSGregory Neil Shapiro 	if (result != SASL_OK)
5981a7ec597cSGregory Neil Shapiro 		return result;
5982a7ec597cSGregory Neil Shapiro 
5983a7ec597cSGregory Neil Shapiro 	result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id);
5984a7ec597cSGregory Neil Shapiro 	if (result != SASL_OK)
5985a7ec597cSGregory Neil Shapiro 		return result;
5986a7ec597cSGregory Neil Shapiro # else /* SASL >= 20000 */
5987a7ec597cSGregory Neil Shapiro #  if NETINET
5988a7ec597cSGregory Neil Shapiro 	if (saddr_r != NULL)
5989a7ec597cSGregory Neil Shapiro 		result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r);
5990a7ec597cSGregory Neil Shapiro 	if (result != SASL_OK)
5991a7ec597cSGregory Neil Shapiro 		return result;
5992a7ec597cSGregory Neil Shapiro 
5993a7ec597cSGregory Neil Shapiro 	if (saddr_l != NULL)
5994a7ec597cSGregory Neil Shapiro 		result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l);
5995a7ec597cSGregory Neil Shapiro 	if (result != SASL_OK)
5996a7ec597cSGregory Neil Shapiro 		return result;
5997a7ec597cSGregory Neil Shapiro #  endif /* NETINET */
5998a7ec597cSGregory Neil Shapiro 
5999a7ec597cSGregory Neil Shapiro 	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
6000a7ec597cSGregory Neil Shapiro 	if (result != SASL_OK)
6001a7ec597cSGregory Neil Shapiro 		return result;
6002a7ec597cSGregory Neil Shapiro # endif /* SASL >= 20000 */
6003a7ec597cSGregory Neil Shapiro 	return SASL_OK;
6004a7ec597cSGregory Neil Shapiro }
60055b0945b5SGregory Neil Shapiro 
60065b0945b5SGregory Neil Shapiro /*
60075b0945b5SGregory Neil Shapiro **  GET_SASL_USER -- extract user part from SASL reply
60085b0945b5SGregory Neil Shapiro **
60095b0945b5SGregory Neil Shapiro **	Parameters:
60105b0945b5SGregory Neil Shapiro **		val -- sasl reply (may contain NUL)
60115b0945b5SGregory Neil Shapiro **		len -- length of val
60125b0945b5SGregory Neil Shapiro **		auth_type -- auth_type (can be NULL)
60135b0945b5SGregory Neil Shapiro **		user -- output buffer for extract user
60145b0945b5SGregory Neil Shapiro **		user_len -- length of output buffer (user)
60155b0945b5SGregory Neil Shapiro **
60165b0945b5SGregory Neil Shapiro **	Returns:
60175b0945b5SGregory Neil Shapiro **		none.
60185b0945b5SGregory Neil Shapiro **
60195b0945b5SGregory Neil Shapiro **	Note: val is supplied by the client and hence may contain "bad"
60205b0945b5SGregory Neil Shapiro **		(non-printable) characters, but the returned value (user)
60215b0945b5SGregory Neil Shapiro **		is only used for logging which converts those characters.
60225b0945b5SGregory Neil Shapiro */
60235b0945b5SGregory Neil Shapiro 
60245b0945b5SGregory Neil Shapiro static void
get_sasl_user(val,len,auth_type,user,user_len)60255b0945b5SGregory Neil Shapiro get_sasl_user(val, len, auth_type, user, user_len)
60265b0945b5SGregory Neil Shapiro 	char *val;
60275b0945b5SGregory Neil Shapiro 	unsigned int len;
60285b0945b5SGregory Neil Shapiro 	const char *auth_type;
60295b0945b5SGregory Neil Shapiro 	char *user;
60305b0945b5SGregory Neil Shapiro 	size_t user_len;
60315b0945b5SGregory Neil Shapiro {
60325b0945b5SGregory Neil Shapiro 	unsigned int u;
60335b0945b5SGregory Neil Shapiro 
60345b0945b5SGregory Neil Shapiro 	SM_ASSERT(val != NULL);
60355b0945b5SGregory Neil Shapiro 	SM_ASSERT(user != NULL);
60365b0945b5SGregory Neil Shapiro 	SM_ASSERT(user_len > 0);
60375b0945b5SGregory Neil Shapiro 
60385b0945b5SGregory Neil Shapiro 	*user = '\0';
60392fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(auth_type))
60405b0945b5SGregory Neil Shapiro 		return;
60415b0945b5SGregory Neil Shapiro 	if (0 == len)
60425b0945b5SGregory Neil Shapiro 		return;
60435b0945b5SGregory Neil Shapiro 
60445b0945b5SGregory Neil Shapiro # define DIGMD5U	"username=\""
60455b0945b5SGregory Neil Shapiro # define DIGMD5U_L	(sizeof(DIGMD5U) - 1)
60462fb4f839SGregory Neil Shapiro 	if (SM_STRCASEEQ(auth_type, "digest-md5") &&
60475b0945b5SGregory Neil Shapiro 	    strncmp(val, DIGMD5U, DIGMD5U_L) == 0)
60485b0945b5SGregory Neil Shapiro 	{
60495b0945b5SGregory Neil Shapiro 		char *s;
60505b0945b5SGregory Neil Shapiro 
60515b0945b5SGregory Neil Shapiro 		val += DIGMD5U_L;
60525b0945b5SGregory Neil Shapiro 		if (len <= DIGMD5U_L)
60535b0945b5SGregory Neil Shapiro 			return;
60545b0945b5SGregory Neil Shapiro 		len -= DIGMD5U_L;
60555b0945b5SGregory Neil Shapiro 
60565b0945b5SGregory Neil Shapiro 		/* format? could there be a quoted '"'? */
60575b0945b5SGregory Neil Shapiro 		for (s = val, u = 0; *s != '\0' && u < len; s++)
60585b0945b5SGregory Neil Shapiro 		{
60595b0945b5SGregory Neil Shapiro 			if ('"' == *s)
60605b0945b5SGregory Neil Shapiro 			{
60615b0945b5SGregory Neil Shapiro 				*s = '\0';
60625b0945b5SGregory Neil Shapiro 				break;
60635b0945b5SGregory Neil Shapiro 			}
60645b0945b5SGregory Neil Shapiro 			if ('\\' == *s)
60655b0945b5SGregory Neil Shapiro 			{
60665b0945b5SGregory Neil Shapiro 				++s;
60675b0945b5SGregory Neil Shapiro 				if ('\0' == *s)
60685b0945b5SGregory Neil Shapiro 					break;
60695b0945b5SGregory Neil Shapiro 			}
60705b0945b5SGregory Neil Shapiro 		}
60715b0945b5SGregory Neil Shapiro 	}
60722fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(auth_type, "cram-md5"))
60735b0945b5SGregory Neil Shapiro 	{
60745b0945b5SGregory Neil Shapiro 		char *s;
60755b0945b5SGregory Neil Shapiro 
60765b0945b5SGregory Neil Shapiro 		for (s = val, u = 0; *s != '\0' && u < len; s++)
60775b0945b5SGregory Neil Shapiro 		{
60785b0945b5SGregory Neil Shapiro 			if (' ' == *s)
60795b0945b5SGregory Neil Shapiro 			{
60805b0945b5SGregory Neil Shapiro 				*s = '\0';
60815b0945b5SGregory Neil Shapiro 				break;
60825b0945b5SGregory Neil Shapiro 			}
60835b0945b5SGregory Neil Shapiro 		}
60845b0945b5SGregory Neil Shapiro 	}
60855b0945b5SGregory Neil Shapiro 
60862fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(auth_type, "plain") ||
60872fb4f839SGregory Neil Shapiro 		 SM_STRCASEEQ(auth_type, "login"))
60885b0945b5SGregory Neil Shapiro 	{
60895b0945b5SGregory Neil Shapiro 		/*
60905b0945b5SGregory Neil Shapiro 		**  RFC 4616: The PLAIN Simple Authentication and
60915b0945b5SGregory Neil Shapiro 		**	Security Layer (SASL) Mechanism
60925b0945b5SGregory Neil Shapiro 		**    message   = [authzid] UTF8NUL authcid UTF8NUL passwd
60935b0945b5SGregory Neil Shapiro 		**  each part: 1*SAFE ; MUST accept up to 255 octets
60945b0945b5SGregory Neil Shapiro 		**  UTF8NUL   = %x00 ; UTF-8 encoded NUL character
60955b0945b5SGregory Neil Shapiro 		**
60965b0945b5SGregory Neil Shapiro 		**  draft-murchison-sasl-login: it's just username by its own
60975b0945b5SGregory Neil Shapiro 		*/
60985b0945b5SGregory Neil Shapiro 
60995b0945b5SGregory Neil Shapiro 		for (u = 0; u < len; u++)
61005b0945b5SGregory Neil Shapiro 		{
61015b0945b5SGregory Neil Shapiro 			if (val[u] == '\0')
61025b0945b5SGregory Neil Shapiro 			{
61035b0945b5SGregory Neil Shapiro 				val[u] = '/';
61045b0945b5SGregory Neil Shapiro 				(void) sm_strlcpy(user,
61055b0945b5SGregory Neil Shapiro 						val + ((0 == u) ? 1 : 0),
61065b0945b5SGregory Neil Shapiro 						user_len);
61075b0945b5SGregory Neil Shapiro 				return;
61085b0945b5SGregory Neil Shapiro 			}
61095b0945b5SGregory Neil Shapiro 		}
61105b0945b5SGregory Neil Shapiro 	}
61115b0945b5SGregory Neil Shapiro 	else
61125b0945b5SGregory Neil Shapiro 	{
61135b0945b5SGregory Neil Shapiro 		/*
61145b0945b5SGregory Neil Shapiro 		**  Extracting the "user" from other mechanisms
61155b0945b5SGregory Neil Shapiro 		**  is currently not supported.
61165b0945b5SGregory Neil Shapiro 		*/
61175b0945b5SGregory Neil Shapiro 
61185b0945b5SGregory Neil Shapiro 		return;
61195b0945b5SGregory Neil Shapiro 	}
61205b0945b5SGregory Neil Shapiro 
61215b0945b5SGregory Neil Shapiro 	/*
61225b0945b5SGregory Neil Shapiro 	**  Does the input buffer has an NUL in it so it can be treated
61235b0945b5SGregory Neil Shapiro 	**  as a C string?
61245b0945b5SGregory Neil Shapiro 	*/
61255b0945b5SGregory Neil Shapiro 
61265b0945b5SGregory Neil Shapiro 	/* SM_ASSERT(len > 0); see above */
61275b0945b5SGregory Neil Shapiro 	u = len - 1;
61285b0945b5SGregory Neil Shapiro 	if (val[u] != '\0')
61295b0945b5SGregory Neil Shapiro 	{
61305b0945b5SGregory Neil Shapiro 		for (u = 0; u < len; u++)
61315b0945b5SGregory Neil Shapiro 		{
61325b0945b5SGregory Neil Shapiro 			if (val[u] == '\0')
61335b0945b5SGregory Neil Shapiro 				break;
61345b0945b5SGregory Neil Shapiro 		}
61355b0945b5SGregory Neil Shapiro 	}
61365b0945b5SGregory Neil Shapiro 	if (val[u] != '\0')
61375b0945b5SGregory Neil Shapiro 		user_len = SM_MIN(len, user_len);
61385b0945b5SGregory Neil Shapiro 
61395b0945b5SGregory Neil Shapiro 	(void) sm_strlcpy(user, val, user_len);
61405b0945b5SGregory Neil Shapiro }
6141a7ec597cSGregory Neil Shapiro #endif /* SASL */
6142