138032Speter/*
2261370Sgshapiro * Copyright (c) 1998-2010, 2012, 2013 Proofpoint, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
1590792Sgshapiro#if MILTER
16132943Sgshapiro# include <libmilter/mfapi.h>
1790792Sgshapiro# include <libmilter/mfdef.h>
1890792Sgshapiro#endif /* MILTER */
1964562Sgshapiro
20266711SgshapiroSM_RCSID("@(#)$Id: srvrsmtp.c,v 8.1016 2013-11-22 20:51:56 ca Exp $")
2164562Sgshapiro
22157001Sgshapiro#include <sm/time.h>
23132943Sgshapiro#include <sm/fdset.h>
24132943Sgshapiro
2590792Sgshapiro#if SASL || STARTTLS
2690792Sgshapiro# include "sfsasl.h"
2790792Sgshapiro#endif /* SASL || STARTTLS */
2890792Sgshapiro#if SASL
2990792Sgshapiro# define ENC64LEN(l)	(((l) + 2) * 4 / 3 + 1)
3064562Sgshapirostatic int saslmechs __P((sasl_conn_t *, char **));
3190792Sgshapiro#endif /* SASL */
3290792Sgshapiro#if STARTTLS
33244928Sgshapiro#  include <openssl/err.h>
3490792Sgshapiro# include <sysexits.h>
3538032Speter
3690792Sgshapirostatic SSL_CTX	*srv_ctx = NULL;	/* TLS server context */
3790792Sgshapirostatic SSL	*srv_ssl = NULL;	/* per connection context */
3838032Speter
3990792Sgshapirostatic bool	tls_ok_srv = false;
4090792Sgshapiro
4190792Sgshapiro# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
4290792Sgshapiro				bitset(SRV_VRFY_CLT, features))
4390792Sgshapiro#endif /* STARTTLS */
4490792Sgshapiro
45168515Sgshapiro#if _FFR_DM_ONE
46168515Sgshapirostatic bool	NotFirstDelivery = false;
47168515Sgshapiro#endif /* _FFR_DM_ONE */
48168515Sgshapiro
4990792Sgshapiro/* server features */
5090792Sgshapiro#define SRV_NONE	0x0000	/* none... */
5190792Sgshapiro#define SRV_OFFER_TLS	0x0001	/* offer STARTTLS */
5290792Sgshapiro#define SRV_VRFY_CLT	0x0002	/* request a cert */
5390792Sgshapiro#define SRV_OFFER_AUTH	0x0004	/* offer AUTH */
5490792Sgshapiro#define SRV_OFFER_ETRN	0x0008	/* offer ETRN */
5590792Sgshapiro#define SRV_OFFER_VRFY	0x0010	/* offer VRFY (not yet used) */
5690792Sgshapiro#define SRV_OFFER_EXPN	0x0020	/* offer EXPN */
5790792Sgshapiro#define SRV_OFFER_VERB	0x0040	/* offer VERB */
5890792Sgshapiro#define SRV_OFFER_DSN	0x0080	/* offer DSN */
5990792Sgshapiro#if PIPELINING
6090792Sgshapiro# define SRV_OFFER_PIPE	0x0100	/* offer PIPELINING */
6190792Sgshapiro# if _FFR_NO_PIPE
6290792Sgshapiro#  define SRV_NO_PIPE	0x0200	/* disable PIPELINING, sleep if used */
6390792Sgshapiro# endif /* _FFR_NO_PIPE */
6490792Sgshapiro#endif /* PIPELINING */
6590792Sgshapiro#define SRV_REQ_AUTH	0x0400	/* require AUTH */
66132943Sgshapiro#define SRV_REQ_SEC	0x0800	/* require security - equiv to AuthOptions=p */
6790792Sgshapiro#define SRV_TMP_FAIL	0x1000	/* ruleset caused a temporary failure */
6890792Sgshapiro
6990792Sgshapirostatic unsigned int	srvfeatures __P((ENVELOPE *, char *, unsigned int));
7090792Sgshapiro
71132943Sgshapiro#define	STOP_ATTACK	((time_t) -1)
72132943Sgshapirostatic time_t	checksmtpattack __P((volatile unsigned int *, unsigned int,
73132943Sgshapiro				     bool, char *, ENVELOPE *));
7464562Sgshapirostatic void	printvrfyaddr __P((ADDRESS *, bool, bool));
7564562Sgshapirostatic char	*skipword __P((char *volatile, char *));
7690792Sgshapirostatic void	setup_smtpd_io __P((void));
77120256Sgshapiro
78120256Sgshapiro#if SASL
79120256Sgshapiro# if SASL >= 20000
80120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
81120256Sgshapiro				char *_remoteip, char *_localip,
82120256Sgshapiro				char *_auth_id, sasl_ssf_t *_ext_ssf));
83120256Sgshapiro
84120256Sgshapiro# define RESET_SASLCONN	\
85147078Sgshapiro	do							\
86147078Sgshapiro	{							\
87147078Sgshapiro		result = reset_saslconn(&conn, AuthRealm, remoteip, \
88147078Sgshapiro					localip, auth_id, &ext_ssf); \
89147078Sgshapiro		if (result != SASL_OK)				\
90147078Sgshapiro			sasl_ok = false;			\
91147078Sgshapiro	} while (0)
92120256Sgshapiro
93120256Sgshapiro# else /* SASL >= 20000 */
94120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
95120256Sgshapiro				struct sockaddr_in *_saddr_r,
96120256Sgshapiro				struct sockaddr_in *_saddr_l,
97120256Sgshapiro				sasl_external_properties_t *_ext_ssf));
98120256Sgshapiro# define RESET_SASLCONN	\
99147078Sgshapiro	do							\
100147078Sgshapiro	{							\
101147078Sgshapiro		result = reset_saslconn(&conn, AuthRealm, &saddr_r, \
102147078Sgshapiro					&saddr_l, &ext_ssf);	\
103147078Sgshapiro		if (result != SASL_OK)				\
104147078Sgshapiro			sasl_ok = false;			\
105147078Sgshapiro	} while (0)
106120256Sgshapiro
107120256Sgshapiro# endif /* SASL >= 20000 */
108120256Sgshapiro#endif /* SASL */
109120256Sgshapiro
11064562Sgshapiroextern ENVELOPE	BlankEnvelope;
11138032Speter
112132943Sgshapiro#define NBADRCPTS						\
113132943Sgshapiro	do							\
114132943Sgshapiro	{							\
115132943Sgshapiro		char buf[16];					\
116168515Sgshapiro		(void) sm_snprintf(buf, sizeof(buf), "%d",	\
117132943Sgshapiro			BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \
118132943Sgshapiro				? n_badrcpts - 1 : n_badrcpts);	\
119132943Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
120132943Sgshapiro	} while (0)
121132943Sgshapiro
12290792Sgshapiro#define SKIP_SPACE(s)	while (isascii(*s) && isspace(*s))	\
12390792Sgshapiro				(s)++
12490792Sgshapiro
12538032Speter/*
126168515Sgshapiro**  PARSE_ESMTP_ARGS -- parse EMSTP arguments (for MAIL, RCPT)
127168515Sgshapiro**
128168515Sgshapiro**	Parameters:
129168515Sgshapiro**		e -- the envelope
130168515Sgshapiro**		addr_st -- address (RCPT only)
131168515Sgshapiro**		p -- read buffer
132168515Sgshapiro**		delimptr -- current position in read buffer
133168515Sgshapiro**		which -- MAIL/RCPT
134168515Sgshapiro**		args -- arguments (output)
135168515Sgshapiro**		esmtp_args -- function to process a single ESMTP argument
136168515Sgshapiro**
137168515Sgshapiro**	Returns:
138168515Sgshapiro**		none
139168515Sgshapiro*/
140168515Sgshapiro
141168515Sgshapirovoid
142168515Sgshapiroparse_esmtp_args(e, addr_st, p, delimptr, which, args, esmtp_args)
143168515Sgshapiro	ENVELOPE *e;
144168515Sgshapiro	ADDRESS *addr_st;
145168515Sgshapiro	char *p;
146168515Sgshapiro	char *delimptr;
147168515Sgshapiro	char *which;
148168515Sgshapiro	char *args[];
149168515Sgshapiro	esmtp_args_F esmtp_args;
150168515Sgshapiro{
151168515Sgshapiro	int argno;
152168515Sgshapiro
153168515Sgshapiro	argno = 0;
154168515Sgshapiro	if (args != NULL)
155168515Sgshapiro		args[argno++] = p;
156168515Sgshapiro	p = delimptr;
157168515Sgshapiro	while (p != NULL && *p != '\0')
158168515Sgshapiro	{
159168515Sgshapiro		char *kp;
160168515Sgshapiro		char *vp = NULL;
161168515Sgshapiro		char *equal = NULL;
162168515Sgshapiro
163168515Sgshapiro		/* locate the beginning of the keyword */
164168515Sgshapiro		SKIP_SPACE(p);
165168515Sgshapiro		if (*p == '\0')
166168515Sgshapiro			break;
167168515Sgshapiro		kp = p;
168168515Sgshapiro
169168515Sgshapiro		/* skip to the value portion */
170168515Sgshapiro		while ((isascii(*p) && isalnum(*p)) || *p == '-')
171168515Sgshapiro			p++;
172168515Sgshapiro		if (*p == '=')
173168515Sgshapiro		{
174168515Sgshapiro			equal = p;
175168515Sgshapiro			*p++ = '\0';
176168515Sgshapiro			vp = p;
177168515Sgshapiro
178168515Sgshapiro			/* skip to the end of the value */
179168515Sgshapiro			while (*p != '\0' && *p != ' ' &&
180168515Sgshapiro			       !(isascii(*p) && iscntrl(*p)) &&
181168515Sgshapiro			       *p != '=')
182168515Sgshapiro				p++;
183168515Sgshapiro		}
184168515Sgshapiro
185168515Sgshapiro		if (*p != '\0')
186168515Sgshapiro			*p++ = '\0';
187168515Sgshapiro
188168515Sgshapiro		if (tTd(19, 1))
189168515Sgshapiro			sm_dprintf("%s: got arg %s=\"%s\"\n", which, kp,
190168515Sgshapiro				vp == NULL ? "<null>" : vp);
191168515Sgshapiro
192168515Sgshapiro		esmtp_args(addr_st, kp, vp, e);
193168515Sgshapiro		if (equal != NULL)
194168515Sgshapiro			*equal = '=';
195168515Sgshapiro		if (args != NULL)
196168515Sgshapiro			args[argno] = kp;
197168515Sgshapiro		argno++;
198168515Sgshapiro		if (argno >= MAXSMTPARGS - 1)
199168515Sgshapiro			usrerr("501 5.5.4 Too many parameters");
200168515Sgshapiro		if (Errors > 0)
201168515Sgshapiro			break;
202168515Sgshapiro	}
203168515Sgshapiro	if (args != NULL)
204168515Sgshapiro		args[argno] = NULL;
205168515Sgshapiro}
206168515Sgshapiro
207168515Sgshapiro/*
20838032Speter**  SMTP -- run the SMTP protocol.
20938032Speter**
21038032Speter**	Parameters:
21138032Speter**		nullserver -- if non-NULL, rejection message for
21290792Sgshapiro**			(almost) all SMTP commands.
21390792Sgshapiro**		d_flags -- daemon flags
21438032Speter**		e -- the envelope.
21538032Speter**
21638032Speter**	Returns:
21738032Speter**		never.
21838032Speter**
21938032Speter**	Side Effects:
22090792Sgshapiro**		Reads commands from the input channel and processes them.
22138032Speter*/
22238032Speter
22390792Sgshapiro/*
22490792Sgshapiro**  Notice: The smtp server doesn't have a session context like the client
22590792Sgshapiro**	side has (mci). Therefore some data (session oriented) is allocated
22690792Sgshapiro**	or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
22790792Sgshapiro**	This should be fixed in a successor version.
22890792Sgshapiro*/
22990792Sgshapiro
23038032Speterstruct cmd
23138032Speter{
23264562Sgshapiro	char	*cmd_name;	/* command name */
23364562Sgshapiro	int	cmd_code;	/* internal code, see below */
23438032Speter};
23538032Speter
23664562Sgshapiro/* values for cmd_code */
23790792Sgshapiro#define CMDERROR	0	/* bad command */
23890792Sgshapiro#define CMDMAIL	1	/* mail -- designate sender */
23990792Sgshapiro#define CMDRCPT	2	/* rcpt -- designate recipient */
24090792Sgshapiro#define CMDDATA	3	/* data -- send message text */
24190792Sgshapiro#define CMDRSET	4	/* rset -- reset state */
24290792Sgshapiro#define CMDVRFY	5	/* vrfy -- verify address */
24390792Sgshapiro#define CMDEXPN	6	/* expn -- expand address */
24490792Sgshapiro#define CMDNOOP	7	/* noop -- do nothing */
24590792Sgshapiro#define CMDQUIT	8	/* quit -- close connection and die */
24690792Sgshapiro#define CMDHELO	9	/* helo -- be polite */
24790792Sgshapiro#define CMDHELP	10	/* help -- give usage info */
24890792Sgshapiro#define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
24990792Sgshapiro#define CMDETRN	12	/* etrn -- flush queue */
25090792Sgshapiro#if SASL
25190792Sgshapiro# define CMDAUTH	13	/* auth -- SASL authenticate */
25290792Sgshapiro#endif /* SASL */
25390792Sgshapiro#if STARTTLS
25490792Sgshapiro# define CMDSTLS	14	/* STARTTLS -- start TLS session */
25590792Sgshapiro#endif /* STARTTLS */
25638032Speter/* non-standard commands */
25790792Sgshapiro#define CMDVERB	17	/* verb -- go into verbose mode */
25864562Sgshapiro/* unimplemented commands from RFC 821 */
25990792Sgshapiro#define CMDUNIMPL	19	/* unimplemented rfc821 commands */
26038032Speter/* use this to catch and log "door handle" attempts on your system */
26190792Sgshapiro#define CMDLOGBOGUS	23	/* bogus command that should be logged */
26238032Speter/* debugging-only commands, only enabled if SMTPDEBUG is defined */
26390792Sgshapiro#define CMDDBGQSHOW	24	/* showq -- show send queue */
26490792Sgshapiro#define CMDDBGDEBUG	25	/* debug -- set debug mode */
26538032Speter
26666494Sgshapiro/*
26790792Sgshapiro**  Note: If you change this list, remember to update 'helpfile'
26866494Sgshapiro*/
26966494Sgshapiro
27038032Speterstatic struct cmd	CmdTab[] =
27138032Speter{
27238032Speter	{ "mail",	CMDMAIL		},
27338032Speter	{ "rcpt",	CMDRCPT		},
27438032Speter	{ "data",	CMDDATA		},
27538032Speter	{ "rset",	CMDRSET		},
27638032Speter	{ "vrfy",	CMDVRFY		},
27738032Speter	{ "expn",	CMDEXPN		},
27838032Speter	{ "help",	CMDHELP		},
27938032Speter	{ "noop",	CMDNOOP		},
28038032Speter	{ "quit",	CMDQUIT		},
28138032Speter	{ "helo",	CMDHELO		},
28238032Speter	{ "ehlo",	CMDEHLO		},
28338032Speter	{ "etrn",	CMDETRN		},
28438032Speter	{ "verb",	CMDVERB		},
28564562Sgshapiro	{ "send",	CMDUNIMPL	},
28664562Sgshapiro	{ "saml",	CMDUNIMPL	},
28764562Sgshapiro	{ "soml",	CMDUNIMPL	},
28864562Sgshapiro	{ "turn",	CMDUNIMPL	},
28990792Sgshapiro#if SASL
29064562Sgshapiro	{ "auth",	CMDAUTH,	},
29190792Sgshapiro#endif /* SASL */
29290792Sgshapiro#if STARTTLS
29364562Sgshapiro	{ "starttls",	CMDSTLS,	},
29490792Sgshapiro#endif /* STARTTLS */
29538032Speter    /* remaining commands are here only to trap and log attempts to use them */
29638032Speter	{ "showq",	CMDDBGQSHOW	},
29738032Speter	{ "debug",	CMDDBGDEBUG	},
29838032Speter	{ "wiz",	CMDLOGBOGUS	},
29938032Speter
30038032Speter	{ NULL,		CMDERROR	}
30138032Speter};
30238032Speter
30364562Sgshapirostatic char	*CurSmtpClient;		/* who's at the other end of channel */
30438032Speter
30590792Sgshapiro#ifndef MAXBADCOMMANDS
30690792Sgshapiro# define MAXBADCOMMANDS 25	/* maximum number of bad commands */
30794334Sgshapiro#endif /* ! MAXBADCOMMANDS */
30890792Sgshapiro#ifndef MAXHELOCOMMANDS
30990792Sgshapiro# define MAXHELOCOMMANDS 3	/* max HELO/EHLO commands before slowdown */
31094334Sgshapiro#endif /* ! MAXHELOCOMMANDS */
31190792Sgshapiro#ifndef MAXVRFYCOMMANDS
31290792Sgshapiro# define MAXVRFYCOMMANDS 6	/* max VRFY/EXPN commands before slowdown */
31394334Sgshapiro#endif /* ! MAXVRFYCOMMANDS */
31490792Sgshapiro#ifndef MAXETRNCOMMANDS
31590792Sgshapiro# define MAXETRNCOMMANDS 8	/* max ETRN commands before slowdown */
31694334Sgshapiro#endif /* ! MAXETRNCOMMANDS */
31790792Sgshapiro#ifndef MAXTIMEOUT
31890792Sgshapiro# define MAXTIMEOUT (4 * 60)	/* max timeout for bad commands */
31994334Sgshapiro#endif /* ! MAXTIMEOUT */
32038032Speter
321132943Sgshapiro/*
322132943Sgshapiro**  Maximum shift value to compute timeout for bad commands.
323132943Sgshapiro**  This introduces an upper limit of 2^MAXSHIFT for the timeout.
324132943Sgshapiro*/
325132943Sgshapiro
326132943Sgshapiro#ifndef MAXSHIFT
327132943Sgshapiro# define MAXSHIFT 8
328132943Sgshapiro#endif /* ! MAXSHIFT */
329132943Sgshapiro#if MAXSHIFT > 31
330132943Sgshapiro ERROR _MAXSHIFT > 31 is invalid
331132943Sgshapiro#endif /* MAXSHIFT */
332132943Sgshapiro
333132943Sgshapiro
334132943Sgshapiro#if MAXBADCOMMANDS > 0
335132943Sgshapiro# define STOP_IF_ATTACK(r)	do		\
336132943Sgshapiro	{					\
337132943Sgshapiro		if ((r) == STOP_ATTACK)		\
338132943Sgshapiro			goto stopattack;	\
339132943Sgshapiro	} while (0)
340132943Sgshapiro
341132943Sgshapiro#else /* MAXBADCOMMANDS > 0 */
342132943Sgshapiro# define STOP_IF_ATTACK(r)	r
343132943Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
344132943Sgshapiro
345132943Sgshapiro
34690792Sgshapiro#if SM_HEAP_CHECK
34790792Sgshapirostatic SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
34890792Sgshapiro	"@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
34990792Sgshapiro#endif /* SM_HEAP_CHECK */
35038032Speter
35190792Sgshapirotypedef struct
35290792Sgshapiro{
353173340Sgshapiro	bool		sm_gotmail;	/* mail command received */
354173340Sgshapiro	unsigned int	sm_nrcpts;	/* number of successful RCPT commands */
355173340Sgshapiro	bool		sm_discard;
35690792Sgshapiro#if MILTER
357173340Sgshapiro	bool		sm_milterize;
358173340Sgshapiro	bool		sm_milterlist;	/* any filters in the list? */
359173340Sgshapiro	milters_T	sm_milters;
360173340Sgshapiro
361173340Sgshapiro	/* e_nrcpts from envelope before recipient() call */
362173340Sgshapiro	unsigned int	sm_e_nrcpts_orig;
36390792Sgshapiro#endif /* MILTER */
364173340Sgshapiro	char		*sm_quarmsg;	/* carry quarantining across messages */
36590792Sgshapiro} SMTP_T;
36690792Sgshapiro
367132943Sgshapirostatic bool	smtp_data __P((SMTP_T *, ENVELOPE *));
36890792Sgshapiro
369132943Sgshapiro#define MSG_TEMPFAIL "451 4.3.2 Please try again later"
37090792Sgshapiro
37190792Sgshapiro#if MILTER
37290792Sgshapiro# define MILTER_ABORT(e)	milter_abort((e))
373110560Sgshapiro
37490792Sgshapiro# define MILTER_REPLY(str)						\
37590792Sgshapiro	{								\
37690792Sgshapiro		int savelogusrerrs = LogUsrErrs;			\
37790792Sgshapiro									\
378168515Sgshapiro		milter_cmd_fail = true;					\
37990792Sgshapiro		switch (state)						\
38090792Sgshapiro		{							\
381157001Sgshapiro		  case SMFIR_SHUTDOWN:					\
382157001Sgshapiro			if (MilterLogLevel > 3)				\
383157001Sgshapiro			{						\
384157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
385157001Sgshapiro					  "Milter: %s=%s, reject=421, errormode=4",	\
386157001Sgshapiro					  str, addr);			\
387157001Sgshapiro				LogUsrErrs = false;			\
388157001Sgshapiro			}						\
389157001Sgshapiro			{						\
390157001Sgshapiro				bool tsave = QuickAbort;		\
391157001Sgshapiro									\
392157001Sgshapiro				QuickAbort = false;			\
393157001Sgshapiro				usrerr("421 4.3.0 closing connection");	\
394157001Sgshapiro				QuickAbort = tsave;			\
395157001Sgshapiro				e->e_sendqueue = NULL;			\
396157001Sgshapiro				goto doquit;				\
397157001Sgshapiro			}						\
398157001Sgshapiro			break;						\
39990792Sgshapiro		  case SMFIR_REPLYCODE:					\
40090792Sgshapiro			if (MilterLogLevel > 3)				\
40190792Sgshapiro			{						\
40290792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
40390792Sgshapiro					  "Milter: %s=%s, reject=%s",	\
40490792Sgshapiro					  str, addr, response);		\
40590792Sgshapiro				LogUsrErrs = false;			\
40690792Sgshapiro			}						\
407157001Sgshapiro			if (strncmp(response, "421 ", 4) == 0		\
408157001Sgshapiro			    || strncmp(response, "421-", 4) == 0)	\
409132943Sgshapiro			{						\
410132943Sgshapiro				bool tsave = QuickAbort;		\
411132943Sgshapiro									\
412132943Sgshapiro				QuickAbort = false;			\
413132943Sgshapiro				usrerr(response);			\
414132943Sgshapiro				QuickAbort = tsave;			\
415132943Sgshapiro				e->e_sendqueue = NULL;			\
416132943Sgshapiro				goto doquit;				\
417132943Sgshapiro			}						\
418132943Sgshapiro			else						\
419132943Sgshapiro				usrerr(response);			\
42090792Sgshapiro			break;						\
42190792Sgshapiro									\
42290792Sgshapiro		  case SMFIR_REJECT:					\
42390792Sgshapiro			if (MilterLogLevel > 3)				\
42490792Sgshapiro			{						\
42590792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
42690792Sgshapiro					  "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
42790792Sgshapiro					  str, addr);			\
42890792Sgshapiro				LogUsrErrs = false;			\
42990792Sgshapiro			}						\
43090792Sgshapiro			usrerr("550 5.7.1 Command rejected");		\
43190792Sgshapiro			break;						\
43290792Sgshapiro									\
43390792Sgshapiro		  case SMFIR_DISCARD:					\
43490792Sgshapiro			if (MilterLogLevel > 3)				\
43590792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
43690792Sgshapiro					  "Milter: %s=%s, discard",	\
43790792Sgshapiro					  str, addr);			\
43890792Sgshapiro			e->e_flags |= EF_DISCARD;			\
439168515Sgshapiro			milter_cmd_fail = false;			\
44090792Sgshapiro			break;						\
44190792Sgshapiro									\
44290792Sgshapiro		  case SMFIR_TEMPFAIL:					\
44390792Sgshapiro			if (MilterLogLevel > 3)				\
44490792Sgshapiro			{						\
44590792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
44690792Sgshapiro					  "Milter: %s=%s, reject=%s",	\
44790792Sgshapiro					  str, addr, MSG_TEMPFAIL);	\
44890792Sgshapiro				LogUsrErrs = false;			\
44990792Sgshapiro			}						\
45090792Sgshapiro			usrerr(MSG_TEMPFAIL);				\
45190792Sgshapiro			break;						\
452168515Sgshapiro		  default:						\
453168515Sgshapiro			milter_cmd_fail = false;			\
454168515Sgshapiro			break;						\
45590792Sgshapiro		}							\
45690792Sgshapiro		LogUsrErrs = savelogusrerrs;				\
45790792Sgshapiro		if (response != NULL)					\
45890792Sgshapiro			sm_free(response); /* XXX */			\
45990792Sgshapiro	}
46090792Sgshapiro
46190792Sgshapiro#else /* MILTER */
46290792Sgshapiro# define MILTER_ABORT(e)
46390792Sgshapiro#endif /* MILTER */
46490792Sgshapiro
46590792Sgshapiro/* clear all SMTP state (for HELO/EHLO/RSET) */
46690792Sgshapiro#define CLEAR_STATE(cmd)					\
467132943Sgshapirodo								\
46890792Sgshapiro{								\
46990792Sgshapiro	/* abort milter filters */				\
47090792Sgshapiro	MILTER_ABORT(e);					\
47190792Sgshapiro								\
47290792Sgshapiro	if (smtp.sm_nrcpts > 0)					\
47390792Sgshapiro	{							\
47490792Sgshapiro		logundelrcpts(e, cmd, 10, false);		\
47590792Sgshapiro		smtp.sm_nrcpts = 0;				\
47690792Sgshapiro		macdefine(&e->e_macro, A_PERM,			\
47790792Sgshapiro			  macid("{nrcpts}"), "0");		\
47890792Sgshapiro	}							\
47990792Sgshapiro								\
48090792Sgshapiro	e->e_sendqueue = NULL;					\
48190792Sgshapiro	e->e_flags |= EF_CLRQUEUE;				\
48290792Sgshapiro								\
483203004Sgshapiro	if (tTd(92, 2))						\
484203004Sgshapiro		sm_dprintf("CLEAR_STATE: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",\
485203004Sgshapiro			e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel);\
48690792Sgshapiro	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))	\
48790792Sgshapiro		logsender(e, NULL);				\
48890792Sgshapiro	e->e_flags &= ~EF_LOGSENDER;				\
48990792Sgshapiro								\
49090792Sgshapiro	/* clean up a bit */					\
49190792Sgshapiro	smtp.sm_gotmail = false;				\
49290792Sgshapiro	SuprErrs = true;					\
493203004Sgshapiro	(void) dropenvelope(e, true, false);			\
49490792Sgshapiro	sm_rpool_free(e->e_rpool);				\
49590792Sgshapiro	e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL));	\
49690792Sgshapiro	CurEnv = e;						\
497168515Sgshapiro	e->e_features = features;				\
498132943Sgshapiro								\
499132943Sgshapiro	/* put back discard bit */				\
500132943Sgshapiro	if (smtp.sm_discard)					\
501132943Sgshapiro		e->e_flags |= EF_DISCARD;			\
502132943Sgshapiro								\
503132943Sgshapiro	/* restore connection quarantining */			\
504132943Sgshapiro	if (smtp.sm_quarmsg == NULL)				\
505132943Sgshapiro	{							\
506132943Sgshapiro		e->e_quarmsg = NULL;				\
507132943Sgshapiro		macdefine(&e->e_macro, A_PERM,			\
508132943Sgshapiro			macid("{quarantine}"), "");		\
509132943Sgshapiro	}							\
510132943Sgshapiro	else							\
511132943Sgshapiro	{							\
512132943Sgshapiro		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,	\
513132943Sgshapiro						smtp.sm_quarmsg);	\
514132943Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"),	\
515132943Sgshapiro			  e->e_quarmsg);			\
516132943Sgshapiro	}							\
517132943Sgshapiro} while (0)
51890792Sgshapiro
51990792Sgshapiro/* sleep to flatten out connection load */
52090792Sgshapiro#define MIN_DELAY_LOG	15	/* wait before logging this again */
52190792Sgshapiro
52290792Sgshapiro/* is it worth setting the process title for 1s? */
52390792Sgshapiro#define DELAY_CONN(cmd)						\
52490792Sgshapiro	if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA)	\
52590792Sgshapiro	{							\
52690792Sgshapiro		time_t dnow;					\
52790792Sgshapiro								\
52890792Sgshapiro		sm_setproctitle(true, e,			\
52990792Sgshapiro				"%s: %s: delaying %s: load average: %d", \
53090792Sgshapiro				qid_printname(e), CurSmtpClient,	\
53190792Sgshapiro				cmd, DelayLA);	\
53290792Sgshapiro		if (LogLevel > 8 && (dnow = curtime()) > log_delay)	\
53390792Sgshapiro		{						\
53490792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,		\
53590792Sgshapiro				  "delaying=%s, load average=%d >= %d",	\
53690792Sgshapiro				  cmd, CurrentLA, DelayLA);		\
53790792Sgshapiro			log_delay = dnow + MIN_DELAY_LOG;	\
53890792Sgshapiro		}						\
53990792Sgshapiro		(void) sleep(1);				\
54090792Sgshapiro		sm_setproctitle(true, e, "%s %s: %.80s",	\
54190792Sgshapiro				qid_printname(e), CurSmtpClient, inp);	\
54290792Sgshapiro	}
54390792Sgshapiro
544168515Sgshapirostatic bool SevenBitInput_Saved;	/* saved version of SevenBitInput */
54590792Sgshapiro
54638032Spetervoid
54764562Sgshapirosmtp(nullserver, d_flags, e)
54864562Sgshapiro	char *volatile nullserver;
54964562Sgshapiro	BITMAP256 d_flags;
55038032Speter	register ENVELOPE *volatile e;
55138032Speter{
55238032Speter	register char *volatile p;
55364562Sgshapiro	register struct cmd *volatile c = NULL;
55438032Speter	char *cmd;
55538032Speter	auto ADDRESS *vrfyqueue;
55638032Speter	ADDRESS *a;
55738032Speter	volatile bool gothello;		/* helo command received */
55838032Speter	bool vrfy;			/* set if this is a vrfy command */
55938032Speter	char *volatile protocol;	/* sending protocol */
56038032Speter	char *volatile sendinghost;	/* sending hostname */
56138032Speter	char *volatile peerhostname;	/* name of SMTP peer or "localhost" */
56238032Speter	auto char *delimptr;
56338032Speter	char *id;
56490792Sgshapiro	volatile unsigned int n_badcmds = 0;	/* count of bad commands */
56590792Sgshapiro	volatile unsigned int n_badrcpts = 0;	/* number of rejected RCPT */
56690792Sgshapiro	volatile unsigned int n_verifies = 0;	/* count of VRFY/EXPN */
56790792Sgshapiro	volatile unsigned int n_etrn = 0;	/* count of ETRN */
56890792Sgshapiro	volatile unsigned int n_noop = 0;	/* count of NOOP/VERB/etc */
56990792Sgshapiro	volatile unsigned int n_helo = 0;	/* count of HELO/EHLO */
57038032Speter	bool ok;
57190792Sgshapiro	volatile bool first;
57290792Sgshapiro	volatile bool tempfail = false;
57364562Sgshapiro	volatile time_t wt;		/* timeout after too many commands */
57464562Sgshapiro	volatile time_t previous;	/* time after checksmtpattack() */
57590792Sgshapiro	volatile bool lognullconnection = true;
57638032Speter	register char *q;
57790792Sgshapiro	SMTP_T smtp;
57864562Sgshapiro	char *addr;
57964562Sgshapiro	char *greetcode = "220";
58090792Sgshapiro	char *hostname;			/* my hostname ($j) */
58138032Speter	QUEUE_CHAR *new;
58264562Sgshapiro	char *args[MAXSMTPARGS];
583168515Sgshapiro	char inp[MAXINPLINE];
584168515Sgshapiro#if MAXINPLINE < MAXLINE
585168515Sgshapiro ERROR _MAXINPLINE must NOT be less than _MAXLINE: MAXINPLINE < MAXLINE
586168515Sgshapiro#endif /* MAXINPLINE < MAXLINE */
58738032Speter	char cmdbuf[MAXLINE];
58890792Sgshapiro#if SASL
58964562Sgshapiro	sasl_conn_t *conn;
59064562Sgshapiro	volatile bool sasl_ok;
59190792Sgshapiro	volatile unsigned int n_auth = 0;	/* count of AUTH commands */
59264562Sgshapiro	bool ismore;
59364562Sgshapiro	int result;
59464562Sgshapiro	volatile int authenticating;
59564562Sgshapiro	char *user;
59698121Sgshapiro	char *in, *out2;
59798121Sgshapiro# if SASL >= 20000
598168515Sgshapiro	char *auth_id = NULL;
59998121Sgshapiro	const char *out;
600102528Sgshapiro	sasl_ssf_t ext_ssf;
601120256Sgshapiro	char localip[60], remoteip[60];
60298121Sgshapiro# else /* SASL >= 20000 */
60398121Sgshapiro	char *out;
60464562Sgshapiro	const char *errstr;
60598121Sgshapiro	sasl_external_properties_t ext_ssf;
606120256Sgshapiro	struct sockaddr_in saddr_l;
607120256Sgshapiro	struct sockaddr_in saddr_r;
60898121Sgshapiro# endif /* SASL >= 20000 */
60998121Sgshapiro	sasl_security_properties_t ssp;
61098121Sgshapiro	sasl_ssf_t *ssf;
61190792Sgshapiro	unsigned int inlen, out2len;
61264562Sgshapiro	unsigned int outlen;
61364562Sgshapiro	char *volatile auth_type;
61464562Sgshapiro	char *mechlist;
61590792Sgshapiro	volatile unsigned int n_mechs;
61690792Sgshapiro	unsigned int len;
617168515Sgshapiro#else /* SASL */
61890792Sgshapiro#endif /* SASL */
619132943Sgshapiro	int r;
62090792Sgshapiro#if STARTTLS
62166494Sgshapiro	int rfd, wfd;
62290792Sgshapiro	volatile bool tls_active = false;
623132943Sgshapiro	volatile bool smtps = bitnset(D_SMTPS, d_flags);
62464562Sgshapiro	bool saveQuickAbort;
62564562Sgshapiro	bool saveSuprErrs;
62690792Sgshapiro	time_t tlsstart;
62790792Sgshapiro#endif /* STARTTLS */
62890792Sgshapiro	volatile unsigned int features;
62990792Sgshapiro#if PIPELINING
63090792Sgshapiro# if _FFR_NO_PIPE
63190792Sgshapiro	int np_log = 0;
63290792Sgshapiro# endif /* _FFR_NO_PIPE */
63390792Sgshapiro#endif /* PIPELINING */
63490792Sgshapiro	volatile time_t log_delay = (time_t) 0;
635168515Sgshapiro#if MILTER
636168515Sgshapiro	volatile bool milter_cmd_done, milter_cmd_safe;
637168515Sgshapiro	volatile bool milter_rcpt_added, milter_cmd_fail;
638168515Sgshapiro	ADDRESS addr_st;
639168515Sgshapiro# define p_addr_st	&addr_st
640168515Sgshapiro#else /* MILTER */
641168515Sgshapiro# define p_addr_st	NULL
642168515Sgshapiro#endif /* MILTER */
643168515Sgshapiro	size_t inplen;
644182352Sgshapiro#if _FFR_BADRCPT_SHUTDOWN
645182352Sgshapiro	int n_badrcpts_adj;
646182352Sgshapiro#endif /* _FFR_BADRCPT_SHUTDOWN */
64738032Speter
648168515Sgshapiro	SevenBitInput_Saved = SevenBitInput;
64990792Sgshapiro	smtp.sm_nrcpts = 0;
65090792Sgshapiro#if MILTER
65190792Sgshapiro	smtp.sm_milterize = (nullserver == NULL);
65290792Sgshapiro	smtp.sm_milterlist = false;
653168515Sgshapiro	addr = NULL;
65490792Sgshapiro#endif /* MILTER */
65590792Sgshapiro
65690792Sgshapiro	/* setup I/O fd correctly for the SMTP server */
65790792Sgshapiro	setup_smtpd_io();
65890792Sgshapiro
65990792Sgshapiro#if SM_HEAP_CHECK
66090792Sgshapiro	if (sm_debug_active(&DebugLeakSmtp, 1))
66138032Speter	{
66290792Sgshapiro		sm_heap_newgroup();
66390792Sgshapiro		sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
66438032Speter	}
66590792Sgshapiro#endif /* SM_HEAP_CHECK */
66664562Sgshapiro
66790792Sgshapiro	/* XXX the rpool should be set when e is initialized in main() */
66890792Sgshapiro	e->e_rpool = sm_rpool_new_x(NULL);
66990792Sgshapiro	e->e_macro.mac_rpool = e->e_rpool;
67090792Sgshapiro
67138032Speter	settime(e);
67290792Sgshapiro	sm_getla();
67338032Speter	peerhostname = RealHostName;
67438032Speter	if (peerhostname == NULL)
67538032Speter		peerhostname = "localhost";
67638032Speter	CurHostName = peerhostname;
67738032Speter	CurSmtpClient = macvalue('_', e);
67838032Speter	if (CurSmtpClient == NULL)
67938032Speter		CurSmtpClient = CurHostName;
68038032Speter
68138032Speter	/* check_relay may have set discard bit, save for later */
68290792Sgshapiro	smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
68338032Speter
68490792Sgshapiro#if PIPELINING
68590792Sgshapiro	/* auto-flush output when reading input */
68690792Sgshapiro	(void) sm_io_autoflush(InChannel, OutChannel);
68790792Sgshapiro#endif /* PIPELINING */
68864562Sgshapiro
68990792Sgshapiro	sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
69090792Sgshapiro
69190792Sgshapiro	/* Set default features for server. */
69290792Sgshapiro	features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
69390792Sgshapiro		     bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
69490792Sgshapiro		| (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
69590792Sgshapiro		| (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
69690792Sgshapiro			: (SRV_OFFER_EXPN
69790792Sgshapiro			  | (bitset(PRIV_NOVERB, PrivacyFlags)
69890792Sgshapiro			     ? SRV_NONE : SRV_OFFER_VERB)))
699159609Sgshapiro		| ((bitset(PRIV_NORECEIPTS, PrivacyFlags) || !SendMIMEErrors)
700159609Sgshapiro			 ? SRV_NONE : SRV_OFFER_DSN)
70190792Sgshapiro#if SASL
70290792Sgshapiro		| (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
703132943Sgshapiro		| (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC
704132943Sgshapiro							  : SRV_NONE)
70590792Sgshapiro#endif /* SASL */
70690792Sgshapiro#if PIPELINING
70790792Sgshapiro		| SRV_OFFER_PIPE
70890792Sgshapiro#endif /* PIPELINING */
70990792Sgshapiro#if STARTTLS
71090792Sgshapiro		| (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
71190792Sgshapiro		| (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
71290792Sgshapiro						       : SRV_VRFY_CLT)
71390792Sgshapiro#endif /* STARTTLS */
71490792Sgshapiro		;
71590792Sgshapiro	if (nullserver == NULL)
71690792Sgshapiro	{
71790792Sgshapiro		features = srvfeatures(e, CurSmtpClient, features);
71890792Sgshapiro		if (bitset(SRV_TMP_FAIL, features))
71990792Sgshapiro		{
72090792Sgshapiro			if (LogLevel > 4)
72190792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
72290792Sgshapiro					  "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
72390792Sgshapiro					  CurSmtpClient);
72490792Sgshapiro			nullserver = "450 4.3.0 Please try again later.";
72590792Sgshapiro		}
726132943Sgshapiro		else
727132943Sgshapiro		{
72890792Sgshapiro#if PIPELINING
72990792Sgshapiro# if _FFR_NO_PIPE
730132943Sgshapiro			if (bitset(SRV_NO_PIPE, features))
731132943Sgshapiro			{
732132943Sgshapiro				/* for consistency */
733132943Sgshapiro				features &= ~SRV_OFFER_PIPE;
734132943Sgshapiro			}
73590792Sgshapiro# endif /* _FFR_NO_PIPE */
73690792Sgshapiro#endif /* PIPELINING */
737132943Sgshapiro#if SASL
738132943Sgshapiro			if (bitset(SRV_REQ_SEC, features))
739132943Sgshapiro				SASLOpts |= SASL_SEC_NOPLAINTEXT;
740132943Sgshapiro			else
741132943Sgshapiro				SASLOpts &= ~SASL_SEC_NOPLAINTEXT;
742132943Sgshapiro#endif /* SASL */
743132943Sgshapiro		}
74490792Sgshapiro	}
745132943Sgshapiro	else if (strncmp(nullserver, "421 ", 4) == 0)
746132943Sgshapiro	{
747132943Sgshapiro		message(nullserver);
748132943Sgshapiro		goto doquit;
749132943Sgshapiro	}
75090792Sgshapiro
751168515Sgshapiro	e->e_features = features;
75290792Sgshapiro	hostname = macvalue('j', e);
75390792Sgshapiro#if SASL
754132943Sgshapiro	if (AuthRealm == NULL)
755132943Sgshapiro		AuthRealm = hostname;
75690792Sgshapiro	sasl_ok = bitset(SRV_OFFER_AUTH, features);
75764562Sgshapiro	n_mechs = 0;
75890792Sgshapiro	authenticating = SASL_NOT_AUTH;
75964562Sgshapiro
76064562Sgshapiro	/* SASL server new connection */
76190792Sgshapiro	if (sasl_ok)
76238032Speter	{
76398121Sgshapiro# if SASL >= 20000
764132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL,
76598121Sgshapiro					 NULL, 0, &conn);
76698121Sgshapiro# elif SASL > 10505
76790792Sgshapiro		/* use empty realm: only works in SASL > 1.5.5 */
768132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn);
76998121Sgshapiro# else /* SASL >= 20000 */
77090792Sgshapiro		/* use no realm -> realm is set to hostname by SASL lib */
771132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0,
77290792Sgshapiro					 &conn);
77398121Sgshapiro# endif /* SASL >= 20000 */
77490792Sgshapiro		sasl_ok = result == SASL_OK;
77590792Sgshapiro		if (!sasl_ok)
77690792Sgshapiro		{
77790792Sgshapiro			if (LogLevel > 9)
77890792Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
77990792Sgshapiro					  "AUTH error: sasl_server_new failed=%d",
78090792Sgshapiro					  result);
78190792Sgshapiro		}
78290792Sgshapiro	}
78390792Sgshapiro	if (sasl_ok)
78490792Sgshapiro	{
78564562Sgshapiro		/*
78664562Sgshapiro		**  SASL set properties for sasl
78764562Sgshapiro		**  set local/remote IP
78898121Sgshapiro		**  XXX Cyrus SASL v1 only supports IPv4
78964562Sgshapiro		**
79064562Sgshapiro		**  XXX where exactly are these used/required?
79164562Sgshapiro		**  Kerberos_v4
79264562Sgshapiro		*/
79364562Sgshapiro
79498121Sgshapiro# if SASL >= 20000
795147078Sgshapiro		localip[0] = remoteip[0] = '\0';
79698121Sgshapiro#  if NETINET || NETINET6
79790792Sgshapiro		in = macvalue(macid("{daemon_family}"), e);
79898121Sgshapiro		if (in != NULL && (
79998121Sgshapiro#   if NETINET6
80098121Sgshapiro		    strcmp(in, "inet6") == 0 ||
80198121Sgshapiro#   endif /* NETINET6 */
80298121Sgshapiro		    strcmp(in, "inet") == 0))
80398121Sgshapiro		{
80498121Sgshapiro			SOCKADDR_LEN_T addrsize;
80598121Sgshapiro			SOCKADDR saddr_l;
80698121Sgshapiro			SOCKADDR saddr_r;
80798121Sgshapiro
80898121Sgshapiro			addrsize = sizeof(saddr_r);
80998121Sgshapiro			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
81098121Sgshapiro						      NULL),
81198121Sgshapiro					(struct sockaddr *) &saddr_r,
81298121Sgshapiro					&addrsize) == 0)
81398121Sgshapiro			{
81498121Sgshapiro				if (iptostring(&saddr_r, addrsize,
815168515Sgshapiro					       remoteip, sizeof(remoteip)))
81698121Sgshapiro				{
81798121Sgshapiro					sasl_setprop(conn, SASL_IPREMOTEPORT,
81898121Sgshapiro						     remoteip);
81998121Sgshapiro				}
82098121Sgshapiro				addrsize = sizeof(saddr_l);
82198121Sgshapiro				if (getsockname(sm_io_getinfo(InChannel,
82298121Sgshapiro							      SM_IO_WHAT_FD,
82398121Sgshapiro							      NULL),
82498121Sgshapiro						(struct sockaddr *) &saddr_l,
82598121Sgshapiro						&addrsize) == 0)
82698121Sgshapiro				{
82798121Sgshapiro					if (iptostring(&saddr_l, addrsize,
82898121Sgshapiro						       localip,
829168515Sgshapiro						       sizeof(localip)))
83098121Sgshapiro					{
83198121Sgshapiro						sasl_setprop(conn,
83298121Sgshapiro							     SASL_IPLOCALPORT,
83398121Sgshapiro							     localip);
83498121Sgshapiro					}
83598121Sgshapiro				}
83698121Sgshapiro			}
83798121Sgshapiro		}
83898121Sgshapiro#  endif /* NETINET || NETINET6 */
83998121Sgshapiro# else /* SASL >= 20000 */
84098121Sgshapiro#  if NETINET
84198121Sgshapiro		in = macvalue(macid("{daemon_family}"), e);
84264562Sgshapiro		if (in != NULL && strcmp(in, "inet") == 0)
84364562Sgshapiro		{
84464562Sgshapiro			SOCKADDR_LEN_T addrsize;
84564562Sgshapiro
84664562Sgshapiro			addrsize = sizeof(struct sockaddr_in);
84790792Sgshapiro			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
84890792Sgshapiro						      NULL),
84964562Sgshapiro					(struct sockaddr *)&saddr_r,
85064562Sgshapiro					&addrsize) == 0)
85164562Sgshapiro			{
85264562Sgshapiro				sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
85364562Sgshapiro				addrsize = sizeof(struct sockaddr_in);
85490792Sgshapiro				if (getsockname(sm_io_getinfo(InChannel,
85590792Sgshapiro							      SM_IO_WHAT_FD,
85690792Sgshapiro							      NULL),
85764562Sgshapiro						(struct sockaddr *)&saddr_l,
85864562Sgshapiro						&addrsize) == 0)
85964562Sgshapiro					sasl_setprop(conn, SASL_IP_LOCAL,
86064562Sgshapiro						     &saddr_l);
86164562Sgshapiro			}
86264562Sgshapiro		}
86398121Sgshapiro#  endif /* NETINET */
86498121Sgshapiro# endif /* SASL >= 20000 */
86564562Sgshapiro
86664562Sgshapiro		auth_type = NULL;
86764562Sgshapiro		mechlist = NULL;
86864562Sgshapiro		user = NULL;
86990792Sgshapiro# if 0
87090792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
87190792Sgshapiro			macid("{auth_author}"), NULL);
87290792Sgshapiro# endif /* 0 */
87364562Sgshapiro
87464562Sgshapiro		/* set properties */
875168515Sgshapiro		(void) memset(&ssp, '\0', sizeof(ssp));
87690792Sgshapiro
87764562Sgshapiro		/* XXX should these be options settable via .cf ? */
87864562Sgshapiro		/* ssp.min_ssf = 0; is default due to memset() */
879223067Sgshapiro		ssp.max_ssf = MaxSLBits;
880223067Sgshapiro		ssp.maxbufsize = MAXOUTLEN;
88164562Sgshapiro		ssp.security_flags = SASLOpts & SASL_SEC_MASK;
88264562Sgshapiro		sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
88364562Sgshapiro
88464562Sgshapiro		if (sasl_ok)
88564562Sgshapiro		{
88664562Sgshapiro			/*
88764562Sgshapiro			**  external security strength factor;
88890792Sgshapiro			**	currently we have none so zero
88964562Sgshapiro			*/
89090792Sgshapiro
89198121Sgshapiro# if SASL >= 20000
89298121Sgshapiro			ext_ssf = 0;
89398121Sgshapiro			auth_id = NULL;
89498121Sgshapiro			sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL,
89598121Sgshapiro						 &ext_ssf) == SASL_OK) &&
89698121Sgshapiro				   (sasl_setprop(conn, SASL_AUTH_EXTERNAL,
897102528Sgshapiro						 auth_id) == SASL_OK));
89898121Sgshapiro# else /* SASL >= 20000 */
89964562Sgshapiro			ext_ssf.ssf = 0;
90064562Sgshapiro			ext_ssf.auth_id = NULL;
90164562Sgshapiro			sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
90264562Sgshapiro					       &ext_ssf) == SASL_OK;
90398121Sgshapiro# endif /* SASL >= 20000 */
90464562Sgshapiro		}
90564562Sgshapiro		if (sasl_ok)
90664562Sgshapiro			n_mechs = saslmechs(conn, &mechlist);
90738032Speter	}
90890792Sgshapiro#endif /* SASL */
90938032Speter
910120256Sgshapiro#if STARTTLS
911182352Sgshapiro
912203004Sgshapiro
913182352Sgshapiro	set_tls_rd_tmo(TimeOuts.to_nextcommand);
914120256Sgshapiro#endif /* STARTTLS */
915120256Sgshapiro
91690792Sgshapiro#if MILTER
91790792Sgshapiro	if (smtp.sm_milterize)
91864562Sgshapiro	{
91964562Sgshapiro		char state;
92064562Sgshapiro
92164562Sgshapiro		/* initialize mail filter connection */
922173340Sgshapiro		smtp.sm_milterlist = milter_init(e, &state, &smtp.sm_milters);
92364562Sgshapiro		switch (state)
92464562Sgshapiro		{
92564562Sgshapiro		  case SMFIR_REJECT:
92690792Sgshapiro			if (MilterLogLevel > 3)
92790792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
92894334Sgshapiro					  "Milter: initialization failed, rejecting commands");
92964562Sgshapiro			greetcode = "554";
93064562Sgshapiro			nullserver = "Command rejected";
93190792Sgshapiro			smtp.sm_milterize = false;
93264562Sgshapiro			break;
93364562Sgshapiro
93464562Sgshapiro		  case SMFIR_TEMPFAIL:
93590792Sgshapiro			if (MilterLogLevel > 3)
93690792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
93794334Sgshapiro					  "Milter: initialization failed, temp failing commands");
93890792Sgshapiro			tempfail = true;
93990792Sgshapiro			smtp.sm_milterize = false;
94064562Sgshapiro			break;
941157001Sgshapiro
942157001Sgshapiro		  case SMFIR_SHUTDOWN:
943157001Sgshapiro			if (MilterLogLevel > 3)
944157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
945157001Sgshapiro					  "Milter: initialization failed, closing connection");
946157001Sgshapiro			tempfail = true;
947157001Sgshapiro			smtp.sm_milterize = false;
948157001Sgshapiro			message("421 4.7.0 %s closing connection",
949157001Sgshapiro					MyHostName);
950157001Sgshapiro
951157001Sgshapiro			/* arrange to ignore send list */
952157001Sgshapiro			e->e_sendqueue = NULL;
953182352Sgshapiro			lognullconnection = false;
954157001Sgshapiro			goto doquit;
95564562Sgshapiro		}
95664562Sgshapiro	}
95764562Sgshapiro
95890792Sgshapiro	if (smtp.sm_milterlist && smtp.sm_milterize &&
95990792Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
96064562Sgshapiro	{
96164562Sgshapiro		char state;
96290792Sgshapiro		char *response;
96364562Sgshapiro
964161389Sgshapiro		q = macvalue(macid("{client_name}"), e);
965168515Sgshapiro		SM_ASSERT(q != NULL || OpMode == MD_SMTP);
966168515Sgshapiro		if (q == NULL)
967168515Sgshapiro			q = "localhost";
968161389Sgshapiro		response = milter_connect(q, RealHostAddr, e, &state);
96964562Sgshapiro		switch (state)
97064562Sgshapiro		{
97164562Sgshapiro		  case SMFIR_REPLYCODE:	/* REPLYCODE shouldn't happen */
97264562Sgshapiro		  case SMFIR_REJECT:
97390792Sgshapiro			if (MilterLogLevel > 3)
97490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
97590792Sgshapiro					  "Milter: connect: host=%s, addr=%s, rejecting commands",
97690792Sgshapiro					  peerhostname,
97790792Sgshapiro					  anynet_ntoa(&RealHostAddr));
97864562Sgshapiro			greetcode = "554";
97964562Sgshapiro			nullserver = "Command rejected";
98090792Sgshapiro			smtp.sm_milterize = false;
98164562Sgshapiro			break;
98264562Sgshapiro
98364562Sgshapiro		  case SMFIR_TEMPFAIL:
98490792Sgshapiro			if (MilterLogLevel > 3)
98590792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
98690792Sgshapiro					  "Milter: connect: host=%s, addr=%s, temp failing commands",
98790792Sgshapiro					  peerhostname,
98890792Sgshapiro					  anynet_ntoa(&RealHostAddr));
98990792Sgshapiro			tempfail = true;
99090792Sgshapiro			smtp.sm_milterize = false;
99164562Sgshapiro			break;
992110560Sgshapiro
993110560Sgshapiro		  case SMFIR_SHUTDOWN:
994110560Sgshapiro			if (MilterLogLevel > 3)
995110560Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
996110560Sgshapiro					  "Milter: connect: host=%s, addr=%s, shutdown",
997110560Sgshapiro					  peerhostname,
998110560Sgshapiro					  anynet_ntoa(&RealHostAddr));
999110560Sgshapiro			tempfail = true;
1000110560Sgshapiro			smtp.sm_milterize = false;
1001110560Sgshapiro			message("421 4.7.0 %s closing connection",
1002110560Sgshapiro					MyHostName);
1003110560Sgshapiro
1004110560Sgshapiro			/* arrange to ignore send list */
1005110560Sgshapiro			e->e_sendqueue = NULL;
1006110560Sgshapiro			goto doquit;
100764562Sgshapiro		}
100890792Sgshapiro		if (response != NULL)
100990792Sgshapiro			sm_free(response); /* XXX */
101064562Sgshapiro	}
101190792Sgshapiro#endif /* MILTER */
101264562Sgshapiro
1013132943Sgshapiro	/*
1014132943Sgshapiro	**  Broken proxies and SMTP slammers
1015132943Sgshapiro	**  push data without waiting, catch them
1016132943Sgshapiro	*/
1017132943Sgshapiro
1018132943Sgshapiro	if (
101990792Sgshapiro#if STARTTLS
1020132943Sgshapiro	    !smtps &&
1021132943Sgshapiro#endif /* STARTTLS */
1022168515Sgshapiro	    *greetcode == '2' && nullserver == NULL)
1023132943Sgshapiro	{
1024132943Sgshapiro		time_t msecs = 0;
1025132943Sgshapiro		char **pvp;
1026132943Sgshapiro		char pvpbuf[PSBUFSIZE];
1027132943Sgshapiro
1028132943Sgshapiro		/* Ask the rulesets how long to pause */
1029132943Sgshapiro		pvp = NULL;
1030132943Sgshapiro		r = rscap("greet_pause", peerhostname,
1031132943Sgshapiro			  anynet_ntoa(&RealHostAddr), e,
1032132943Sgshapiro			  &pvp, pvpbuf, sizeof(pvpbuf));
1033132943Sgshapiro		if (r == EX_OK && pvp != NULL && pvp[0] != NULL &&
1034132943Sgshapiro		    (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
1035132943Sgshapiro		{
1036132943Sgshapiro			msecs = strtol(pvp[1], NULL, 10);
1037132943Sgshapiro		}
1038132943Sgshapiro
1039132943Sgshapiro		if (msecs > 0)
1040132943Sgshapiro		{
1041132943Sgshapiro			int fd;
1042132943Sgshapiro			fd_set readfds;
1043132943Sgshapiro			struct timeval timeout;
1044157001Sgshapiro			struct timeval bp, ep, tp; /* {begin,end,total}pause */
1045168515Sgshapiro			int eoftest;
1046132943Sgshapiro
1047132943Sgshapiro			/* pause for a moment */
1048132943Sgshapiro			timeout.tv_sec = msecs / 1000;
1049132943Sgshapiro			timeout.tv_usec = (msecs % 1000) * 1000;
1050132943Sgshapiro
1051132943Sgshapiro			/* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
1052132943Sgshapiro			if (timeout.tv_sec >= 300)
1053132943Sgshapiro			{
1054132943Sgshapiro				timeout.tv_sec = 300;
1055132943Sgshapiro				timeout.tv_usec = 0;
1056132943Sgshapiro			}
1057132943Sgshapiro
1058132943Sgshapiro			/* check if data is on the socket during the pause */
1059132943Sgshapiro			fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
1060132943Sgshapiro			FD_ZERO(&readfds);
1061132943Sgshapiro			SM_FD_SET(fd, &readfds);
1062157001Sgshapiro			gettimeofday(&bp, NULL);
1063132943Sgshapiro			if (select(fd + 1, FDSET_CAST &readfds,
1064132943Sgshapiro			    NULL, NULL, &timeout) > 0 &&
1065168515Sgshapiro			    FD_ISSET(fd, &readfds) &&
1066182352Sgshapiro			    (eoftest = sm_io_getc(InChannel, SM_TIME_DEFAULT))
1067168515Sgshapiro			    != SM_IO_EOF)
1068132943Sgshapiro			{
1069182352Sgshapiro				sm_io_ungetc(InChannel, SM_TIME_DEFAULT,
1070168515Sgshapiro					     eoftest);
1071157001Sgshapiro				gettimeofday(&ep, NULL);
1072157001Sgshapiro				timersub(&ep, &bp, &tp);
1073132943Sgshapiro				greetcode = "554";
1074132943Sgshapiro				nullserver = "Command rejected";
1075132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
1076168515Sgshapiro					  "rejecting commands from %s [%s] due to pre-greeting traffic after %d seconds",
1077132943Sgshapiro					  peerhostname,
1078168515Sgshapiro					  anynet_ntoa(&RealHostAddr),
1079168515Sgshapiro					  (int) tp.tv_sec +
1080157001Sgshapiro						(tp.tv_usec >= 500000 ? 1 : 0)
1081157001Sgshapiro					 );
1082132943Sgshapiro			}
1083132943Sgshapiro		}
1084132943Sgshapiro	}
1085132943Sgshapiro
1086132943Sgshapiro#if STARTTLS
108790792Sgshapiro	/* If this an smtps connection, start TLS now */
108890792Sgshapiro	if (smtps)
1089120256Sgshapiro	{
1090120256Sgshapiro		Errors = 0;
109190792Sgshapiro		goto starttls;
1092120256Sgshapiro	}
109390792Sgshapiro
109490792Sgshapiro  greeting:
109590792Sgshapiro
109690792Sgshapiro#endif /* STARTTLS */
109790792Sgshapiro
109838032Speter	/* output the first line, inserting "ESMTP" as second word */
109990792Sgshapiro	if (*greetcode == '5')
1100168515Sgshapiro		(void) sm_snprintf(inp, sizeof(inp),
1101168515Sgshapiro				"%s not accepting messages", hostname);
110290792Sgshapiro	else
1103168515Sgshapiro		expand(SmtpGreeting, inp, sizeof(inp), e);
110490792Sgshapiro
110538032Speter	p = strchr(inp, '\n');
110638032Speter	if (p != NULL)
110738032Speter		*p++ = '\0';
110838032Speter	id = strchr(inp, ' ');
110938032Speter	if (id == NULL)
111038032Speter		id = &inp[strlen(inp)];
111164562Sgshapiro	if (p == NULL)
1112168515Sgshapiro		(void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
111364562Sgshapiro			 "%s %%.*s ESMTP%%s", greetcode);
111464562Sgshapiro	else
1115168515Sgshapiro		(void) sm_snprintf(cmdbuf, sizeof(cmdbuf),
111664562Sgshapiro			 "%s-%%.*s ESMTP%%s", greetcode);
111766494Sgshapiro	message(cmdbuf, (int) (id - inp), inp, id);
111838032Speter
111938032Speter	/* output remaining lines */
112038032Speter	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
112138032Speter	{
112238032Speter		*p++ = '\0';
112338032Speter		if (isascii(*id) && isspace(*id))
112438032Speter			id++;
1125168515Sgshapiro		(void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, "-%s");
112664562Sgshapiro		message(cmdbuf, id);
112738032Speter	}
112838032Speter	if (id != NULL)
112938032Speter	{
113038032Speter		if (isascii(*id) && isspace(*id))
113138032Speter			id++;
1132168515Sgshapiro		(void) sm_strlcpyn(cmdbuf, sizeof(cmdbuf), 2, greetcode, " %s");
113364562Sgshapiro		message(cmdbuf, id);
113438032Speter	}
113538032Speter
113638032Speter	protocol = NULL;
113738032Speter	sendinghost = macvalue('s', e);
113890792Sgshapiro
113990792Sgshapiro	/* If quarantining by a connect/ehlo action, save between messages */
114090792Sgshapiro	if (e->e_quarmsg == NULL)
114190792Sgshapiro		smtp.sm_quarmsg = NULL;
114290792Sgshapiro	else
114390792Sgshapiro		smtp.sm_quarmsg = newstr(e->e_quarmsg);
114490792Sgshapiro
114590792Sgshapiro	/* sendinghost's storage must outlive the current envelope */
114690792Sgshapiro	if (sendinghost != NULL)
114790792Sgshapiro		sendinghost = sm_strdup_x(sendinghost);
114890792Sgshapiro	first = true;
114990792Sgshapiro	gothello = false;
115090792Sgshapiro	smtp.sm_gotmail = false;
115138032Speter	for (;;)
115238032Speter	{
115390792Sgshapiro	    SM_TRY
115490792Sgshapiro	    {
115590792Sgshapiro		QuickAbort = false;
115690792Sgshapiro		HoldErrs = false;
115790792Sgshapiro		SuprErrs = false;
115890792Sgshapiro		LogUsrErrs = false;
115990792Sgshapiro		OnlyOneError = true;
116038032Speter		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
1161168515Sgshapiro#if MILTER
1162168515Sgshapiro		milter_cmd_fail = false;
1163168515Sgshapiro#endif /* MILTER */
116438032Speter
116538032Speter		/* setup for the read */
116638032Speter		e->e_to = NULL;
116738032Speter		Errors = 0;
116864562Sgshapiro		FileName = NULL;
116990792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
117038032Speter
117138032Speter		/* read the input line */
117238032Speter		SmtpPhase = "server cmd read";
117390792Sgshapiro		sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
117438032Speter
117538032Speter		/* handle errors */
117690792Sgshapiro		if (sm_io_error(OutChannel) ||
1177168515Sgshapiro		    (p = sfgets(inp, sizeof(inp), InChannel,
117864562Sgshapiro				TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
117938032Speter		{
118064562Sgshapiro			char *d;
118164562Sgshapiro
118290792Sgshapiro			d = macvalue(macid("{daemon_name}"), e);
118364562Sgshapiro			if (d == NULL)
118464562Sgshapiro				d = "stdin";
118538032Speter			/* end of file, just die */
118638032Speter			disconnect(1, e);
118764562Sgshapiro
118890792Sgshapiro#if MILTER
118964562Sgshapiro			/* close out milter filters */
119064562Sgshapiro			milter_quit(e);
119190792Sgshapiro#endif /* MILTER */
119264562Sgshapiro
119364562Sgshapiro			message("421 4.4.1 %s Lost input channel from %s",
119438032Speter				MyHostName, CurSmtpClient);
119590792Sgshapiro			if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
119638032Speter				sm_syslog(LOG_NOTICE, e->e_id,
1197110560Sgshapiro					  "lost input channel from %s to %s after %s",
119864562Sgshapiro					  CurSmtpClient, d,
119964562Sgshapiro					  (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
120038032Speter			/*
120142575Speter			**  If have not accepted mail (DATA), do not bounce
120242575Speter			**  bad addresses back to sender.
120338032Speter			*/
120464562Sgshapiro
120538032Speter			if (bitset(EF_CLRQUEUE, e->e_flags))
120638032Speter				e->e_sendqueue = NULL;
120764562Sgshapiro			goto doquit;
120838032Speter		}
120938032Speter
1210168515Sgshapiro		/* also used by "proxy" check below */
1211168515Sgshapiro		inplen = strlen(inp);
1212168515Sgshapiro#if SASL
1213168515Sgshapiro		/*
1214168515Sgshapiro		**  SMTP AUTH requires accepting any length,
1215168515Sgshapiro		**  at least for challenge/response. However, not imposing
1216168515Sgshapiro		**  a limit is a bad idea (denial of service).
1217168515Sgshapiro		*/
1218168515Sgshapiro
1219168515Sgshapiro		if (authenticating != SASL_PROC_AUTH
1220168515Sgshapiro		    && sm_strncasecmp(inp, "AUTH ", 5) != 0
1221168515Sgshapiro		    && inplen > MAXLINE)
1222168515Sgshapiro		{
1223168515Sgshapiro			message("421 4.7.0 %s Command too long, possible attack %s",
1224168515Sgshapiro				MyHostName, CurSmtpClient);
1225168515Sgshapiro			sm_syslog(LOG_INFO, e->e_id,
1226168515Sgshapiro				  "%s: SMTP violation, input too long: %lu",
1227168515Sgshapiro				  CurSmtpClient, (unsigned long) inplen);
1228168515Sgshapiro			goto doquit;
1229168515Sgshapiro		}
1230168515Sgshapiro#endif /* SASL */
1231168515Sgshapiro
123290792Sgshapiro		if (first)
123390792Sgshapiro		{
1234168515Sgshapiro			size_t cmdlen;
1235110560Sgshapiro			int idx;
1236110560Sgshapiro			char *http_cmd;
1237110560Sgshapiro			static char *http_cmds[] = { "GET", "POST",
1238110560Sgshapiro						     "CONNECT", "USER", NULL };
1239110560Sgshapiro
1240110560Sgshapiro			for (idx = 0; (http_cmd = http_cmds[idx]) != NULL;
1241110560Sgshapiro			     idx++)
1242110560Sgshapiro			{
1243110560Sgshapiro				cmdlen = strlen(http_cmd);
1244110560Sgshapiro				if (cmdlen < inplen &&
1245110560Sgshapiro				    sm_strncasecmp(inp, http_cmd, cmdlen) == 0 &&
1246110560Sgshapiro				    isascii(inp[cmdlen]) && isspace(inp[cmdlen]))
1247110560Sgshapiro				{
1248110560Sgshapiro					/* Open proxy, drop it */
1249110560Sgshapiro					message("421 4.7.0 %s Rejecting open proxy %s",
1250110560Sgshapiro						MyHostName, CurSmtpClient);
1251110560Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1252110560Sgshapiro						  "%s: probable open proxy: command=%.40s",
1253110560Sgshapiro						  CurSmtpClient, inp);
1254110560Sgshapiro					goto doquit;
1255110560Sgshapiro				}
1256110560Sgshapiro			}
125790792Sgshapiro			first = false;
125890792Sgshapiro		}
125990792Sgshapiro
126038032Speter		/* clean up end of line */
126190792Sgshapiro		fixcrlf(inp, true);
126238032Speter
126390792Sgshapiro#if PIPELINING
126490792Sgshapiro# if _FFR_NO_PIPE
126590792Sgshapiro		/*
126690792Sgshapiro		**  if there is more input and pipelining is disabled:
126790792Sgshapiro		**	delay ... (and maybe discard the input?)
126890792Sgshapiro		**  XXX this doesn't really work, at least in tests using
126990792Sgshapiro		**  telnet SM_IO_IS_READABLE only returns 1 if there were
127090792Sgshapiro		**  more than 2 input lines available.
127190792Sgshapiro		*/
127290792Sgshapiro
127390792Sgshapiro		if (bitset(SRV_NO_PIPE, features) &&
1274110560Sgshapiro		    sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
127590792Sgshapiro		{
127690792Sgshapiro			if (++np_log < 3)
127790792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
1278203004Sgshapiro					  "unauthorized PIPELINING, sleeping, relay=%.100s",
1279203004Sgshapiro					   CurSmtpClient);
128090792Sgshapiro			sleep(1);
128190792Sgshapiro		}
128290792Sgshapiro
128390792Sgshapiro# endif /* _FFR_NO_PIPE */
128490792Sgshapiro#endif /* PIPELINING */
128590792Sgshapiro
128690792Sgshapiro#if SASL
128764562Sgshapiro		if (authenticating == SASL_PROC_AUTH)
128864562Sgshapiro		{
128990792Sgshapiro# if 0
129064562Sgshapiro			if (*inp == '\0')
129164562Sgshapiro			{
129264562Sgshapiro				authenticating = SASL_NOT_AUTH;
129364562Sgshapiro				message("501 5.5.2 missing input");
1294120256Sgshapiro				RESET_SASLCONN;
129564562Sgshapiro				continue;
129664562Sgshapiro			}
129790792Sgshapiro# endif /* 0 */
129864562Sgshapiro			if (*inp == '*' && *(inp + 1) == '\0')
129964562Sgshapiro			{
130064562Sgshapiro				authenticating = SASL_NOT_AUTH;
130164562Sgshapiro
1302173340Sgshapiro				/* RFC 2554 4. */
130364562Sgshapiro				message("501 5.0.0 AUTH aborted");
1304120256Sgshapiro				RESET_SASLCONN;
130564562Sgshapiro				continue;
130664562Sgshapiro			}
130764562Sgshapiro
130864562Sgshapiro			/* could this be shorter? XXX */
130998121Sgshapiro# if SASL >= 20000
131098121Sgshapiro			in = xalloc(strlen(inp) + 1);
131198121Sgshapiro			result = sasl_decode64(inp, strlen(inp), in,
131298121Sgshapiro					       strlen(inp), &inlen);
131398121Sgshapiro# else /* SASL >= 20000 */
131464562Sgshapiro			out = xalloc(strlen(inp));
131564562Sgshapiro			result = sasl_decode64(inp, strlen(inp), out, &outlen);
131698121Sgshapiro# endif /* SASL >= 20000 */
131764562Sgshapiro			if (result != SASL_OK)
131864562Sgshapiro			{
131964562Sgshapiro				authenticating = SASL_NOT_AUTH;
132064562Sgshapiro
1321173340Sgshapiro				/* RFC 2554 4. */
132264562Sgshapiro				message("501 5.5.4 cannot decode AUTH parameter %s",
132364562Sgshapiro					inp);
132498121Sgshapiro# if SASL >= 20000
132598121Sgshapiro				sm_free(in);
132698121Sgshapiro# endif /* SASL >= 20000 */
1327120256Sgshapiro				RESET_SASLCONN;
132864562Sgshapiro				continue;
132964562Sgshapiro			}
133064562Sgshapiro
133198121Sgshapiro# if SASL >= 20000
133298121Sgshapiro			result = sasl_server_step(conn,	in, inlen,
133398121Sgshapiro						  &out, &outlen);
133498121Sgshapiro			sm_free(in);
133598121Sgshapiro# else /* SASL >= 20000 */
133664562Sgshapiro			result = sasl_server_step(conn,	out, outlen,
133764562Sgshapiro						  &out, &outlen, &errstr);
133898121Sgshapiro# endif /* SASL >= 20000 */
133964562Sgshapiro
134064562Sgshapiro			/* get an OK if we're done */
134164562Sgshapiro			if (result == SASL_OK)
134264562Sgshapiro			{
134364562Sgshapiro  authenticated:
134464562Sgshapiro				message("235 2.0.0 OK Authenticated");
134564562Sgshapiro				authenticating = SASL_IS_AUTH;
134690792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
134790792Sgshapiro					macid("{auth_type}"), auth_type);
134864562Sgshapiro
134998121Sgshapiro# if SASL >= 20000
135098121Sgshapiro				user = macvalue(macid("{auth_authen}"), e);
135198121Sgshapiro
135298121Sgshapiro				/* get security strength (features) */
135398121Sgshapiro				result = sasl_getprop(conn, SASL_SSF,
135498121Sgshapiro						      (const void **) &ssf);
135598121Sgshapiro# else /* SASL >= 20000 */
135664562Sgshapiro				result = sasl_getprop(conn, SASL_USERNAME,
135764562Sgshapiro						      (void **)&user);
135864562Sgshapiro				if (result != SASL_OK)
135964562Sgshapiro				{
136064562Sgshapiro					user = "";
136190792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
136290792Sgshapiro						  A_PERM,
136390792Sgshapiro						  macid("{auth_authen}"), NULL);
136464562Sgshapiro				}
136564562Sgshapiro				else
136664562Sgshapiro				{
136790792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
136890792Sgshapiro						  A_TEMP,
1369132943Sgshapiro						  macid("{auth_authen}"),
1370132943Sgshapiro						  xtextify(user, "<>\")"));
137164562Sgshapiro				}
137264562Sgshapiro
137390792Sgshapiro# if 0
137464562Sgshapiro				/* get realm? */
137564562Sgshapiro				sasl_getprop(conn, SASL_REALM, (void **) &data);
137690792Sgshapiro# endif /* 0 */
137764562Sgshapiro
137864562Sgshapiro				/* get security strength (features) */
137964562Sgshapiro				result = sasl_getprop(conn, SASL_SSF,
138064562Sgshapiro						      (void **) &ssf);
138198121Sgshapiro# endif /* SASL >= 20000 */
138264562Sgshapiro				if (result != SASL_OK)
138364562Sgshapiro				{
138490792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
138590792Sgshapiro						  A_PERM,
138690792Sgshapiro						  macid("{auth_ssf}"), "0");
138764562Sgshapiro					ssf = NULL;
138864562Sgshapiro				}
138964562Sgshapiro				else
139064562Sgshapiro				{
139164562Sgshapiro					char pbuf[8];
139264562Sgshapiro
1393168515Sgshapiro					(void) sm_snprintf(pbuf, sizeof(pbuf),
139490792Sgshapiro							   "%u", *ssf);
139590792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
139690792Sgshapiro						  A_TEMP,
139790792Sgshapiro						  macid("{auth_ssf}"), pbuf);
139864562Sgshapiro					if (tTd(95, 8))
139990792Sgshapiro						sm_dprintf("AUTH auth_ssf: %u\n",
140090792Sgshapiro							   *ssf);
140164562Sgshapiro				}
140290792Sgshapiro
140364562Sgshapiro				/*
140490792Sgshapiro				**  Only switch to encrypted connection
140564562Sgshapiro				**  if a security layer has been negotiated
140664562Sgshapiro				*/
140790792Sgshapiro
140864562Sgshapiro				if (ssf != NULL && *ssf > 0)
140964562Sgshapiro				{
1410159609Sgshapiro					int tmo;
1411159609Sgshapiro
141264562Sgshapiro					/*
141390792Sgshapiro					**  Convert I/O layer to use SASL.
141490792Sgshapiro					**  If the call fails, the connection
141590792Sgshapiro					**  is aborted.
141664562Sgshapiro					*/
141790792Sgshapiro
1418159609Sgshapiro					tmo = TimeOuts.to_datablock * 1000;
141990792Sgshapiro					if (sfdcsasl(&InChannel, &OutChannel,
1420159609Sgshapiro						     conn, tmo) == 0)
142164562Sgshapiro					{
142264562Sgshapiro						/* restart dialogue */
142364562Sgshapiro						n_helo = 0;
142494334Sgshapiro# if PIPELINING
142590792Sgshapiro						(void) sm_io_autoflush(InChannel,
142690792Sgshapiro								       OutChannel);
142794334Sgshapiro# endif /* PIPELINING */
142864562Sgshapiro					}
142964562Sgshapiro					else
143064562Sgshapiro						syserr("503 5.3.3 SASL TLS failed");
143164562Sgshapiro				}
143290792Sgshapiro
143390792Sgshapiro				/* NULL pointer ok since it's our function */
143490792Sgshapiro				if (LogLevel > 8)
143564562Sgshapiro					sm_syslog(LOG_INFO, NOQID,
1436110560Sgshapiro						  "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d",
143790792Sgshapiro						  CurSmtpClient,
143890792Sgshapiro						  shortenstring(user, 128),
143990792Sgshapiro						  auth_type, *ssf);
144064562Sgshapiro			}
144164562Sgshapiro			else if (result == SASL_CONTINUE)
144264562Sgshapiro			{
144364562Sgshapiro				len = ENC64LEN(outlen);
144464562Sgshapiro				out2 = xalloc(len);
144564562Sgshapiro				result = sasl_encode64(out, outlen, out2, len,
144690792Sgshapiro						       &out2len);
144764562Sgshapiro				if (result != SASL_OK)
144864562Sgshapiro				{
144964562Sgshapiro					/* correct code? XXX */
145064562Sgshapiro					/* 454 Temp. authentication failure */
145164562Sgshapiro					message("454 4.5.4 Internal error: unable to encode64");
145264562Sgshapiro					if (LogLevel > 5)
145364562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
1454203004Sgshapiro							  "AUTH encode64 error [%d for \"%s\"], relay=%.100s",
1455203004Sgshapiro							  result, out,
1456203004Sgshapiro							  CurSmtpClient);
145764562Sgshapiro					/* start over? */
145864562Sgshapiro					authenticating = SASL_NOT_AUTH;
145964562Sgshapiro				}
146064562Sgshapiro				else
146164562Sgshapiro				{
146264562Sgshapiro					message("334 %s", out2);
146364562Sgshapiro					if (tTd(95, 2))
146490792Sgshapiro						sm_dprintf("AUTH continue: msg='%s' len=%u\n",
146590792Sgshapiro							   out2, out2len);
146664562Sgshapiro				}
146798121Sgshapiro# if SASL >= 20000
146898121Sgshapiro				sm_free(out2);
146998121Sgshapiro# endif /* SASL >= 20000 */
147064562Sgshapiro			}
147164562Sgshapiro			else
147264562Sgshapiro			{
147364562Sgshapiro				/* not SASL_OK or SASL_CONT */
147498121Sgshapiro				message("535 5.7.0 authentication failed");
147564562Sgshapiro				if (LogLevel > 9)
147664562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
1477203004Sgshapiro						  "AUTH failure (%s): %s (%d) %s, relay=%.100s",
147864562Sgshapiro						  auth_type,
147964562Sgshapiro						  sasl_errstring(result, NULL,
148064562Sgshapiro								 NULL),
148190792Sgshapiro						  result,
148298121Sgshapiro# if SASL >= 20000
1483203004Sgshapiro						  sasl_errdetail(conn),
148498121Sgshapiro# else /* SASL >= 20000 */
1485203004Sgshapiro						  errstr == NULL ? "" : errstr,
148698121Sgshapiro# endif /* SASL >= 20000 */
1487203004Sgshapiro						  CurSmtpClient);
1488120256Sgshapiro				RESET_SASLCONN;
148964562Sgshapiro				authenticating = SASL_NOT_AUTH;
149064562Sgshapiro			}
149164562Sgshapiro		}
149264562Sgshapiro		else
149364562Sgshapiro		{
149464562Sgshapiro			/* don't want to do any of this if authenticating */
149590792Sgshapiro#endif /* SASL */
149664562Sgshapiro
149738032Speter		/* echo command to transcript */
149838032Speter		if (e->e_xfp != NULL)
149990792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
150090792Sgshapiro					     "<<< %s\n", inp);
150138032Speter
150290792Sgshapiro		if (LogLevel > 14)
150390792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
150438032Speter
150538032Speter		/* break off command */
150638032Speter		for (p = inp; isascii(*p) && isspace(*p); p++)
150738032Speter			continue;
150838032Speter		cmd = cmdbuf;
150938032Speter		while (*p != '\0' &&
151038032Speter		       !(isascii(*p) && isspace(*p)) &&
1511168515Sgshapiro		       cmd < &cmdbuf[sizeof(cmdbuf) - 2])
151238032Speter			*cmd++ = *p++;
151338032Speter		*cmd = '\0';
151438032Speter
151538032Speter		/* throw away leading whitespace */
151690792Sgshapiro		SKIP_SPACE(p);
151738032Speter
151838032Speter		/* decode command */
151964562Sgshapiro		for (c = CmdTab; c->cmd_name != NULL; c++)
152038032Speter		{
152190792Sgshapiro			if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
152238032Speter				break;
152338032Speter		}
152438032Speter
152538032Speter		/* reset errors */
152638032Speter		errno = 0;
152738032Speter
152890792Sgshapiro		/* check whether a "non-null" command has been used */
152990792Sgshapiro		switch (c->cmd_code)
153090792Sgshapiro		{
153190792Sgshapiro#if SASL
153290792Sgshapiro		  case CMDAUTH:
153390792Sgshapiro			/* avoid information leak; take first two words? */
153490792Sgshapiro			q = "AUTH";
153590792Sgshapiro			break;
153690792Sgshapiro#endif /* SASL */
153790792Sgshapiro
153890792Sgshapiro		  case CMDMAIL:
153990792Sgshapiro		  case CMDEXPN:
154090792Sgshapiro		  case CMDVRFY:
154190792Sgshapiro		  case CMDETRN:
154290792Sgshapiro			lognullconnection = false;
154390792Sgshapiro			/* FALLTHROUGH */
154490792Sgshapiro		  default:
154590792Sgshapiro			q = inp;
154690792Sgshapiro			break;
154790792Sgshapiro		}
154890792Sgshapiro
154990792Sgshapiro		if (e->e_id == NULL)
155090792Sgshapiro			sm_setproctitle(true, e, "%s: %.80s",
155190792Sgshapiro					CurSmtpClient, q);
155290792Sgshapiro		else
155390792Sgshapiro			sm_setproctitle(true, e, "%s %s: %.80s",
155490792Sgshapiro					qid_printname(e),
155590792Sgshapiro					CurSmtpClient, q);
155690792Sgshapiro
155738032Speter		/*
155838032Speter		**  Process command.
155938032Speter		**
156038032Speter		**	If we are running as a null server, return 550
156190792Sgshapiro		**	to almost everything.
156238032Speter		*/
156338032Speter
156464562Sgshapiro		if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
156538032Speter		{
156664562Sgshapiro			switch (c->cmd_code)
156738032Speter			{
156838032Speter			  case CMDQUIT:
156938032Speter			  case CMDHELO:
157038032Speter			  case CMDEHLO:
157138032Speter			  case CMDNOOP:
157264562Sgshapiro			  case CMDRSET:
1573125820Sgshapiro			  case CMDERROR:
157438032Speter				/* process normally */
157538032Speter				break;
157638032Speter
157764562Sgshapiro			  case CMDETRN:
157864562Sgshapiro				if (bitnset(D_ETRNONLY, d_flags) &&
157964562Sgshapiro				    nullserver == NULL)
158064562Sgshapiro					break;
158190792Sgshapiro				DELAY_CONN("ETRN");
158280785Sgshapiro				/* FALLTHROUGH */
158364562Sgshapiro
158438032Speter			  default:
158590792Sgshapiro#if MAXBADCOMMANDS > 0
158690792Sgshapiro				/* theoretically this could overflow */
158790792Sgshapiro				if (nullserver != NULL &&
158890792Sgshapiro				    ++n_badcmds > MAXBADCOMMANDS)
158964562Sgshapiro				{
159090792Sgshapiro					message("421 4.7.0 %s Too many bad commands; closing connection",
159190792Sgshapiro						MyHostName);
159290792Sgshapiro
159390792Sgshapiro					/* arrange to ignore send list */
159490792Sgshapiro					e->e_sendqueue = NULL;
159590792Sgshapiro					goto doquit;
159664562Sgshapiro				}
159790792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
159864562Sgshapiro				if (nullserver != NULL)
159964562Sgshapiro				{
160064562Sgshapiro					if (ISSMTPREPLY(nullserver))
160164562Sgshapiro						usrerr(nullserver);
160264562Sgshapiro					else
160390792Sgshapiro						usrerr("550 5.0.0 %s",
160490792Sgshapiro						       nullserver);
160564562Sgshapiro				}
160664562Sgshapiro				else
160764562Sgshapiro					usrerr("452 4.4.5 Insufficient disk space; try again later");
160838032Speter				continue;
160938032Speter			}
161038032Speter		}
161138032Speter
161264562Sgshapiro		switch (c->cmd_code)
161338032Speter		{
161490792Sgshapiro#if SASL
161564562Sgshapiro		  case CMDAUTH: /* sasl */
161690792Sgshapiro			DELAY_CONN("AUTH");
161790792Sgshapiro			if (!sasl_ok || n_mechs <= 0)
161864562Sgshapiro			{
161964562Sgshapiro				message("503 5.3.3 AUTH not available");
162064562Sgshapiro				break;
162164562Sgshapiro			}
162264562Sgshapiro			if (authenticating == SASL_IS_AUTH)
162364562Sgshapiro			{
162464562Sgshapiro				message("503 5.5.0 Already Authenticated");
162564562Sgshapiro				break;
162664562Sgshapiro			}
162790792Sgshapiro			if (smtp.sm_gotmail)
162864562Sgshapiro			{
162964562Sgshapiro				message("503 5.5.0 AUTH not permitted during a mail transaction");
163064562Sgshapiro				break;
163164562Sgshapiro			}
163264562Sgshapiro			if (tempfail)
163364562Sgshapiro			{
163464562Sgshapiro				if (LogLevel > 9)
163564562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1636110560Sgshapiro						  "SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)",
163764562Sgshapiro						  p, CurSmtpClient);
1638132943Sgshapiro				usrerr("454 4.3.0 Please try again later");
163964562Sgshapiro				break;
164064562Sgshapiro			}
164164562Sgshapiro
164290792Sgshapiro			ismore = false;
164364562Sgshapiro
164464562Sgshapiro			/* crude way to avoid crack attempts */
1645132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1,
1646132943Sgshapiro							true, "AUTH", e));
164764562Sgshapiro
164890792Sgshapiro			/* make sure mechanism (p) is a valid string */
164964562Sgshapiro			for (q = p; *q != '\0' && isascii(*q); q++)
165064562Sgshapiro			{
165164562Sgshapiro				if (isspace(*q))
165264562Sgshapiro				{
165364562Sgshapiro					*q = '\0';
165464562Sgshapiro					while (*++q != '\0' &&
165564562Sgshapiro					       isascii(*q) && isspace(*q))
165664562Sgshapiro						continue;
165764562Sgshapiro					*(q - 1) = '\0';
165864562Sgshapiro					ismore = (*q != '\0');
165964562Sgshapiro					break;
166064562Sgshapiro				}
166164562Sgshapiro			}
166264562Sgshapiro
166398121Sgshapiro			if (*p == '\0')
166498121Sgshapiro			{
166598121Sgshapiro				message("501 5.5.2 AUTH mechanism must be specified");
166698121Sgshapiro				break;
166798121Sgshapiro			}
166898121Sgshapiro
166964562Sgshapiro			/* check whether mechanism is available */
167064562Sgshapiro			if (iteminlist(p, mechlist, " ") == NULL)
167164562Sgshapiro			{
167298121Sgshapiro				message("504 5.3.3 AUTH mechanism %.32s not available",
167364562Sgshapiro					p);
167464562Sgshapiro				break;
167564562Sgshapiro			}
167664562Sgshapiro
1677173340Sgshapiro			/*
1678173340Sgshapiro			**  RFC 2554 4.
1679173340Sgshapiro			**  Unlike a zero-length client answer to a
1680173340Sgshapiro			**  334 reply, a zero- length initial response
1681173340Sgshapiro			**  is sent as a single equals sign ("=").
1682173340Sgshapiro			*/
1683173340Sgshapiro
1684173340Sgshapiro			if (ismore && *q == '=' && *(q + 1) == '\0')
168564562Sgshapiro			{
1686173340Sgshapiro				/* will be free()d, don't use in=""; */
1687173340Sgshapiro				in = xalloc(1);
1688173340Sgshapiro				*in = '\0';
1689173340Sgshapiro				inlen = 0;
1690173340Sgshapiro			}
1691173340Sgshapiro			else if (ismore)
1692173340Sgshapiro			{
169364562Sgshapiro				/* could this be shorter? XXX */
169498121Sgshapiro# if SASL >= 20000
169598121Sgshapiro				in = xalloc(strlen(q) + 1);
1696102528Sgshapiro				result = sasl_decode64(q, strlen(q), in,
169798121Sgshapiro						       strlen(q), &inlen);
169898121Sgshapiro# else /* SASL >= 20000 */
169990792Sgshapiro				in = sm_rpool_malloc(e->e_rpool, strlen(q));
170064562Sgshapiro				result = sasl_decode64(q, strlen(q), in,
170190792Sgshapiro						       &inlen);
170298121Sgshapiro# endif /* SASL >= 20000 */
170364562Sgshapiro				if (result != SASL_OK)
170464562Sgshapiro				{
170564562Sgshapiro					message("501 5.5.4 cannot BASE64 decode '%s'",
170664562Sgshapiro						q);
170764562Sgshapiro					if (LogLevel > 5)
170864562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
1709203004Sgshapiro							  "AUTH decode64 error [%d for \"%s\"], relay=%.100s",
1710203004Sgshapiro							  result, q,
1711203004Sgshapiro							  CurSmtpClient);
171264562Sgshapiro					/* start over? */
171364562Sgshapiro					authenticating = SASL_NOT_AUTH;
171498121Sgshapiro# if SASL >= 20000
171598121Sgshapiro					sm_free(in);
171698121Sgshapiro# endif /* SASL >= 20000 */
171764562Sgshapiro					in = NULL;
171864562Sgshapiro					inlen = 0;
171964562Sgshapiro					break;
172064562Sgshapiro				}
172164562Sgshapiro			}
172264562Sgshapiro			else
172364562Sgshapiro			{
172464562Sgshapiro				in = NULL;
172564562Sgshapiro				inlen = 0;
172664562Sgshapiro			}
172764562Sgshapiro
172864562Sgshapiro			/* see if that auth type exists */
172998121Sgshapiro# if SASL >= 20000
1730102528Sgshapiro			result = sasl_server_start(conn, p, in, inlen,
173198121Sgshapiro						   &out, &outlen);
173298121Sgshapiro			if (in != NULL)
173398121Sgshapiro				sm_free(in);
173498121Sgshapiro# else /* SASL >= 20000 */
173564562Sgshapiro			result = sasl_server_start(conn, p, in, inlen,
173664562Sgshapiro						   &out, &outlen, &errstr);
173798121Sgshapiro# endif /* SASL >= 20000 */
173864562Sgshapiro
173964562Sgshapiro			if (result != SASL_OK && result != SASL_CONTINUE)
174064562Sgshapiro			{
174198121Sgshapiro				message("535 5.7.0 authentication failed");
174264562Sgshapiro				if (LogLevel > 9)
174364562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
1744203004Sgshapiro						  "AUTH failure (%s): %s (%d) %s, relay=%.100s",
174564562Sgshapiro						  p,
174664562Sgshapiro						  sasl_errstring(result, NULL,
174764562Sgshapiro								 NULL),
174890792Sgshapiro						  result,
174998121Sgshapiro# if SASL >= 20000
1750203004Sgshapiro						  sasl_errdetail(conn),
175198121Sgshapiro# else /* SASL >= 20000 */
1752203004Sgshapiro						  errstr,
175398121Sgshapiro# endif /* SASL >= 20000 */
1754203004Sgshapiro						  CurSmtpClient);
1755120256Sgshapiro				RESET_SASLCONN;
175664562Sgshapiro				break;
175764562Sgshapiro			}
175864562Sgshapiro			auth_type = newstr(p);
175964562Sgshapiro
176064562Sgshapiro			if (result == SASL_OK)
176164562Sgshapiro			{
176264562Sgshapiro				/* ugly, but same code */
176364562Sgshapiro				goto authenticated;
176464562Sgshapiro				/* authenticated by the initial response */
176564562Sgshapiro			}
176664562Sgshapiro
176764562Sgshapiro			/* len is at least 2 */
176864562Sgshapiro			len = ENC64LEN(outlen);
176964562Sgshapiro			out2 = xalloc(len);
177064562Sgshapiro			result = sasl_encode64(out, outlen, out2, len,
177190792Sgshapiro					       &out2len);
177264562Sgshapiro
177364562Sgshapiro			if (result != SASL_OK)
177464562Sgshapiro			{
177564562Sgshapiro				message("454 4.5.4 Temporary authentication failure");
177664562Sgshapiro				if (LogLevel > 5)
177764562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
177890792Sgshapiro						  "AUTH encode64 error [%d for \"%s\"]",
177964562Sgshapiro						  result, out);
178064562Sgshapiro
178164562Sgshapiro				/* start over? */
178264562Sgshapiro				authenticating = SASL_NOT_AUTH;
1783120256Sgshapiro				RESET_SASLCONN;
178464562Sgshapiro			}
178564562Sgshapiro			else
178664562Sgshapiro			{
178764562Sgshapiro				message("334 %s", out2);
178864562Sgshapiro				authenticating = SASL_PROC_AUTH;
178964562Sgshapiro			}
179098121Sgshapiro# if SASL >= 20000
179198121Sgshapiro			sm_free(out2);
179298121Sgshapiro# endif /* SASL >= 20000 */
179364562Sgshapiro			break;
179490792Sgshapiro#endif /* SASL */
179564562Sgshapiro
179690792Sgshapiro#if STARTTLS
179764562Sgshapiro		  case CMDSTLS: /* starttls */
179890792Sgshapiro			DELAY_CONN("STARTTLS");
179964562Sgshapiro			if (*p != '\0')
180064562Sgshapiro			{
180164562Sgshapiro				message("501 5.5.2 Syntax error (no parameters allowed)");
180264562Sgshapiro				break;
180364562Sgshapiro			}
180490792Sgshapiro			if (!bitset(SRV_OFFER_TLS, features))
180564562Sgshapiro			{
180664562Sgshapiro				message("503 5.5.0 TLS not available");
180764562Sgshapiro				break;
180864562Sgshapiro			}
180977349Sgshapiro			if (!tls_ok_srv)
181064562Sgshapiro			{
181164562Sgshapiro				message("454 4.3.3 TLS not available after start");
181264562Sgshapiro				break;
181364562Sgshapiro			}
181490792Sgshapiro			if (smtp.sm_gotmail)
181564562Sgshapiro			{
181664562Sgshapiro				message("503 5.5.0 TLS not permitted during a mail transaction");
181764562Sgshapiro				break;
181864562Sgshapiro			}
181964562Sgshapiro			if (tempfail)
182064562Sgshapiro			{
182164562Sgshapiro				if (LogLevel > 9)
182264562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1823110560Sgshapiro						  "SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)",
182464562Sgshapiro						  p, CurSmtpClient);
1825132943Sgshapiro				usrerr("454 4.7.0 Please try again later");
182664562Sgshapiro				break;
182764562Sgshapiro			}
182890792Sgshapiro  starttls:
1829223067Sgshapiro# if USE_OPENSSL_ENGINE
1830223067Sgshapiro			if (!SSLEngineInitialized)
1831223067Sgshapiro			{
1832223067Sgshapiro				if (!SSL_set_engine(NULL))
1833223067Sgshapiro				{
1834223067Sgshapiro					sm_syslog(LOG_ERR, NOQID,
1835223067Sgshapiro						  "STARTTLS=server, SSL_set_engine=failed");
1836223067Sgshapiro					tls_ok_srv = false;
1837223067Sgshapiro					message("454 4.3.3 TLS not available right now");
1838223067Sgshapiro					break;
1839223067Sgshapiro				}
1840223067Sgshapiro				else
1841223067Sgshapiro					SSLEngineInitialized = true;
1842223067Sgshapiro			}
1843223067Sgshapiro# endif /* USE_OPENSSL_ENGINE */
184464562Sgshapiro# if TLS_NO_RSA
184564562Sgshapiro			/*
184664562Sgshapiro			**  XXX do we need a temp key ?
184764562Sgshapiro			*/
184864562Sgshapiro# else /* TLS_NO_RSA */
184964562Sgshapiro# endif /* TLS_NO_RSA */
185090792Sgshapiro
185190792Sgshapiro# if TLS_VRFY_PER_CTX
185290792Sgshapiro			/*
185390792Sgshapiro			**  Note: this sets the verification globally
185490792Sgshapiro			**  (per SSL_CTX)
185590792Sgshapiro			**  it's ok since it applies only to one transaction
185690792Sgshapiro			*/
185790792Sgshapiro
185890792Sgshapiro			TLS_VERIFY_CLIENT();
185990792Sgshapiro# endif /* TLS_VRFY_PER_CTX */
186090792Sgshapiro
186164562Sgshapiro			if (srv_ssl != NULL)
186264562Sgshapiro				SSL_clear(srv_ssl);
186364562Sgshapiro			else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
186464562Sgshapiro			{
186564562Sgshapiro				message("454 4.3.3 TLS not available: error generating SSL handle");
1866120256Sgshapiro				if (LogLevel > 8)
1867249865Sgshapiro					tlslogerr(LOG_WARNING, "server");
186890792Sgshapiro				goto tls_done;
186964562Sgshapiro			}
187090792Sgshapiro
187190792Sgshapiro# if !TLS_VRFY_PER_CTX
187290792Sgshapiro			/*
187390792Sgshapiro			**  this could be used if it were possible to set
187490792Sgshapiro			**  verification per SSL (connection)
187590792Sgshapiro			**  not just per SSL_CTX (global)
187690792Sgshapiro			*/
187790792Sgshapiro
187890792Sgshapiro			TLS_VERIFY_CLIENT();
187990792Sgshapiro# endif /* !TLS_VRFY_PER_CTX */
188090792Sgshapiro
188190792Sgshapiro			rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
188290792Sgshapiro			wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
188390792Sgshapiro
188466494Sgshapiro			if (rfd < 0 || wfd < 0 ||
188566494Sgshapiro			    SSL_set_rfd(srv_ssl, rfd) <= 0 ||
188666494Sgshapiro			    SSL_set_wfd(srv_ssl, wfd) <= 0)
188764562Sgshapiro			{
188864562Sgshapiro				message("454 4.3.3 TLS not available: error set fd");
188964562Sgshapiro				SSL_free(srv_ssl);
189064562Sgshapiro				srv_ssl = NULL;
189190792Sgshapiro				goto tls_done;
189264562Sgshapiro			}
189390792Sgshapiro			if (!smtps)
189490792Sgshapiro				message("220 2.0.0 Ready to start TLS");
189590792Sgshapiro# if PIPELINING
189690792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
189790792Sgshapiro# endif /* PIPELINING */
189890792Sgshapiro
189964562Sgshapiro			SSL_set_accept_state(srv_ssl);
190064562Sgshapiro
190164562Sgshapiro#  define SSL_ACC(s)	SSL_accept(s)
190290792Sgshapiro
190390792Sgshapiro			tlsstart = curtime();
190490792Sgshapiro  ssl_retry:
190564562Sgshapiro			if ((r = SSL_ACC(srv_ssl)) <= 0)
190664562Sgshapiro			{
1907157001Sgshapiro				int i, ssl_err;
190864562Sgshapiro
1909157001Sgshapiro				ssl_err = SSL_get_error(srv_ssl, r);
1910157001Sgshapiro				i = tls_retry(srv_ssl, rfd, wfd, tlsstart,
1911157001Sgshapiro						TimeOuts.to_starttls, ssl_err,
1912157001Sgshapiro						"server");
1913157001Sgshapiro				if (i > 0)
1914157001Sgshapiro					goto ssl_retry;
191590792Sgshapiro
191664562Sgshapiro				if (LogLevel > 5)
191764562Sgshapiro				{
1918244928Sgshapiro					unsigned long l;
1919244928Sgshapiro					const char *sr;
1920244928Sgshapiro
1921244928Sgshapiro					l = ERR_peek_error();
1922244928Sgshapiro					sr = ERR_reason_error_string(l);
192390792Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
1924244928Sgshapiro						  "STARTTLS=server, error: accept failed=%d, reason=%s, SSL_error=%d, errno=%d, retry=%d, relay=%.100s",
1925244928Sgshapiro						  r, sr == NULL ? "unknown"
1926244928Sgshapiro								: sr,
1927244928Sgshapiro						  ssl_err, errno, i,
1928203004Sgshapiro						  CurSmtpClient);
1929244928Sgshapiro					if (LogLevel > 9)
1930249865Sgshapiro						tlslogerr(LOG_WARNING, "server");
193164562Sgshapiro				}
193290792Sgshapiro				tls_ok_srv = false;
193364562Sgshapiro				SSL_free(srv_ssl);
193464562Sgshapiro				srv_ssl = NULL;
193564562Sgshapiro
193664562Sgshapiro				/*
193764562Sgshapiro				**  according to the next draft of
193864562Sgshapiro				**  RFC 2487 the connection should be dropped
193964562Sgshapiro				*/
194064562Sgshapiro
194164562Sgshapiro				/* arrange to ignore any current send list */
194264562Sgshapiro				e->e_sendqueue = NULL;
194364562Sgshapiro				goto doquit;
194464562Sgshapiro			}
194564562Sgshapiro
194664562Sgshapiro			/* ignore return code for now, it's in {verify} */
194790792Sgshapiro			(void) tls_get_info(srv_ssl, true,
194890792Sgshapiro					    CurSmtpClient,
194990792Sgshapiro					    &BlankEnvelope.e_macro,
195090792Sgshapiro					    bitset(SRV_VRFY_CLT, features));
195164562Sgshapiro
195264562Sgshapiro			/*
195364562Sgshapiro			**  call Stls_client to find out whether
195464562Sgshapiro			**  to accept the connection from the client
195564562Sgshapiro			*/
195664562Sgshapiro
195764562Sgshapiro			saveQuickAbort = QuickAbort;
195864562Sgshapiro			saveSuprErrs = SuprErrs;
195990792Sgshapiro			SuprErrs = true;
196090792Sgshapiro			QuickAbort = false;
196164562Sgshapiro			if (rscheck("tls_client",
196290792Sgshapiro				     macvalue(macid("{verify}"), e),
1963102528Sgshapiro				     "STARTTLS", e,
1964102528Sgshapiro				     RSF_RMCOMM|RSF_COUNT,
1965168515Sgshapiro				     5, NULL, NOQID, NULL) != EX_OK ||
196690792Sgshapiro			    Errors > 0)
196764562Sgshapiro			{
196864562Sgshapiro				extern char MsgBuf[];
196964562Sgshapiro
197064562Sgshapiro				if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
197164562Sgshapiro					nullserver = newstr(MsgBuf);
197264562Sgshapiro				else
197364562Sgshapiro					nullserver = "503 5.7.0 Authentication required.";
197464562Sgshapiro			}
197564562Sgshapiro			QuickAbort = saveQuickAbort;
197664562Sgshapiro			SuprErrs = saveSuprErrs;
197764562Sgshapiro
197890792Sgshapiro			tls_ok_srv = false;	/* don't offer STARTTLS again */
197964562Sgshapiro			n_helo = 0;
198090792Sgshapiro# if SASL
198164562Sgshapiro			if (sasl_ok)
198264562Sgshapiro			{
1983132943Sgshapiro				int cipher_bits;
1984132943Sgshapiro				bool verified;
1985132943Sgshapiro				char *s, *v, *c;
198664562Sgshapiro
198790792Sgshapiro				s = macvalue(macid("{cipher_bits}"), e);
1988132943Sgshapiro				v = macvalue(macid("{verify}"), e);
1989132943Sgshapiro				c = macvalue(macid("{cert_subject}"), e);
1990132943Sgshapiro				verified = (v != NULL && strcmp(v, "OK") == 0);
1991132943Sgshapiro				if (s != NULL && (cipher_bits = atoi(s)) > 0)
1992132943Sgshapiro				{
199398121Sgshapiro#  if SASL >= 20000
1994132943Sgshapiro					ext_ssf = cipher_bits;
1995132943Sgshapiro					auth_id = verified ? c : NULL;
1996132943Sgshapiro					sasl_ok = ((sasl_setprop(conn,
1997132943Sgshapiro							SASL_SSF_EXTERNAL,
1998132943Sgshapiro							&ext_ssf) == SASL_OK) &&
1999132943Sgshapiro						   (sasl_setprop(conn,
2000132943Sgshapiro							SASL_AUTH_EXTERNAL,
2001132943Sgshapiro							auth_id) == SASL_OK));
200298121Sgshapiro#  else /* SASL >= 20000 */
2003132943Sgshapiro					ext_ssf.ssf = cipher_bits;
2004132943Sgshapiro					ext_ssf.auth_id = verified ? c : NULL;
2005132943Sgshapiro					sasl_ok = sasl_setprop(conn,
2006132943Sgshapiro							SASL_SSF_EXTERNAL,
2007132943Sgshapiro							&ext_ssf) == SASL_OK;
200898121Sgshapiro#  endif /* SASL >= 20000 */
200964562Sgshapiro					mechlist = NULL;
201064562Sgshapiro					if (sasl_ok)
201164562Sgshapiro						n_mechs = saslmechs(conn,
201264562Sgshapiro								    &mechlist);
201364562Sgshapiro				}
201464562Sgshapiro			}
201590792Sgshapiro# endif /* SASL */
201664562Sgshapiro
201764562Sgshapiro			/* switch to secure connection */
201890792Sgshapiro			if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
201990792Sgshapiro			{
202090792Sgshapiro				tls_active = true;
202190792Sgshapiro# if PIPELINING
202290792Sgshapiro				(void) sm_io_autoflush(InChannel, OutChannel);
202390792Sgshapiro# endif /* PIPELINING */
202490792Sgshapiro			}
202564562Sgshapiro			else
202664562Sgshapiro			{
202764562Sgshapiro				/*
202864562Sgshapiro				**  XXX this is an internal error
202964562Sgshapiro				**  how to deal with it?
203064562Sgshapiro				**  we can't generate an error message
203164562Sgshapiro				**  since the other side switched to an
203264562Sgshapiro				**  encrypted layer, but we could not...
203364562Sgshapiro				**  just "hang up"?
203464562Sgshapiro				*/
203590792Sgshapiro
203664562Sgshapiro				nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
203790792Sgshapiro				syserr("STARTTLS: can't switch to encrypted layer");
203864562Sgshapiro			}
203990792Sgshapiro		  tls_done:
204090792Sgshapiro			if (smtps)
204190792Sgshapiro			{
204290792Sgshapiro				if (tls_active)
204390792Sgshapiro					goto greeting;
204490792Sgshapiro				else
204590792Sgshapiro					goto doquit;
204690792Sgshapiro			}
204764562Sgshapiro			break;
204890792Sgshapiro#endif /* STARTTLS */
204964562Sgshapiro
205038032Speter		  case CMDHELO:		/* hello -- introduce yourself */
205138032Speter		  case CMDEHLO:		/* extended hello */
205290792Sgshapiro			DELAY_CONN("EHLO");
205364562Sgshapiro			if (c->cmd_code == CMDEHLO)
205438032Speter			{
205538032Speter				protocol = "ESMTP";
205638032Speter				SmtpPhase = "server EHLO";
205738032Speter			}
205838032Speter			else
205938032Speter			{
206038032Speter				protocol = "SMTP";
206138032Speter				SmtpPhase = "server HELO";
206238032Speter			}
206338032Speter
206438032Speter			/* avoid denial-of-service */
2065132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
2066132943Sgshapiro							true, "HELO/EHLO", e));
206738032Speter
206890792Sgshapiro#if 0
206990792Sgshapiro			/* RFC2821 4.1.4 allows duplicate HELO/EHLO */
207038032Speter			/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
207138032Speter			if (gothello)
207238032Speter			{
207338032Speter				usrerr("503 %s Duplicate HELO/EHLO",
207473188Sgshapiro				       MyHostName);
207538032Speter				break;
207638032Speter			}
207790792Sgshapiro#endif /* 0 */
207838032Speter
207938032Speter			/* check for valid domain name (re 1123 5.2.5) */
208038032Speter			if (*p == '\0' && !AllowBogusHELO)
208138032Speter			{
208238032Speter				usrerr("501 %s requires domain address",
208338032Speter					cmdbuf);
208438032Speter				break;
208538032Speter			}
208638032Speter
208738032Speter			/* check for long domain name (hides Received: info) */
208838032Speter			if (strlen(p) > MAXNAME)
208938032Speter			{
209038032Speter				usrerr("501 Invalid domain name");
209164562Sgshapiro				if (LogLevel > 9)
209264562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
2093110560Sgshapiro						  "invalid domain name (too long) from %s",
209464562Sgshapiro						  CurSmtpClient);
209538032Speter				break;
209638032Speter			}
209738032Speter
209898121Sgshapiro			ok = true;
209938032Speter			for (q = p; *q != '\0'; q++)
210038032Speter			{
210138032Speter				if (!isascii(*q))
210238032Speter					break;
210338032Speter				if (isalnum(*q))
210438032Speter					continue;
210538032Speter				if (isspace(*q))
210638032Speter				{
210738032Speter					*q = '\0';
210898121Sgshapiro
210998121Sgshapiro					/* only complain if strict check */
211098121Sgshapiro					ok = AllowBogusHELO;
2111141858Sgshapiro
2112141858Sgshapiro					/* allow trailing whitespace */
2113141858Sgshapiro					while (!ok && *++q != '\0' &&
2114141858Sgshapiro					       isspace(*q))
2115141858Sgshapiro						;
2116141858Sgshapiro					if (*q == '\0')
2117141858Sgshapiro						ok = true;
211838032Speter					break;
211938032Speter				}
2120120256Sgshapiro				if (strchr("[].-_#:", *q) == NULL)
212138032Speter					break;
212238032Speter			}
212364562Sgshapiro
212498121Sgshapiro			if (*q == '\0' && ok)
212538032Speter			{
212638032Speter				q = "pleased to meet you";
212790792Sgshapiro				sendinghost = sm_strdup_x(p);
212838032Speter			}
212938032Speter			else if (!AllowBogusHELO)
213038032Speter			{
213138032Speter				usrerr("501 Invalid domain name");
213264562Sgshapiro				if (LogLevel > 9)
213364562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
2134110560Sgshapiro						  "invalid domain name (%s) from %.100s",
213564562Sgshapiro						  p, CurSmtpClient);
213638032Speter				break;
213738032Speter			}
213838032Speter			else
213938032Speter			{
214038032Speter				q = "accepting invalid domain name";
214138032Speter			}
214238032Speter
2143157001Sgshapiro			if (gothello || smtp.sm_gotmail)
214490792Sgshapiro				CLEAR_STATE(cmdbuf);
214590792Sgshapiro
214690792Sgshapiro#if MILTER
214790792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
214890792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
214964562Sgshapiro			{
215064562Sgshapiro				char state;
215164562Sgshapiro				char *response;
215264562Sgshapiro
215364562Sgshapiro				response = milter_helo(p, e, &state);
215464562Sgshapiro				switch (state)
215564562Sgshapiro				{
215664562Sgshapiro				  case SMFIR_REJECT:
215790792Sgshapiro					if (MilterLogLevel > 3)
215890792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
215990792Sgshapiro							  "Milter: helo=%s, reject=Command rejected",
216090792Sgshapiro							  p);
216164562Sgshapiro					nullserver = "Command rejected";
216290792Sgshapiro					smtp.sm_milterize = false;
216364562Sgshapiro					break;
216464562Sgshapiro
216564562Sgshapiro				  case SMFIR_TEMPFAIL:
216690792Sgshapiro					if (MilterLogLevel > 3)
216790792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
216890792Sgshapiro							  "Milter: helo=%s, reject=%s",
216990792Sgshapiro							  p, MSG_TEMPFAIL);
217090792Sgshapiro					tempfail = true;
217190792Sgshapiro					smtp.sm_milterize = false;
217264562Sgshapiro					break;
2173157001Sgshapiro
2174168515Sgshapiro				  case SMFIR_REPLYCODE:
2175157001Sgshapiro					if (MilterLogLevel > 3)
2176157001Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
2177168515Sgshapiro							  "Milter: helo=%s, reject=%s",
2178168515Sgshapiro							  p, response);
2179168515Sgshapiro					if (strncmp(response, "421 ", 4) != 0
2180168515Sgshapiro					    && strncmp(response, "421-", 4) != 0)
2181168515Sgshapiro					{
2182168515Sgshapiro						nullserver = newstr(response);
2183168515Sgshapiro						smtp.sm_milterize = false;
2184168515Sgshapiro						break;
2185168515Sgshapiro					}
2186168515Sgshapiro					/* FALLTHROUGH */
2187168515Sgshapiro
2188168515Sgshapiro				  case SMFIR_SHUTDOWN:
2189168515Sgshapiro					if (MilterLogLevel > 3 &&
2190168515Sgshapiro					    response == NULL)
2191168515Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
2192159609Sgshapiro							  "Milter: helo=%s, reject=421 4.7.0 %s closing connection",
2193157001Sgshapiro							  p, MyHostName);
2194157001Sgshapiro					tempfail = true;
2195157001Sgshapiro					smtp.sm_milterize = false;
2196168515Sgshapiro					if (response != NULL)
2197168515Sgshapiro						usrerr(response);
2198168515Sgshapiro					else
2199168515Sgshapiro						message("421 4.7.0 %s closing connection",
2200168515Sgshapiro							MyHostName);
2201157001Sgshapiro					/* arrange to ignore send list */
2202157001Sgshapiro					e->e_sendqueue = NULL;
2203168515Sgshapiro					lognullconnection = false;
2204157001Sgshapiro					goto doquit;
220564562Sgshapiro				}
220690792Sgshapiro				if (response != NULL)
220790792Sgshapiro					sm_free(response);
220890792Sgshapiro
220990792Sgshapiro				/*
221090792Sgshapiro				**  If quarantining by a connect/ehlo action,
221190792Sgshapiro				**  save between messages
221290792Sgshapiro				*/
221390792Sgshapiro
221490792Sgshapiro				if (smtp.sm_quarmsg == NULL &&
221590792Sgshapiro				    e->e_quarmsg != NULL)
221690792Sgshapiro					smtp.sm_quarmsg = newstr(e->e_quarmsg);
221764562Sgshapiro			}
221890792Sgshapiro#endif /* MILTER */
221990792Sgshapiro			gothello = true;
222064562Sgshapiro
222138032Speter			/* print HELO response message */
222264562Sgshapiro			if (c->cmd_code != CMDEHLO)
222338032Speter			{
222438032Speter				message("250 %s Hello %s, %s",
222538032Speter					MyHostName, CurSmtpClient, q);
222638032Speter				break;
222738032Speter			}
222838032Speter
222938032Speter			message("250-%s Hello %s, %s",
223038032Speter				MyHostName, CurSmtpClient, q);
223138032Speter
223264562Sgshapiro			/* offer ENHSC even for nullserver */
223364562Sgshapiro			if (nullserver != NULL)
223464562Sgshapiro			{
223564562Sgshapiro				message("250 ENHANCEDSTATUSCODES");
223664562Sgshapiro				break;
223764562Sgshapiro			}
223864562Sgshapiro
223966494Sgshapiro			/*
224066494Sgshapiro			**  print EHLO features list
224166494Sgshapiro			**
224266494Sgshapiro			**  Note: If you change this list,
224390792Sgshapiro			**	  remember to update 'helpfile'
224466494Sgshapiro			*/
224566494Sgshapiro
224664562Sgshapiro			message("250-ENHANCEDSTATUSCODES");
224790792Sgshapiro#if PIPELINING
224890792Sgshapiro			if (bitset(SRV_OFFER_PIPE, features))
224990792Sgshapiro				message("250-PIPELINING");
225090792Sgshapiro#endif /* PIPELINING */
225190792Sgshapiro			if (bitset(SRV_OFFER_EXPN, features))
225238032Speter			{
225338032Speter				message("250-EXPN");
225490792Sgshapiro				if (bitset(SRV_OFFER_VERB, features))
225538032Speter					message("250-VERB");
225638032Speter			}
225790792Sgshapiro#if MIME8TO7
225838032Speter			message("250-8BITMIME");
225990792Sgshapiro#endif /* MIME8TO7 */
226038032Speter			if (MaxMessageSize > 0)
226138032Speter				message("250-SIZE %ld", MaxMessageSize);
226238032Speter			else
226338032Speter				message("250-SIZE");
226490792Sgshapiro#if DSN
226590792Sgshapiro			if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
226638032Speter				message("250-DSN");
226790792Sgshapiro#endif /* DSN */
226890792Sgshapiro			if (bitset(SRV_OFFER_ETRN, features))
226938032Speter				message("250-ETRN");
227090792Sgshapiro#if SASL
227164562Sgshapiro			if (sasl_ok && mechlist != NULL && *mechlist != '\0')
227264562Sgshapiro				message("250-AUTH %s", mechlist);
227390792Sgshapiro#endif /* SASL */
227490792Sgshapiro#if STARTTLS
2275223067Sgshapiro			if (tls_ok_srv && bitset(SRV_OFFER_TLS, features))
227664562Sgshapiro				message("250-STARTTLS");
227790792Sgshapiro#endif /* STARTTLS */
227890792Sgshapiro			if (DeliverByMin > 0)
227990792Sgshapiro				message("250-DELIVERBY %ld",
228090792Sgshapiro					(long) DeliverByMin);
228190792Sgshapiro			else if (DeliverByMin == 0)
228290792Sgshapiro				message("250-DELIVERBY");
228390792Sgshapiro
228490792Sgshapiro			/* < 0: no deliver-by */
228590792Sgshapiro
228638032Speter			message("250 HELP");
228738032Speter			break;
228838032Speter
228938032Speter		  case CMDMAIL:		/* mail -- designate sender */
229038032Speter			SmtpPhase = "server MAIL";
229190792Sgshapiro			DELAY_CONN("MAIL");
229238032Speter
229338032Speter			/* check for validity of this command */
229438032Speter			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
229538032Speter			{
229664562Sgshapiro				usrerr("503 5.0.0 Polite people say HELO first");
229738032Speter				break;
229838032Speter			}
229990792Sgshapiro			if (smtp.sm_gotmail)
230038032Speter			{
230164562Sgshapiro				usrerr("503 5.5.0 Sender already specified");
230238032Speter				break;
230338032Speter			}
230490792Sgshapiro#if SASL
230590792Sgshapiro			if (bitset(SRV_REQ_AUTH, features) &&
230664562Sgshapiro			    authenticating != SASL_IS_AUTH)
230764562Sgshapiro			{
230864562Sgshapiro				usrerr("530 5.7.0 Authentication required");
230964562Sgshapiro				break;
231064562Sgshapiro			}
231190792Sgshapiro#endif /* SASL */
231238032Speter
231364562Sgshapiro			p = skipword(p, "from");
231464562Sgshapiro			if (p == NULL)
231564562Sgshapiro				break;
231664562Sgshapiro			if (tempfail)
231764562Sgshapiro			{
231864562Sgshapiro				if (LogLevel > 9)
231964562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2320110560Sgshapiro						  "SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)",
232164562Sgshapiro						  p, CurSmtpClient);
232290792Sgshapiro				usrerr(MSG_TEMPFAIL);
232364562Sgshapiro				break;
232464562Sgshapiro			}
232564562Sgshapiro
232638032Speter			/* make sure we know who the sending host is */
232738032Speter			if (sendinghost == NULL)
232838032Speter				sendinghost = peerhostname;
232938032Speter
233038032Speter
233190792Sgshapiro#if SM_HEAP_CHECK
233290792Sgshapiro			if (sm_debug_active(&DebugLeakSmtp, 1))
233390792Sgshapiro			{
233490792Sgshapiro				sm_heap_newgroup();
233590792Sgshapiro				sm_dprintf("smtp() heap group #%d\n",
233690792Sgshapiro					sm_heap_group());
233790792Sgshapiro			}
233890792Sgshapiro#endif /* SM_HEAP_CHECK */
233964562Sgshapiro
234038032Speter			if (Errors > 0)
234190792Sgshapiro				goto undo_no_pm;
234238032Speter			if (!gothello)
234338032Speter			{
234490792Sgshapiro				auth_warning(e, "%s didn't use HELO protocol",
234590792Sgshapiro					     CurSmtpClient);
234638032Speter			}
234790792Sgshapiro#ifdef PICKY_HELO_CHECK
234890792Sgshapiro			if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
234990792Sgshapiro			    (sm_strcasecmp(peerhostname, "localhost") != 0 ||
235090792Sgshapiro			     sm_strcasecmp(sendinghost, MyHostName) != 0))
235138032Speter			{
235238032Speter				auth_warning(e, "Host %s claimed to be %s",
235390792Sgshapiro					     CurSmtpClient, sendinghost);
235438032Speter			}
235590792Sgshapiro#endif /* PICKY_HELO_CHECK */
235638032Speter
235738032Speter			if (protocol == NULL)
235838032Speter				protocol = "SMTP";
235990792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'r', protocol);
236090792Sgshapiro			macdefine(&e->e_macro, A_PERM, 's', sendinghost);
236164562Sgshapiro
236238032Speter			if (Errors > 0)
236390792Sgshapiro				goto undo_no_pm;
236490792Sgshapiro			smtp.sm_nrcpts = 0;
236590792Sgshapiro			n_badrcpts = 0;
236690792Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
236790792Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
2368132943Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"),
2369132943Sgshapiro				"0");
237064562Sgshapiro			e->e_flags |= EF_CLRQUEUE;
237190792Sgshapiro			sm_setproctitle(true, e, "%s %s: %.80s",
237264562Sgshapiro					qid_printname(e),
237364562Sgshapiro					CurSmtpClient, inp);
237438032Speter
237590792Sgshapiro			/* do the processing */
237690792Sgshapiro		    SM_TRY
237790792Sgshapiro		    {
237894334Sgshapiro			extern char *FullName;
237994334Sgshapiro
238090792Sgshapiro			QuickAbort = true;
238194334Sgshapiro			SM_FREE_CLR(FullName);
238264562Sgshapiro
238338032Speter			/* must parse sender first */
238438032Speter			delimptr = NULL;
238590792Sgshapiro			setsender(p, e, &delimptr, ' ', false);
238638032Speter			if (delimptr != NULL && *delimptr != '\0')
238738032Speter				*delimptr++ = '\0';
238838032Speter			if (Errors > 0)
238990792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
239038032Speter
239164562Sgshapiro			/* Successfully set e_from, allow logging */
239264562Sgshapiro			e->e_flags |= EF_LOGSENDER;
239338032Speter
239464562Sgshapiro			/* put resulting triple from parseaddr() into macros */
239564562Sgshapiro			if (e->e_from.q_mailer != NULL)
239690792Sgshapiro				 macdefine(&e->e_macro, A_PERM,
239790792Sgshapiro					macid("{mail_mailer}"),
239890792Sgshapiro					e->e_from.q_mailer->m_name);
239964562Sgshapiro			else
240090792Sgshapiro				 macdefine(&e->e_macro, A_PERM,
240190792Sgshapiro					macid("{mail_mailer}"), NULL);
240264562Sgshapiro			if (e->e_from.q_host != NULL)
240390792Sgshapiro				macdefine(&e->e_macro, A_PERM,
240490792Sgshapiro					macid("{mail_host}"),
240590792Sgshapiro					e->e_from.q_host);
240664562Sgshapiro			else
240790792Sgshapiro				macdefine(&e->e_macro, A_PERM,
240890792Sgshapiro					macid("{mail_host}"), "localhost");
240964562Sgshapiro			if (e->e_from.q_user != NULL)
241090792Sgshapiro				macdefine(&e->e_macro, A_PERM,
241190792Sgshapiro					macid("{mail_addr}"),
241290792Sgshapiro					e->e_from.q_user);
241364562Sgshapiro			else
241490792Sgshapiro				macdefine(&e->e_macro, A_PERM,
241590792Sgshapiro					macid("{mail_addr}"), NULL);
241664562Sgshapiro			if (Errors > 0)
241790792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
241864562Sgshapiro
241938032Speter			/* check for possible spoofing */
242038032Speter			if (RealUid != 0 && OpMode == MD_SMTP &&
242138032Speter			    !wordinclass(RealUserName, 't') &&
242264562Sgshapiro			    (!bitnset(M_LOCALMAILER,
242364562Sgshapiro				      e->e_from.q_mailer->m_flags) ||
242464562Sgshapiro			     strcmp(e->e_from.q_user, RealUserName) != 0))
242538032Speter			{
242638032Speter				auth_warning(e, "%s owned process doing -bs",
242738032Speter					RealUserName);
242838032Speter			}
242938032Speter
2430120256Sgshapiro			/* reset to default value */
2431168515Sgshapiro			SevenBitInput = SevenBitInput_Saved;
2432120256Sgshapiro
243338032Speter			/* now parse ESMTP arguments */
243438032Speter			e->e_msgsize = 0;
243564562Sgshapiro			addr = p;
2436168515Sgshapiro			parse_esmtp_args(e, NULL, p, delimptr, "MAIL", args,
2437168515Sgshapiro					mail_esmtp_args);
243838032Speter			if (Errors > 0)
243990792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
244038032Speter
244190792Sgshapiro#if SASL
244290792Sgshapiro# if _FFR_AUTH_PASSING
244390792Sgshapiro			/* set the default AUTH= if the sender didn't */
244490792Sgshapiro			if (e->e_auth_param == NULL)
244590792Sgshapiro			{
244690792Sgshapiro				/* XXX only do this for an MSA? */
244790792Sgshapiro				e->e_auth_param = macvalue(macid("{auth_authen}"),
244890792Sgshapiro							   e);
244990792Sgshapiro				if (e->e_auth_param == NULL)
245090792Sgshapiro					e->e_auth_param = "<>";
245190792Sgshapiro
245290792Sgshapiro				/*
245390792Sgshapiro				**  XXX should we invoke Strust_auth now?
245490792Sgshapiro				**  authorizing as the client that just
245590792Sgshapiro				**  authenticated, so we'll trust implicitly
245690792Sgshapiro				*/
245790792Sgshapiro			}
245890792Sgshapiro# endif /* _FFR_AUTH_PASSING */
245990792Sgshapiro#endif /* SASL */
246090792Sgshapiro
246164562Sgshapiro			/* do config file checking of the sender */
246290792Sgshapiro			macdefine(&e->e_macro, A_PERM,
246390792Sgshapiro				macid("{addr_type}"), "e s");
246490792Sgshapiro#if _FFR_MAIL_MACRO
246590792Sgshapiro			/* make the "real" sender address available */
246690792Sgshapiro			macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
246790792Sgshapiro				  e->e_from.q_paddr);
246890792Sgshapiro#endif /* _FFR_MAIL_MACRO */
246964562Sgshapiro			if (rscheck("check_mail", addr,
2470102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
2471168515Sgshapiro				    NULL, e->e_id, NULL) != EX_OK ||
247264562Sgshapiro			    Errors > 0)
247390792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
247490792Sgshapiro			macdefine(&e->e_macro, A_PERM,
247590792Sgshapiro				  macid("{addr_type}"), NULL);
247664562Sgshapiro
247766494Sgshapiro			if (MaxMessageSize > 0 &&
247877349Sgshapiro			    (e->e_msgsize > MaxMessageSize ||
247977349Sgshapiro			     e->e_msgsize < 0))
248038032Speter			{
248164562Sgshapiro				usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
248238032Speter					MaxMessageSize);
248390792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
248438032Speter			}
248564562Sgshapiro
248690792Sgshapiro			/*
248790792Sgshapiro			**  XXX always check whether there is at least one fs
248890792Sgshapiro			**  with enough space?
248990792Sgshapiro			**  However, this may not help much: the queue group
249090792Sgshapiro			**  selection may later on select a FS that hasn't
249190792Sgshapiro			**  enough space.
249290792Sgshapiro			*/
249390792Sgshapiro
249490792Sgshapiro			if ((NumFileSys == 1 || NumQueue == 1) &&
249590792Sgshapiro			    !enoughdiskspace(e->e_msgsize, e)
249690792Sgshapiro#if _FFR_ANY_FREE_FS
249790792Sgshapiro			    && !filesys_free(e->e_msgsize)
249890792Sgshapiro#endif /* _FFR_ANY_FREE_FS */
249990792Sgshapiro			   )
250038032Speter			{
250190792Sgshapiro				/*
250290792Sgshapiro				**  We perform this test again when the
250390792Sgshapiro				**  queue directory is selected, in collect.
250490792Sgshapiro				*/
250590792Sgshapiro
250664562Sgshapiro				usrerr("452 4.4.5 Insufficient disk space; try again later");
250790792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
250838032Speter			}
250938032Speter			if (Errors > 0)
251090792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
251164562Sgshapiro
251290792Sgshapiro			LogUsrErrs = true;
251390792Sgshapiro#if MILTER
251490792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
251590792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
251664562Sgshapiro			{
251764562Sgshapiro				char state;
251864562Sgshapiro				char *response;
251964562Sgshapiro
252064562Sgshapiro				response = milter_envfrom(args, e, &state);
252190792Sgshapiro				MILTER_REPLY("from");
252264562Sgshapiro			}
252390792Sgshapiro#endif /* MILTER */
252464562Sgshapiro			if (Errors > 0)
252590792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
252664562Sgshapiro
252764562Sgshapiro			message("250 2.1.0 Sender ok");
252890792Sgshapiro			smtp.sm_gotmail = true;
252990792Sgshapiro		    }
253090792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
253190792Sgshapiro		    {
253290792Sgshapiro			/*
253390792Sgshapiro			**  An error occurred while processing a MAIL command.
253490792Sgshapiro			**  Jump to the common error handling code.
253590792Sgshapiro			*/
253690792Sgshapiro
253790792Sgshapiro			sm_exc_free(exc);
253890792Sgshapiro			goto undo_no_pm;
253990792Sgshapiro		    }
254090792Sgshapiro		    SM_END_TRY
254138032Speter			break;
254238032Speter
254390792Sgshapiro		  undo_no_pm:
254490792Sgshapiro			e->e_flags &= ~EF_PM_NOTIFY;
254590792Sgshapiro		  undo:
254690792Sgshapiro			break;
254790792Sgshapiro
254838032Speter		  case CMDRCPT:		/* rcpt -- designate recipient */
254990792Sgshapiro			DELAY_CONN("RCPT");
2550168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2551168515Sgshapiro				macid("{rcpt_mailer}"), NULL);
2552168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2553168515Sgshapiro				macid("{rcpt_host}"), NULL);
2554168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2555168515Sgshapiro				macid("{rcpt_addr}"), NULL);
2556168515Sgshapiro#if MILTER
2557168515Sgshapiro			(void) memset(&addr_st, '\0', sizeof(addr_st));
2558168515Sgshapiro			a = NULL;
2559168515Sgshapiro			milter_rcpt_added = false;
2560173340Sgshapiro			smtp.sm_e_nrcpts_orig = e->e_nrcpts;
2561168515Sgshapiro#endif
2562182352Sgshapiro#if _FFR_BADRCPT_SHUTDOWN
2563182352Sgshapiro			/*
2564182352Sgshapiro			**  hack to deal with hack, see below:
2565203004Sgshapiro			**  n_badrcpts is increased if limit is reached.
2566182352Sgshapiro			*/
2567182352Sgshapiro
2568182352Sgshapiro			n_badrcpts_adj = (BadRcptThrottle > 0 &&
2569182352Sgshapiro					  n_badrcpts > BadRcptThrottle &&
2570182352Sgshapiro					  LogLevel > 5)
2571182352Sgshapiro					  ? n_badrcpts - 1 : n_badrcpts;
2572182352Sgshapiro			if (BadRcptShutdown > 0 &&
2573182352Sgshapiro			    n_badrcpts_adj >= BadRcptShutdown &&
2574182352Sgshapiro			    (BadRcptShutdownGood == 0 ||
2575182352Sgshapiro			     smtp.sm_nrcpts == 0 ||
2576182352Sgshapiro			     (n_badrcpts_adj * 100 /
2577182352Sgshapiro			      (smtp.sm_nrcpts + n_badrcpts) >=
2578182352Sgshapiro			      BadRcptShutdownGood)))
2579182352Sgshapiro			{
2580182352Sgshapiro				if (LogLevel > 5)
2581182352Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2582182352Sgshapiro						  "%s: Possible SMTP RCPT flood, shutting down connection.",
2583182352Sgshapiro						  CurSmtpClient);
2584182352Sgshapiro				message("421 4.7.0 %s Too many bad recipients; closing connection",
2585182352Sgshapiro				MyHostName);
2586182352Sgshapiro
2587182352Sgshapiro				/* arrange to ignore any current send list */
2588182352Sgshapiro				e->e_sendqueue = NULL;
2589182352Sgshapiro				goto doquit;
2590182352Sgshapiro			}
2591182352Sgshapiro#endif /* _FFR_BADRCPT_SHUTDOWN */
2592125820Sgshapiro			if (BadRcptThrottle > 0 &&
2593125820Sgshapiro			    n_badrcpts >= BadRcptThrottle)
2594125820Sgshapiro			{
2595125820Sgshapiro				if (LogLevel > 5 &&
2596125820Sgshapiro				    n_badrcpts == BadRcptThrottle)
2597125820Sgshapiro				{
2598125820Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2599125820Sgshapiro						  "%s: Possible SMTP RCPT flood, throttling.",
2600125820Sgshapiro						  CurSmtpClient);
2601125820Sgshapiro
2602125820Sgshapiro					/* To avoid duplicated message */
2603125820Sgshapiro					n_badrcpts++;
2604125820Sgshapiro				}
2605132943Sgshapiro				NBADRCPTS;
2606125820Sgshapiro
2607125820Sgshapiro				/*
2608125820Sgshapiro				**  Don't use exponential backoff for now.
2609203004Sgshapiro				**  Some systems will open more connections
2610125820Sgshapiro				**  and actually overload the receiver even
2611125820Sgshapiro				**  more.
2612125820Sgshapiro				*/
2613125820Sgshapiro
2614203004Sgshapiro				(void) sleep(BadRcptThrottleDelay);
2615125820Sgshapiro			}
261690792Sgshapiro			if (!smtp.sm_gotmail)
261738032Speter			{
261864562Sgshapiro				usrerr("503 5.0.0 Need MAIL before RCPT");
261938032Speter				break;
262038032Speter			}
262138032Speter			SmtpPhase = "server RCPT";
262290792Sgshapiro		    SM_TRY
262390792Sgshapiro		    {
262490792Sgshapiro			QuickAbort = true;
262590792Sgshapiro			LogUsrErrs = true;
262638032Speter
262738032Speter			/* limit flooding of our machine */
262890792Sgshapiro			if (MaxRcptPerMsg > 0 &&
262990792Sgshapiro			    smtp.sm_nrcpts >= MaxRcptPerMsg)
263038032Speter			{
263190792Sgshapiro				/* sleep(1); / * slow down? */
263264562Sgshapiro				usrerr("452 4.5.3 Too many recipients");
263390792Sgshapiro				goto rcpt_done;
263438032Speter			}
263538032Speter
2636223067Sgshapiro			if (!SM_IS_INTERACTIVE(e->e_sendmode)
2637157001Sgshapiro#if _FFR_DM_ONE
2638157001Sgshapiro			    && (NotFirstDelivery || SM_DM_ONE != e->e_sendmode)
2639157001Sgshapiro#endif /* _FFR_DM_ONE */
2640157001Sgshapiro			   )
264138032Speter				e->e_flags |= EF_VRFYONLY;
264238032Speter
264390792Sgshapiro#if MILTER
264464562Sgshapiro			/*
2645168515Sgshapiro			**  Do not expand recipients at RCPT time (in the call
2646173340Sgshapiro			**  to recipient()) if a milter can delete or reject
2647173340Sgshapiro			**  a RCPT.  If they are expanded, it is impossible
2648173340Sgshapiro			**  for removefromlist() to figure out the expanded
2649173340Sgshapiro			**  members of the original recipient and mark them
2650173340Sgshapiro			**  as QS_DONTSEND.
265164562Sgshapiro			*/
265264562Sgshapiro
2653173340Sgshapiro			if (!(smtp.sm_milterlist && smtp.sm_milterize &&
2654173340Sgshapiro			      !bitset(EF_DISCARD, e->e_flags)) &&
2655173340Sgshapiro			    (smtp.sm_milters.mis_flags &
2656173340Sgshapiro			     (MIS_FL_DEL_RCPT|MIS_FL_REJ_RCPT)) != 0)
2657173340Sgshapiro				e->e_flags |= EF_VRFYONLY;
2658168515Sgshapiro			milter_cmd_done = false;
2659168515Sgshapiro			milter_cmd_safe = false;
266090792Sgshapiro#endif /* MILTER */
266164562Sgshapiro
266238032Speter			p = skipword(p, "to");
266338032Speter			if (p == NULL)
266490792Sgshapiro				goto rcpt_done;
266590792Sgshapiro			macdefine(&e->e_macro, A_PERM,
266690792Sgshapiro				macid("{addr_type}"), "e r");
266790792Sgshapiro			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
266890792Sgshapiro				      e, true);
266990792Sgshapiro			macdefine(&e->e_macro, A_PERM,
267090792Sgshapiro				macid("{addr_type}"), NULL);
267142575Speter			if (Errors > 0)
267290792Sgshapiro				goto rcpt_done;
267342575Speter			if (a == NULL)
267442575Speter			{
267564562Sgshapiro				usrerr("501 5.0.0 Missing recipient");
267690792Sgshapiro				goto rcpt_done;
267742575Speter			}
267842575Speter
267938032Speter			if (delimptr != NULL && *delimptr != '\0')
268038032Speter				*delimptr++ = '\0';
268138032Speter
268264562Sgshapiro			/* put resulting triple from parseaddr() into macros */
268364562Sgshapiro			if (a->q_mailer != NULL)
268490792Sgshapiro				macdefine(&e->e_macro, A_PERM,
268590792Sgshapiro					macid("{rcpt_mailer}"),
268690792Sgshapiro					a->q_mailer->m_name);
268764562Sgshapiro			else
268890792Sgshapiro				macdefine(&e->e_macro, A_PERM,
268990792Sgshapiro					macid("{rcpt_mailer}"), NULL);
269064562Sgshapiro			if (a->q_host != NULL)
269190792Sgshapiro				macdefine(&e->e_macro, A_PERM,
269290792Sgshapiro					macid("{rcpt_host}"), a->q_host);
269364562Sgshapiro			else
269490792Sgshapiro				macdefine(&e->e_macro, A_PERM,
269590792Sgshapiro					macid("{rcpt_host}"), "localhost");
269664562Sgshapiro			if (a->q_user != NULL)
269790792Sgshapiro				macdefine(&e->e_macro, A_PERM,
269890792Sgshapiro					macid("{rcpt_addr}"), a->q_user);
269964562Sgshapiro			else
270090792Sgshapiro				macdefine(&e->e_macro, A_PERM,
270190792Sgshapiro					macid("{rcpt_addr}"), NULL);
270264562Sgshapiro			if (Errors > 0)
270390792Sgshapiro				goto rcpt_done;
270438032Speter
270538032Speter			/* now parse ESMTP arguments */
270664562Sgshapiro			addr = p;
2707168515Sgshapiro			parse_esmtp_args(e, a, p, delimptr, "RCPT", args,
2708168515Sgshapiro					rcpt_esmtp_args);
270938032Speter			if (Errors > 0)
271090792Sgshapiro				goto rcpt_done;
271138032Speter
2712168515Sgshapiro#if MILTER
2713168515Sgshapiro			/*
2714168515Sgshapiro			**  rscheck() can trigger an "exception"
2715168515Sgshapiro			**  in which case the execution continues at
2716168515Sgshapiro			**  SM_EXCEPT(exc, "[!F]*")
2717168515Sgshapiro			**  This means milter_cmd_safe is not set
2718168515Sgshapiro			**  and hence milter is not invoked.
2719168515Sgshapiro			**  Would it be "safe" to change that, i.e., use
2720168515Sgshapiro			**  milter_cmd_safe = true;
2721168515Sgshapiro			**  here so a milter is informed (if requested)
2722168515Sgshapiro			**  about RCPTs that are rejected by check_rcpt?
2723168515Sgshapiro			*/
2724168515Sgshapiro# if _FFR_MILTER_CHECK_REJECTIONS_TOO
2725168515Sgshapiro			milter_cmd_safe = true;
2726168515Sgshapiro# endif
2727168515Sgshapiro#endif
2728168515Sgshapiro
272964562Sgshapiro			/* do config file checking of the recipient */
273090792Sgshapiro			macdefine(&e->e_macro, A_PERM,
273190792Sgshapiro				macid("{addr_type}"), "e r");
273264562Sgshapiro			if (rscheck("check_rcpt", addr,
2733102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
2734168515Sgshapiro				    NULL, e->e_id, p_addr_st) != EX_OK ||
273564562Sgshapiro			    Errors > 0)
273690792Sgshapiro				goto rcpt_done;
273790792Sgshapiro			macdefine(&e->e_macro, A_PERM,
273890792Sgshapiro				macid("{addr_type}"), NULL);
273964562Sgshapiro
2740102528Sgshapiro			/* If discarding, don't bother to verify user */
2741102528Sgshapiro			if (bitset(EF_DISCARD, e->e_flags))
2742102528Sgshapiro				a->q_state = QS_VERIFIED;
2743168515Sgshapiro#if MILTER
2744168515Sgshapiro			milter_cmd_safe = true;
2745168515Sgshapiro#endif
2746102528Sgshapiro
2747168515Sgshapiro			/* save in recipient list after ESMTP mods */
2748168515Sgshapiro			a = recipient(a, &e->e_sendqueue, 0, e);
2749168515Sgshapiro			/* may trigger exception... */
2750168515Sgshapiro
275190792Sgshapiro#if MILTER
2752168515Sgshapiro			milter_rcpt_added = true;
2753168515Sgshapiro#endif
2754168515Sgshapiro
2755168515Sgshapiro			if(!(Errors > 0) && QS_IS_BADADDR(a->q_state))
2756168515Sgshapiro			{
2757168515Sgshapiro				/* punt -- should keep message in ADDRESS.... */
2758168515Sgshapiro				usrerr("550 5.1.1 Addressee unknown");
2759168515Sgshapiro			}
2760168515Sgshapiro
2761168515Sgshapiro#if MILTER
2762168515Sgshapiro		rcpt_done:
276390792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
276490792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
276564562Sgshapiro			{
276664562Sgshapiro				char state;
276764562Sgshapiro				char *response;
276864562Sgshapiro
2769168515Sgshapiro				/* how to get the error codes? */
2770168515Sgshapiro				if (Errors > 0)
2771168515Sgshapiro				{
2772168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
2773168515Sgshapiro						macid("{rcpt_mailer}"),
2774168515Sgshapiro						"error");
2775168515Sgshapiro					if (a != NULL &&
2776168515Sgshapiro					    a->q_status != NULL &&
2777168515Sgshapiro					    a->q_rstatus != NULL)
2778168515Sgshapiro					{
2779168515Sgshapiro						macdefine(&e->e_macro, A_PERM,
2780168515Sgshapiro							macid("{rcpt_host}"),
2781168515Sgshapiro							a->q_status);
2782168515Sgshapiro						macdefine(&e->e_macro, A_PERM,
2783168515Sgshapiro							macid("{rcpt_addr}"),
2784168515Sgshapiro							a->q_rstatus);
2785168515Sgshapiro					}
2786168515Sgshapiro					else
2787168515Sgshapiro					{
2788168515Sgshapiro						if (addr_st.q_host != NULL)
2789168515Sgshapiro							macdefine(&e->e_macro,
2790168515Sgshapiro								A_PERM,
2791168515Sgshapiro								macid("{rcpt_host}"),
2792168515Sgshapiro								addr_st.q_host);
2793168515Sgshapiro						if (addr_st.q_user != NULL)
2794168515Sgshapiro							macdefine(&e->e_macro,
2795168515Sgshapiro								A_PERM,
2796168515Sgshapiro								macid("{rcpt_addr}"),
2797168515Sgshapiro								addr_st.q_user);
2798168515Sgshapiro					}
2799168515Sgshapiro				}
2800168515Sgshapiro
2801168515Sgshapiro				response = milter_envrcpt(args, e, &state,
2802168515Sgshapiro							Errors > 0);
2803168515Sgshapiro				milter_cmd_done = true;
280490792Sgshapiro				MILTER_REPLY("to");
280564562Sgshapiro			}
280690792Sgshapiro#endif /* MILTER */
280764562Sgshapiro
280838032Speter			/* no errors during parsing, but might be a duplicate */
280938032Speter			e->e_to = a->q_paddr;
2810168515Sgshapiro			if (!(Errors > 0) && !QS_IS_BADADDR(a->q_state))
281138032Speter			{
281290792Sgshapiro				if (smtp.sm_nrcpts == 0)
281364562Sgshapiro					initsys(e);
281464562Sgshapiro				message("250 2.1.5 Recipient ok%s",
281564562Sgshapiro					QS_IS_QUEUEUP(a->q_state) ?
281638032Speter						" (will queue)" : "");
281790792Sgshapiro				smtp.sm_nrcpts++;
281838032Speter			}
2819168515Sgshapiro
2820168515Sgshapiro			/* Is this needed? */
2821168515Sgshapiro#if !MILTER
2822168515Sgshapiro		rcpt_done:
2823168515Sgshapiro#endif /* !MILTER */
2824168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2825168515Sgshapiro				macid("{rcpt_mailer}"), NULL);
2826168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2827168515Sgshapiro				macid("{rcpt_host}"), NULL);
2828168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2829168515Sgshapiro				macid("{rcpt_addr}"), NULL);
2830168515Sgshapiro			macdefine(&e->e_macro, A_PERM,
2831168515Sgshapiro				macid("{dsn_notify}"), NULL);
2832168515Sgshapiro
283390792Sgshapiro			if (Errors > 0)
2834132943Sgshapiro			{
283590792Sgshapiro				++n_badrcpts;
2836132943Sgshapiro				NBADRCPTS;
2837132943Sgshapiro			}
283890792Sgshapiro		    }
283990792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
284090792Sgshapiro		    {
284190792Sgshapiro			/* An exception occurred while processing RCPT */
284290792Sgshapiro			e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
284390792Sgshapiro			++n_badrcpts;
2844132943Sgshapiro			NBADRCPTS;
2845168515Sgshapiro#if MILTER
2846168515Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
2847168515Sgshapiro			    !bitset(EF_DISCARD, e->e_flags) &&
2848168515Sgshapiro			    !milter_cmd_done && milter_cmd_safe)
2849168515Sgshapiro			{
2850168515Sgshapiro				char state;
2851168515Sgshapiro				char *response;
2852168515Sgshapiro
2853168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
2854168515Sgshapiro					macid("{rcpt_mailer}"), "error");
2855168515Sgshapiro
2856168515Sgshapiro				/* how to get the error codes? */
2857168515Sgshapiro				if (addr_st.q_host != NULL)
2858168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
2859168515Sgshapiro						macid("{rcpt_host}"),
2860168515Sgshapiro						addr_st.q_host);
2861168515Sgshapiro				else if (a != NULL && a->q_status != NULL)
2862168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
2863168515Sgshapiro						macid("{rcpt_host}"),
2864168515Sgshapiro						a->q_status);
2865168515Sgshapiro
2866168515Sgshapiro				if (addr_st.q_user != NULL)
2867168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
2868168515Sgshapiro						macid("{rcpt_addr}"),
2869168515Sgshapiro						addr_st.q_user);
2870168515Sgshapiro				else if (a != NULL && a->q_rstatus != NULL)
2871168515Sgshapiro					macdefine(&e->e_macro, A_PERM,
2872168515Sgshapiro						macid("{rcpt_addr}"),
2873168515Sgshapiro						a->q_rstatus);
2874168515Sgshapiro
2875168515Sgshapiro				response = milter_envrcpt(args, e, &state,
2876168515Sgshapiro							true);
2877168515Sgshapiro				milter_cmd_done = true;
2878168515Sgshapiro				MILTER_REPLY("to");
2879168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
2880168515Sgshapiro					macid("{rcpt_mailer}"), NULL);
2881168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
2882168515Sgshapiro					macid("{rcpt_host}"), NULL);
2883168515Sgshapiro				macdefine(&e->e_macro, A_PERM,
2884168515Sgshapiro					macid("{rcpt_addr}"), NULL);
2885168515Sgshapiro			}
2886168515Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
2887168515Sgshapiro			    milter_rcpt_added && milter_cmd_done &&
2888168515Sgshapiro			    milter_cmd_fail)
2889168515Sgshapiro			{
2890168515Sgshapiro				(void) removefromlist(addr, &e->e_sendqueue, e);
2891168515Sgshapiro				milter_cmd_fail = false;
2892173340Sgshapiro				if (smtp.sm_e_nrcpts_orig < e->e_nrcpts)
2893173340Sgshapiro					e->e_nrcpts = smtp.sm_e_nrcpts_orig;
2894168515Sgshapiro			}
2895168515Sgshapiro#endif /* MILTER */
289690792Sgshapiro		    }
289790792Sgshapiro		    SM_END_TRY
289838032Speter			break;
289938032Speter
290038032Speter		  case CMDDATA:		/* data -- text of mail */
290190792Sgshapiro			DELAY_CONN("DATA");
2902132943Sgshapiro			if (!smtp_data(&smtp, e))
2903132943Sgshapiro				goto doquit;
290438032Speter			break;
290538032Speter
290638032Speter		  case CMDRSET:		/* rset -- reset state */
290738032Speter			if (tTd(94, 100))
290864562Sgshapiro				message("451 4.0.0 Test failure");
290938032Speter			else
291064562Sgshapiro				message("250 2.0.0 Reset state");
291190792Sgshapiro			CLEAR_STATE(cmdbuf);
291238032Speter			break;
291338032Speter
291438032Speter		  case CMDVRFY:		/* vrfy -- verify address */
291538032Speter		  case CMDEXPN:		/* expn -- expand address */
291690792Sgshapiro			vrfy = c->cmd_code == CMDVRFY;
291790792Sgshapiro			DELAY_CONN(vrfy ? "VRFY" : "EXPN");
291864562Sgshapiro			if (tempfail)
291964562Sgshapiro			{
292064562Sgshapiro				if (LogLevel > 9)
292164562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2922110560Sgshapiro						  "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)",
292390792Sgshapiro						  vrfy ? "VRFY" : "EXPN",
292464562Sgshapiro						  p, CurSmtpClient);
292590792Sgshapiro
292690792Sgshapiro				/* RFC 821 doesn't allow 4xy reply code */
292764562Sgshapiro				usrerr("550 5.7.1 Please try again later");
292864562Sgshapiro				break;
292964562Sgshapiro			}
293090792Sgshapiro			wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
293190792Sgshapiro					     false, vrfy ? "VRFY" : "EXPN", e);
2932132943Sgshapiro			STOP_IF_ATTACK(wt);
293364562Sgshapiro			previous = curtime();
2934110560Sgshapiro			if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) ||
2935110560Sgshapiro			    (!vrfy && !bitset(SRV_OFFER_EXPN, features)))
293638032Speter			{
293738032Speter				if (vrfy)
293864562Sgshapiro					message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
293938032Speter				else
294064562Sgshapiro					message("502 5.7.0 Sorry, we do not allow this operation");
294138032Speter				if (LogLevel > 5)
294238032Speter					sm_syslog(LOG_INFO, e->e_id,
2943110560Sgshapiro						  "%s: %s [rejected]",
294464562Sgshapiro						  CurSmtpClient,
294564562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
294638032Speter				break;
294738032Speter			}
294838032Speter			else if (!gothello &&
294938032Speter				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
295038032Speter						PrivacyFlags))
295138032Speter			{
295264562Sgshapiro				usrerr("503 5.0.0 I demand that you introduce yourself first");
295338032Speter				break;
295438032Speter			}
295590792Sgshapiro			if (Errors > 0)
295638032Speter				break;
295738032Speter			if (LogLevel > 5)
2958110560Sgshapiro				sm_syslog(LOG_INFO, e->e_id, "%s: %s",
295964562Sgshapiro					  CurSmtpClient,
296064562Sgshapiro					  shortenstring(inp, MAXSHORTSTR));
296190792Sgshapiro		    SM_TRY
296290792Sgshapiro		    {
296390792Sgshapiro			QuickAbort = true;
296438032Speter			vrfyqueue = NULL;
296538032Speter			if (vrfy)
296638032Speter				e->e_flags |= EF_VRFYONLY;
296738032Speter			while (*p != '\0' && isascii(*p) && isspace(*p))
296838032Speter				p++;
296938032Speter			if (*p == '\0')
297038032Speter			{
297164562Sgshapiro				usrerr("501 5.5.2 Argument required");
297238032Speter			}
297338032Speter			else
297438032Speter			{
297564562Sgshapiro				/* do config file checking of the address */
297664562Sgshapiro				if (rscheck(vrfy ? "check_vrfy" : "check_expn",
2977102528Sgshapiro					    p, NULL, e, RSF_RMCOMM,
2978168515Sgshapiro					    3, NULL, NOQID, NULL) != EX_OK ||
297990792Sgshapiro				    Errors > 0)
298090792Sgshapiro					sm_exc_raisenew_x(&EtypeQuickAbort, 1);
298138032Speter				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
298238032Speter			}
298364562Sgshapiro			if (wt > 0)
298471345Sgshapiro			{
298571345Sgshapiro				time_t t;
298671345Sgshapiro
298771345Sgshapiro				t = wt - (curtime() - previous);
298871345Sgshapiro				if (t > 0)
298971345Sgshapiro					(void) sleep(t);
299071345Sgshapiro			}
299138032Speter			if (Errors > 0)
299290792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
299338032Speter			if (vrfyqueue == NULL)
299438032Speter			{
299564562Sgshapiro				usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
299638032Speter			}
299738032Speter			while (vrfyqueue != NULL)
299838032Speter			{
299964562Sgshapiro				if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
300064562Sgshapiro				{
300164562Sgshapiro					vrfyqueue = vrfyqueue->q_next;
300264562Sgshapiro					continue;
300364562Sgshapiro				}
300438032Speter
300564562Sgshapiro				/* see if there is more in the vrfy list */
300638032Speter				a = vrfyqueue;
300738032Speter				while ((a = a->q_next) != NULL &&
300866494Sgshapiro				       (!QS_IS_UNDELIVERED(a->q_state)))
300938032Speter					continue;
301064562Sgshapiro				printvrfyaddr(vrfyqueue, a == NULL, vrfy);
301164562Sgshapiro				vrfyqueue = a;
301238032Speter			}
301390792Sgshapiro		    }
301490792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
301590792Sgshapiro		    {
301690792Sgshapiro			/*
301790792Sgshapiro			**  An exception occurred while processing VRFY/EXPN
301890792Sgshapiro			*/
301990792Sgshapiro
302090792Sgshapiro			sm_exc_free(exc);
302190792Sgshapiro			goto undo;
302290792Sgshapiro		    }
302390792Sgshapiro		    SM_END_TRY
302438032Speter			break;
302538032Speter
302638032Speter		  case CMDETRN:		/* etrn -- force queue flush */
302790792Sgshapiro			DELAY_CONN("ETRN");
302890792Sgshapiro
302990792Sgshapiro			/* Don't leak queue information via debug flags */
303090792Sgshapiro			if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
303190792Sgshapiro			    (RealUid != 0 && RealUid != TrustedUid &&
303290792Sgshapiro			     OpMode == MD_SMTP))
303338032Speter			{
303464562Sgshapiro				/* different message for MSA ? */
303564562Sgshapiro				message("502 5.7.0 Sorry, we do not allow this operation");
303638032Speter				if (LogLevel > 5)
303738032Speter					sm_syslog(LOG_INFO, e->e_id,
3038110560Sgshapiro						  "%s: %s [rejected]",
303964562Sgshapiro						  CurSmtpClient,
304064562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
304138032Speter				break;
304238032Speter			}
304364562Sgshapiro			if (tempfail)
304464562Sgshapiro			{
304564562Sgshapiro				if (LogLevel > 9)
304664562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
3047110560Sgshapiro						  "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)",
304864562Sgshapiro						  p, CurSmtpClient);
304990792Sgshapiro				usrerr(MSG_TEMPFAIL);
305064562Sgshapiro				break;
305164562Sgshapiro			}
305238032Speter
305338032Speter			if (strlen(p) <= 0)
305438032Speter			{
305564562Sgshapiro				usrerr("500 5.5.2 Parameter required");
305638032Speter				break;
305738032Speter			}
305838032Speter
305938032Speter			/* crude way to avoid denial-of-service attacks */
3060132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS,
3061132943Sgshapiro							true, "ETRN", e));
306238032Speter
306390792Sgshapiro			/*
306490792Sgshapiro			**  Do config file checking of the parameter.
306590792Sgshapiro			**  Even though we have srv_features now, we still
306690792Sgshapiro			**  need this ruleset because the former is called
306790792Sgshapiro			**  when the connection has been established, while
306890792Sgshapiro			**  this ruleset is called when the command is
306990792Sgshapiro			**  actually issued and therefore has all information
307090792Sgshapiro			**  available to make a decision.
307190792Sgshapiro			*/
307290792Sgshapiro
3073102528Sgshapiro			if (rscheck("check_etrn", p, NULL, e,
3074168515Sgshapiro				    RSF_RMCOMM, 3, NULL, NOQID, NULL)
3075168515Sgshapiro								!= EX_OK ||
3076102528Sgshapiro			    Errors > 0)
307764562Sgshapiro				break;
307864562Sgshapiro
307938032Speter			if (LogLevel > 5)
308038032Speter				sm_syslog(LOG_INFO, e->e_id,
3081110560Sgshapiro					  "%s: ETRN %s", CurSmtpClient,
308264562Sgshapiro					  shortenstring(p, MAXSHORTSTR));
308338032Speter
308438032Speter			id = p;
308590792Sgshapiro			if (*id == '#')
308690792Sgshapiro			{
3087111823Sgshapiro				int i, qgrp;
308890792Sgshapiro
308990792Sgshapiro				id++;
3090111823Sgshapiro				qgrp = name2qid(id);
3091111823Sgshapiro				if (!ISVALIDQGRP(qgrp))
309290792Sgshapiro				{
309390792Sgshapiro					usrerr("459 4.5.4 Queue %s unknown",
309490792Sgshapiro					       id);
309590792Sgshapiro					break;
309690792Sgshapiro				}
3097111823Sgshapiro				for (i = 0; i < NumQueue && Queue[i] != NULL;
3098111823Sgshapiro				     i++)
3099111823Sgshapiro					Queue[i]->qg_nextrun = (time_t) -1;
3100111823Sgshapiro				Queue[qgrp]->qg_nextrun = 0;
3101111823Sgshapiro				ok = run_work_group(Queue[qgrp]->qg_wgrp,
3102111823Sgshapiro						    RWG_FORK|RWG_FORCE);
310390792Sgshapiro				if (ok && Errors == 0)
310490792Sgshapiro					message("250 2.0.0 Queuing for queue group %s started", id);
310590792Sgshapiro				break;
310690792Sgshapiro			}
310790792Sgshapiro
310838032Speter			if (*id == '@')
310938032Speter				id++;
311038032Speter			else
311138032Speter				*--id = '@';
311264562Sgshapiro
311390792Sgshapiro			new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
311490792Sgshapiro			if (new == NULL)
311590792Sgshapiro			{
311690792Sgshapiro				syserr("500 5.5.0 ETRN out of memory");
311790792Sgshapiro				break;
311890792Sgshapiro			}
311938032Speter			new->queue_match = id;
312090792Sgshapiro			new->queue_negate = false;
312138032Speter			new->queue_next = NULL;
312238032Speter			QueueLimitRecipient = new;
312390792Sgshapiro			ok = runqueue(true, false, false, true);
312490792Sgshapiro			sm_free(QueueLimitRecipient); /* XXX */
312538032Speter			QueueLimitRecipient = NULL;
312638032Speter			if (ok && Errors == 0)
312764562Sgshapiro				message("250 2.0.0 Queuing for node %s started", p);
312838032Speter			break;
312938032Speter
313038032Speter		  case CMDHELP:		/* help -- give user info */
313190792Sgshapiro			DELAY_CONN("HELP");
313264562Sgshapiro			help(p, e);
313338032Speter			break;
313438032Speter
313538032Speter		  case CMDNOOP:		/* noop -- do nothing */
313690792Sgshapiro			DELAY_CONN("NOOP");
3137157001Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
3138132943Sgshapiro							true, "NOOP", e));
313964562Sgshapiro			message("250 2.0.0 OK");
314038032Speter			break;
314138032Speter
314238032Speter		  case CMDQUIT:		/* quit -- leave mail */
314364562Sgshapiro			message("221 2.0.0 %s closing connection", MyHostName);
314490792Sgshapiro#if PIPELINING
314590792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
314690792Sgshapiro#endif /* PIPELINING */
314738032Speter
314890792Sgshapiro			if (smtp.sm_nrcpts > 0)
314990792Sgshapiro				logundelrcpts(e, "aborted by sender", 9, false);
315090792Sgshapiro
315138032Speter			/* arrange to ignore any current send list */
315238032Speter			e->e_sendqueue = NULL;
315338032Speter
315490792Sgshapiro#if STARTTLS
315564562Sgshapiro			/* shutdown TLS connection */
315664562Sgshapiro			if (tls_active)
315764562Sgshapiro			{
315864562Sgshapiro				(void) endtls(srv_ssl, "server");
315990792Sgshapiro				tls_active = false;
316064562Sgshapiro			}
316190792Sgshapiro#endif /* STARTTLS */
316290792Sgshapiro#if SASL
316364562Sgshapiro			if (authenticating == SASL_IS_AUTH)
316464562Sgshapiro			{
316564562Sgshapiro				sasl_dispose(&conn);
316664562Sgshapiro				authenticating = SASL_NOT_AUTH;
316790792Sgshapiro				/* XXX sasl_done(); this is a child */
316864562Sgshapiro			}
316990792Sgshapiro#endif /* SASL */
317064562Sgshapiro
317164562Sgshapirodoquit:
317238032Speter			/* avoid future 050 messages */
317338032Speter			disconnect(1, e);
317438032Speter
317590792Sgshapiro#if MILTER
317664562Sgshapiro			/* close out milter filters */
317764562Sgshapiro			milter_quit(e);
317890792Sgshapiro#endif /* MILTER */
317964562Sgshapiro
3180203004Sgshapiro			if (tTd(92, 2))
3181203004Sgshapiro				sm_dprintf("QUIT: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",
3182203004Sgshapiro					e->e_id,
3183203004Sgshapiro					bitset(EF_LOGSENDER, e->e_flags),
3184203004Sgshapiro					LogLevel);
318564562Sgshapiro			if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
318664562Sgshapiro				logsender(e, NULL);
318764562Sgshapiro			e->e_flags &= ~EF_LOGSENDER;
318864562Sgshapiro
318998121Sgshapiro			if (lognullconnection && LogLevel > 5 &&
319098121Sgshapiro			    nullserver == NULL)
319164562Sgshapiro			{
319264562Sgshapiro				char *d;
319364562Sgshapiro
319490792Sgshapiro				d = macvalue(macid("{daemon_name}"), e);
319564562Sgshapiro				if (d == NULL)
319664562Sgshapiro					d = "stdin";
319790792Sgshapiro
319890792Sgshapiro				/*
319990792Sgshapiro				**  even though this id is "bogus", it makes
320090792Sgshapiro				**  it simpler to "grep" related events, e.g.,
320190792Sgshapiro				**  timeouts for the same connection.
320290792Sgshapiro				*/
320390792Sgshapiro
320490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3205110560Sgshapiro					  "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
320664562Sgshapiro					  CurSmtpClient, d);
320764562Sgshapiro			}
3208110560Sgshapiro			if (tTd(93, 100))
3209110560Sgshapiro			{
3210110560Sgshapiro				/* return to handle next connection */
3211110560Sgshapiro				return;
3212110560Sgshapiro			}
321390792Sgshapiro			finis(true, true, ExitStat);
321464562Sgshapiro			/* NOTREACHED */
321538032Speter
3216157001Sgshapiro			/* just to avoid bogus warning from some compilers */
3217157001Sgshapiro			exit(EX_OSERR);
3218157001Sgshapiro
321938032Speter		  case CMDVERB:		/* set verbose mode */
322090792Sgshapiro			DELAY_CONN("VERB");
3221110560Sgshapiro			if (!bitset(SRV_OFFER_EXPN, features) ||
3222110560Sgshapiro			    !bitset(SRV_OFFER_VERB, features))
322338032Speter			{
322438032Speter				/* this would give out the same info */
322564562Sgshapiro				message("502 5.7.0 Verbose unavailable");
322638032Speter				break;
322738032Speter			}
3228157001Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_noop, MaxNOOPCommands,
3229132943Sgshapiro							true, "VERB", e));
323038032Speter			Verbose = 1;
323164562Sgshapiro			set_delivery_mode(SM_DELIVER, e);
323264562Sgshapiro			message("250 2.0.0 Verbose mode");
323338032Speter			break;
323438032Speter
323590792Sgshapiro#if SMTPDEBUG
323638032Speter		  case CMDDBGQSHOW:	/* show queues */
323790792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
323890792Sgshapiro					     "Send Queue=");
3239132943Sgshapiro			printaddr(smioout, e->e_sendqueue, true);
324038032Speter			break;
324138032Speter
324238032Speter		  case CMDDBGDEBUG:	/* set debug mode */
3243168515Sgshapiro			tTsetup(tTdvect, sizeof(tTdvect), "0-99.1");
324438032Speter			tTflag(p);
324564562Sgshapiro			message("200 2.0.0 Debug set");
324638032Speter			break;
324738032Speter
324890792Sgshapiro#else /* SMTPDEBUG */
324938032Speter		  case CMDDBGQSHOW:	/* show queues */
325038032Speter		  case CMDDBGDEBUG:	/* set debug mode */
325190792Sgshapiro#endif /* SMTPDEBUG */
325238032Speter		  case CMDLOGBOGUS:	/* bogus command */
325390792Sgshapiro			DELAY_CONN("Bogus");
325438032Speter			if (LogLevel > 0)
325538032Speter				sm_syslog(LOG_CRIT, e->e_id,
3256110560Sgshapiro					  "\"%s\" command from %s (%.100s)",
325764562Sgshapiro					  c->cmd_name, CurSmtpClient,
325864562Sgshapiro					  anynet_ntoa(&RealHostAddr));
325964562Sgshapiro			/* FALLTHROUGH */
326038032Speter
326138032Speter		  case CMDERROR:	/* unknown command */
326290792Sgshapiro#if MAXBADCOMMANDS > 0
326390792Sgshapiro			if (++n_badcmds > MAXBADCOMMANDS)
326438032Speter			{
3265132943Sgshapiro  stopattack:
326664562Sgshapiro				message("421 4.7.0 %s Too many bad commands; closing connection",
326738032Speter					MyHostName);
326864562Sgshapiro
326964562Sgshapiro				/* arrange to ignore any current send list */
327064562Sgshapiro				e->e_sendqueue = NULL;
327138032Speter				goto doquit;
327238032Speter			}
327390792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
327438032Speter
3275132943Sgshapiro#if MILTER && SMFI_VERSION > 2
3276132943Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
3277132943Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
3278132943Sgshapiro			{
3279132943Sgshapiro				char state;
3280132943Sgshapiro				char *response;
3281132943Sgshapiro
3282132943Sgshapiro				if (MilterLogLevel > 9)
3283132943Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
3284132943Sgshapiro						"Sending \"%s\" to Milter", inp);
3285132943Sgshapiro				response = milter_unknown(inp, e, &state);
3286132943Sgshapiro				MILTER_REPLY("unknown");
3287132943Sgshapiro				if (state == SMFIR_REPLYCODE ||
3288132943Sgshapiro				    state == SMFIR_REJECT ||
3289157001Sgshapiro				    state == SMFIR_TEMPFAIL ||
3290157001Sgshapiro				    state == SMFIR_SHUTDOWN)
3291132943Sgshapiro				{
3292132943Sgshapiro					/* MILTER_REPLY already gave an error */
3293132943Sgshapiro					break;
3294132943Sgshapiro				}
3295132943Sgshapiro			}
3296132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 2 */
3297132943Sgshapiro
329864562Sgshapiro			usrerr("500 5.5.1 Command unrecognized: \"%s\"",
329964562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
330038032Speter			break;
330138032Speter
330264562Sgshapiro		  case CMDUNIMPL:
330390792Sgshapiro			DELAY_CONN("Unimpl");
330464562Sgshapiro			usrerr("502 5.5.1 Command not implemented: \"%s\"",
330564562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
330664562Sgshapiro			break;
330764562Sgshapiro
330838032Speter		  default:
330990792Sgshapiro			DELAY_CONN("default");
331038032Speter			errno = 0;
331164562Sgshapiro			syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
331238032Speter			break;
331338032Speter		}
331490792Sgshapiro#if SASL
331564562Sgshapiro		}
331690792Sgshapiro#endif /* SASL */
331790792Sgshapiro	    }
331890792Sgshapiro	    SM_EXCEPT(exc, "[!F]*")
331990792Sgshapiro	    {
332090792Sgshapiro		/*
332190792Sgshapiro		**  The only possible exception is "E:mta.quickabort".
332290792Sgshapiro		**  There is nothing to do except fall through and loop.
332390792Sgshapiro		*/
332490792Sgshapiro	    }
332590792Sgshapiro	    SM_END_TRY
332638032Speter	}
332790792Sgshapiro}
332890792Sgshapiro/*
332990792Sgshapiro**  SMTP_DATA -- implement the SMTP DATA command.
333090792Sgshapiro**
333190792Sgshapiro**	Parameters:
333290792Sgshapiro**		smtp -- status of SMTP connection.
333390792Sgshapiro**		e -- envelope.
333490792Sgshapiro**
333590792Sgshapiro**	Returns:
3336132943Sgshapiro**		true iff SMTP session can continue.
333790792Sgshapiro**
333890792Sgshapiro**	Side Effects:
333990792Sgshapiro**		possibly sends message.
334090792Sgshapiro*/
334164562Sgshapiro
3342132943Sgshapirostatic bool
334390792Sgshapirosmtp_data(smtp, e)
334490792Sgshapiro	SMTP_T *smtp;
334590792Sgshapiro	ENVELOPE *e;
334690792Sgshapiro{
334790792Sgshapiro#if MILTER
334890792Sgshapiro	bool milteraccept;
334990792Sgshapiro#endif /* MILTER */
335090792Sgshapiro	bool aborting;
335190792Sgshapiro	bool doublequeue;
3352168515Sgshapiro	bool rv = true;
335390792Sgshapiro	ADDRESS *a;
335490792Sgshapiro	ENVELOPE *ee;
335590792Sgshapiro	char *id;
335698121Sgshapiro	char *oldid;
3357168515Sgshapiro	unsigned int features;
335890792Sgshapiro	char buf[32];
335990792Sgshapiro
336090792Sgshapiro	SmtpPhase = "server DATA";
336190792Sgshapiro	if (!smtp->sm_gotmail)
336290792Sgshapiro	{
336390792Sgshapiro		usrerr("503 5.0.0 Need MAIL command");
3364132943Sgshapiro		return true;
336590792Sgshapiro	}
336690792Sgshapiro	else if (smtp->sm_nrcpts <= 0)
336790792Sgshapiro	{
336890792Sgshapiro		usrerr("503 5.0.0 Need RCPT (recipient)");
3369132943Sgshapiro		return true;
337090792Sgshapiro	}
3371168515Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "%u", smtp->sm_nrcpts);
337290792Sgshapiro	if (rscheck("check_data", buf, NULL, e,
3373102528Sgshapiro		    RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
3374168515Sgshapiro		    e->e_id, NULL) != EX_OK)
3375132943Sgshapiro		return true;
337690792Sgshapiro
3377132943Sgshapiro#if MILTER && SMFI_VERSION > 3
3378132943Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize &&
3379132943Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
3380132943Sgshapiro	{
3381132943Sgshapiro		char state;
3382132943Sgshapiro		char *response;
3383132943Sgshapiro		int savelogusrerrs = LogUsrErrs;
3384132943Sgshapiro
3385132943Sgshapiro		response = milter_data_cmd(e, &state);
3386132943Sgshapiro		switch (state)
3387132943Sgshapiro		{
3388132943Sgshapiro		  case SMFIR_REPLYCODE:
3389132943Sgshapiro			if (MilterLogLevel > 3)
3390132943Sgshapiro			{
3391132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3392132943Sgshapiro					  "Milter: cmd=data, reject=%s",
3393132943Sgshapiro					  response);
3394132943Sgshapiro				LogUsrErrs = false;
3395132943Sgshapiro			}
3396203004Sgshapiro#if _FFR_MILTER_ENHSC
3397203004Sgshapiro			if (ISSMTPCODE(response))
3398203004Sgshapiro				(void) extenhsc(response + 4, ' ', e->e_enhsc);
3399203004Sgshapiro#endif /* _FFR_MILTER_ENHSC */
3400203004Sgshapiro
3401132943Sgshapiro			usrerr(response);
3402157001Sgshapiro			if (strncmp(response, "421 ", 4) == 0
3403157001Sgshapiro			    || strncmp(response, "421-", 4) == 0)
3404132943Sgshapiro			{
3405132943Sgshapiro				e->e_sendqueue = NULL;
3406132943Sgshapiro				return false;
3407132943Sgshapiro			}
3408132943Sgshapiro			return true;
3409132943Sgshapiro
3410132943Sgshapiro		  case SMFIR_REJECT:
3411132943Sgshapiro			if (MilterLogLevel > 3)
3412132943Sgshapiro			{
3413132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3414132943Sgshapiro					  "Milter: cmd=data, reject=550 5.7.1 Command rejected");
3415132943Sgshapiro				LogUsrErrs = false;
3416132943Sgshapiro			}
3417203004Sgshapiro#if _FFR_MILTER_ENHSC
3418203004Sgshapiro			(void) sm_strlcpy(e->e_enhsc, "5.7.1",
3419203004Sgshapiro					 sizeof(e->e_enhsc));
3420203004Sgshapiro#endif /* _FFR_MILTER_ENHSC */
3421132943Sgshapiro			usrerr("550 5.7.1 Command rejected");
3422132943Sgshapiro			return true;
3423132943Sgshapiro
3424132943Sgshapiro		  case SMFIR_DISCARD:
3425132943Sgshapiro			if (MilterLogLevel > 3)
3426132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3427132943Sgshapiro					  "Milter: cmd=data, discard");
3428132943Sgshapiro			e->e_flags |= EF_DISCARD;
3429132943Sgshapiro			break;
3430132943Sgshapiro
3431132943Sgshapiro		  case SMFIR_TEMPFAIL:
3432132943Sgshapiro			if (MilterLogLevel > 3)
3433132943Sgshapiro			{
3434132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3435132943Sgshapiro					  "Milter: cmd=data, reject=%s",
3436132943Sgshapiro					  MSG_TEMPFAIL);
3437132943Sgshapiro				LogUsrErrs = false;
3438132943Sgshapiro			}
3439203004Sgshapiro#if _FFR_MILTER_ENHSC
3440203004Sgshapiro			(void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
3441203004Sgshapiro#endif /* _FFR_MILTER_ENHSC */
3442132943Sgshapiro			usrerr(MSG_TEMPFAIL);
3443132943Sgshapiro			return true;
3444157001Sgshapiro
3445157001Sgshapiro		  case SMFIR_SHUTDOWN:
3446157001Sgshapiro			if (MilterLogLevel > 3)
3447157001Sgshapiro			{
3448157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3449157001Sgshapiro					  "Milter: cmd=data, reject=421 4.7.0 %s closing connection",
3450157001Sgshapiro					  MyHostName);
3451157001Sgshapiro				LogUsrErrs = false;
3452157001Sgshapiro			}
3453157001Sgshapiro			usrerr("421 4.7.0 %s closing connection", MyHostName);
3454157001Sgshapiro			e->e_sendqueue = NULL;
3455157001Sgshapiro			return false;
3456132943Sgshapiro		}
3457132943Sgshapiro		LogUsrErrs = savelogusrerrs;
3458132943Sgshapiro		if (response != NULL)
3459132943Sgshapiro			sm_free(response); /* XXX */
3460132943Sgshapiro	}
3461132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 3 */
3462132943Sgshapiro
346390792Sgshapiro	/* put back discard bit */
346490792Sgshapiro	if (smtp->sm_discard)
346590792Sgshapiro		e->e_flags |= EF_DISCARD;
346690792Sgshapiro
346790792Sgshapiro	/* check to see if we need to re-expand aliases */
346890792Sgshapiro	/* also reset QS_BADADDR on already-diagnosted addrs */
346990792Sgshapiro	doublequeue = false;
347090792Sgshapiro	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
347190792Sgshapiro	{
347290792Sgshapiro		if (QS_IS_VERIFIED(a->q_state) &&
347390792Sgshapiro		    !bitset(EF_DISCARD, e->e_flags))
347490792Sgshapiro		{
347590792Sgshapiro			/* need to re-expand aliases */
347690792Sgshapiro			doublequeue = true;
347790792Sgshapiro		}
347890792Sgshapiro		if (QS_IS_BADADDR(a->q_state))
347990792Sgshapiro		{
348090792Sgshapiro			/* make this "go away" */
348190792Sgshapiro			a->q_state = QS_DONTSEND;
348290792Sgshapiro		}
348390792Sgshapiro	}
348490792Sgshapiro
348590792Sgshapiro	/* collect the text of the message */
348690792Sgshapiro	SmtpPhase = "collect";
348790792Sgshapiro	buffer_errors();
348890792Sgshapiro
3489120256Sgshapiro	collect(InChannel, true, NULL, e, true);
349090792Sgshapiro
349190792Sgshapiro	/* redefine message size */
3492244928Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "%ld", PRT_NONNEGL(e->e_msgsize));
349390792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
349490792Sgshapiro
349590792Sgshapiro	/* rscheck() will set Errors or EF_DISCARD if it trips */
3496102528Sgshapiro	(void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT,
3497168515Sgshapiro		       3, NULL, e->e_id, NULL);
349890792Sgshapiro
349990792Sgshapiro#if MILTER
350090792Sgshapiro	milteraccept = true;
350190792Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize &&
350290792Sgshapiro	    Errors <= 0 &&
350390792Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
350490792Sgshapiro	{
350590792Sgshapiro		char state;
350690792Sgshapiro		char *response;
350790792Sgshapiro
350890792Sgshapiro		response = milter_data(e, &state);
350990792Sgshapiro		switch (state)
351090792Sgshapiro		{
351190792Sgshapiro		  case SMFIR_REPLYCODE:
351290792Sgshapiro			if (MilterLogLevel > 3)
351390792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
351490792Sgshapiro					  "Milter: data, reject=%s",
351590792Sgshapiro					  response);
351690792Sgshapiro			milteraccept = false;
3517203004Sgshapiro#if _FFR_MILTER_ENHSC
3518203004Sgshapiro			if (ISSMTPCODE(response))
3519203004Sgshapiro				(void) extenhsc(response + 4, ' ', e->e_enhsc);
3520203004Sgshapiro#endif /* _FFR_MILTER_ENHSC */
352190792Sgshapiro			usrerr(response);
3522203004Sgshapiro			if (strncmp(response, "421 ", 4) == 0
3523203004Sgshapiro			    || strncmp(response, "421-", 4) == 0)
3524203004Sgshapiro				rv = false;
352590792Sgshapiro			break;
352690792Sgshapiro
352790792Sgshapiro		  case SMFIR_REJECT:
352890792Sgshapiro			milteraccept = false;
352990792Sgshapiro			if (MilterLogLevel > 3)
353090792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
353190792Sgshapiro					  "Milter: data, reject=554 5.7.1 Command rejected");
353290792Sgshapiro			usrerr("554 5.7.1 Command rejected");
353390792Sgshapiro			break;
353490792Sgshapiro
353590792Sgshapiro		  case SMFIR_DISCARD:
353690792Sgshapiro			if (MilterLogLevel > 3)
353790792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
353890792Sgshapiro					  "Milter: data, discard");
353990792Sgshapiro			milteraccept = false;
354090792Sgshapiro			e->e_flags |= EF_DISCARD;
354190792Sgshapiro			break;
354290792Sgshapiro
354390792Sgshapiro		  case SMFIR_TEMPFAIL:
354490792Sgshapiro			if (MilterLogLevel > 3)
354590792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
354690792Sgshapiro					  "Milter: data, reject=%s",
354790792Sgshapiro					  MSG_TEMPFAIL);
354890792Sgshapiro			milteraccept = false;
3549203004Sgshapiro#if _FFR_MILTER_ENHSC
3550203004Sgshapiro			(void) extenhsc(MSG_TEMPFAIL + 4, ' ', e->e_enhsc);
3551203004Sgshapiro#endif /* _FFR_MILTER_ENHSC */
355290792Sgshapiro			usrerr(MSG_TEMPFAIL);
355390792Sgshapiro			break;
3554157001Sgshapiro
3555157001Sgshapiro		  case SMFIR_SHUTDOWN:
3556157001Sgshapiro			if (MilterLogLevel > 3)
3557157001Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3558157001Sgshapiro					  "Milter: data, reject=421 4.7.0 %s closing connection",
3559157001Sgshapiro					  MyHostName);
3560157001Sgshapiro			milteraccept = false;
3561157001Sgshapiro			usrerr("421 4.7.0 %s closing connection", MyHostName);
3562157001Sgshapiro			rv = false;
3563157001Sgshapiro			break;
356490792Sgshapiro		}
356590792Sgshapiro		if (response != NULL)
356690792Sgshapiro			sm_free(response);
356790792Sgshapiro	}
356890792Sgshapiro
356990792Sgshapiro	/* Milter may have changed message size */
3570244928Sgshapiro	(void) sm_snprintf(buf, sizeof(buf), "%ld", PRT_NONNEGL(e->e_msgsize));
357190792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
357290792Sgshapiro
357390792Sgshapiro	/* abort message filters that didn't get the body & log msg is OK */
357490792Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize)
357590792Sgshapiro	{
357690792Sgshapiro		milter_abort(e);
357790792Sgshapiro		if (milteraccept && MilterLogLevel > 9)
357890792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
357990792Sgshapiro	}
3580132943Sgshapiro
3581132943Sgshapiro	/*
3582132943Sgshapiro	**  If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or
3583132943Sgshapiro	**  milter accepted message, sync it now
3584132943Sgshapiro	**
3585132943Sgshapiro	**  XXX This is almost a copy of the code in collect(): put it into
3586132943Sgshapiro	**	a function that is called from both places?
3587132943Sgshapiro	*/
3588132943Sgshapiro
3589132943Sgshapiro	if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER)
3590132943Sgshapiro	{
3591132943Sgshapiro		int afd;
3592132943Sgshapiro		SM_FILE_T *volatile df;
3593132943Sgshapiro		char *dfname;
3594132943Sgshapiro
3595132943Sgshapiro		df = e->e_dfp;
3596132943Sgshapiro		dfname = queuename(e, DATAFL_LETTER);
3597132943Sgshapiro		if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0
3598132943Sgshapiro		    && errno != EINVAL)
3599132943Sgshapiro		{
3600132943Sgshapiro			int save_errno;
3601132943Sgshapiro
3602132943Sgshapiro			save_errno = errno;
3603132943Sgshapiro			if (save_errno == EEXIST)
3604132943Sgshapiro			{
3605132943Sgshapiro				struct stat st;
3606132943Sgshapiro				int dfd;
3607132943Sgshapiro
3608132943Sgshapiro				if (stat(dfname, &st) < 0)
3609132943Sgshapiro					st.st_size = -1;
3610132943Sgshapiro				errno = EEXIST;
3611132943Sgshapiro				syserr("@collect: bfcommit(%s): already on disk, size=%ld",
3612132943Sgshapiro				       dfname, (long) st.st_size);
3613132943Sgshapiro				dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
3614132943Sgshapiro				if (dfd >= 0)
3615132943Sgshapiro					dumpfd(dfd, true, true);
3616132943Sgshapiro			}
3617132943Sgshapiro			errno = save_errno;
3618132943Sgshapiro			dferror(df, "bfcommit", e);
3619132943Sgshapiro			flush_errors(true);
3620132943Sgshapiro			finis(save_errno != EEXIST, true, ExitStat);
3621132943Sgshapiro		}
3622132943Sgshapiro		else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
3623132943Sgshapiro		{
3624132943Sgshapiro			dferror(df, "sm_io_getinfo", e);
3625132943Sgshapiro			flush_errors(true);
3626132943Sgshapiro			finis(true, true, ExitStat);
3627132943Sgshapiro			/* NOTREACHED */
3628132943Sgshapiro		}
3629132943Sgshapiro		else if (fsync(afd) < 0)
3630132943Sgshapiro		{
3631132943Sgshapiro			dferror(df, "fsync", e);
3632132943Sgshapiro			flush_errors(true);
3633132943Sgshapiro			finis(true, true, ExitStat);
3634132943Sgshapiro			/* NOTREACHED */
3635132943Sgshapiro		}
3636132943Sgshapiro		else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
3637132943Sgshapiro		{
3638132943Sgshapiro			dferror(df, "sm_io_close", e);
3639132943Sgshapiro			flush_errors(true);
3640132943Sgshapiro			finis(true, true, ExitStat);
3641132943Sgshapiro			/* NOTREACHED */
3642132943Sgshapiro		}
3643132943Sgshapiro
3644132943Sgshapiro		/* Now reopen the df file */
3645132943Sgshapiro		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
3646132943Sgshapiro					SM_IO_RDONLY, NULL);
3647132943Sgshapiro		if (e->e_dfp == NULL)
3648132943Sgshapiro		{
3649132943Sgshapiro			/* we haven't acked receipt yet, so just chuck this */
3650132943Sgshapiro			syserr("@Cannot reopen %s", dfname);
3651132943Sgshapiro			finis(true, true, ExitStat);
3652132943Sgshapiro			/* NOTREACHED */
3653132943Sgshapiro		}
3654132943Sgshapiro	}
365590792Sgshapiro#endif /* MILTER */
365690792Sgshapiro
365790792Sgshapiro	/* Check if quarantining stats should be updated */
365890792Sgshapiro	if (e->e_quarmsg != NULL)
365990792Sgshapiro		markstats(e, NULL, STATS_QUARANTINE);
366090792Sgshapiro
366190792Sgshapiro	/*
366290792Sgshapiro	**  If a header/body check (header checks or milter)
366390792Sgshapiro	**  set EF_DISCARD, don't queueup the message --
366490792Sgshapiro	**  that would lose the EF_DISCARD bit and deliver
366590792Sgshapiro	**  the message.
366690792Sgshapiro	*/
366790792Sgshapiro
366890792Sgshapiro	if (bitset(EF_DISCARD, e->e_flags))
366990792Sgshapiro		doublequeue = false;
367090792Sgshapiro
367190792Sgshapiro	aborting = Errors > 0;
3672125820Sgshapiro	if (!(aborting || bitset(EF_DISCARD, e->e_flags)) &&
367390792Sgshapiro	    (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
367490792Sgshapiro	    !split_by_recipient(e))
367590792Sgshapiro		aborting = bitset(EF_FATALERRS, e->e_flags);
367690792Sgshapiro
367790792Sgshapiro	if (aborting)
367890792Sgshapiro	{
3679173340Sgshapiro		ADDRESS *q;
3680173340Sgshapiro
368190792Sgshapiro		/* Log who the mail would have gone to */
368290792Sgshapiro		logundelrcpts(e, e->e_message, 8, false);
3683173340Sgshapiro
3684173340Sgshapiro		/*
3685173340Sgshapiro		**  If something above refused the message, we still haven't
3686173340Sgshapiro		**  accepted responsibility for it.  Don't send DSNs.
3687173340Sgshapiro		*/
3688173340Sgshapiro
3689173340Sgshapiro		for (q = e->e_sendqueue; q != NULL; q = q->q_next)
3690173340Sgshapiro			q->q_flags &= ~Q_PINGFLAGS;
3691173340Sgshapiro
369290792Sgshapiro		flush_errors(true);
369390792Sgshapiro		buffer_errors();
369490792Sgshapiro		goto abortmessage;
369590792Sgshapiro	}
369690792Sgshapiro
369790792Sgshapiro	/* from now on, we have to operate silently */
369890792Sgshapiro	buffer_errors();
369990792Sgshapiro
370090792Sgshapiro#if 0
370190792Sgshapiro	/*
370290792Sgshapiro	**  Clear message, it may contain an error from the SMTP dialogue.
370390792Sgshapiro	**  This error must not show up in the queue.
370490792Sgshapiro	**	Some error message should show up, e.g., alias database
370590792Sgshapiro	**	not available, but others shouldn't, e.g., from check_rcpt.
370690792Sgshapiro	*/
370790792Sgshapiro
370890792Sgshapiro	e->e_message = NULL;
370990792Sgshapiro#endif /* 0 */
371090792Sgshapiro
371190792Sgshapiro	/*
371290792Sgshapiro	**  Arrange to send to everyone.
371390792Sgshapiro	**	If sending to multiple people, mail back
371490792Sgshapiro	**		errors rather than reporting directly.
371590792Sgshapiro	**	In any case, don't mail back errors for
371690792Sgshapiro	**		anything that has happened up to
371790792Sgshapiro	**		now (the other end will do this).
371890792Sgshapiro	**	Truncate our transcript -- the mail has gotten
371990792Sgshapiro	**		to us successfully, and if we have
372090792Sgshapiro	**		to mail this back, it will be easier
372190792Sgshapiro	**		on the reader.
372290792Sgshapiro	**	Then send to everyone.
372390792Sgshapiro	**	Finally give a reply code.  If an error has
372490792Sgshapiro	**		already been given, don't mail a
372590792Sgshapiro	**		message back.
372690792Sgshapiro	**	We goose error returns by clearing error bit.
372790792Sgshapiro	*/
372890792Sgshapiro
372990792Sgshapiro	SmtpPhase = "delivery";
373090792Sgshapiro	(void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
373190792Sgshapiro	id = e->e_id;
373290792Sgshapiro
373390792Sgshapiro#if NAMED_BIND
373490792Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
373590792Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
373690792Sgshapiro#endif /* NAMED_BIND */
373790792Sgshapiro
3738223067Sgshapiro
373990792Sgshapiro	for (ee = e; ee != NULL; ee = ee->e_sibling)
374090792Sgshapiro	{
374190792Sgshapiro		/* make sure we actually do delivery */
374290792Sgshapiro		ee->e_flags &= ~EF_CLRQUEUE;
374390792Sgshapiro
374490792Sgshapiro		/* from now on, operate silently */
374590792Sgshapiro		ee->e_errormode = EM_MAIL;
374690792Sgshapiro
374790792Sgshapiro		if (doublequeue)
374890792Sgshapiro		{
374990792Sgshapiro			/* make sure it is in the queue */
375090792Sgshapiro			queueup(ee, false, true);
375190792Sgshapiro		}
375290792Sgshapiro		else
375390792Sgshapiro		{
3754157001Sgshapiro			int mode;
3755157001Sgshapiro
375690792Sgshapiro			/* send to all recipients */
3757157001Sgshapiro			mode = SM_DEFAULT;
3758157001Sgshapiro#if _FFR_DM_ONE
3759157001Sgshapiro			if (SM_DM_ONE == e->e_sendmode)
3760157001Sgshapiro			{
3761157001Sgshapiro				if (NotFirstDelivery)
3762157001Sgshapiro				{
3763157001Sgshapiro					mode = SM_QUEUE;
3764157001Sgshapiro					e->e_sendmode = SM_QUEUE;
3765157001Sgshapiro				}
3766157001Sgshapiro				else
3767157001Sgshapiro				{
3768157001Sgshapiro					mode = SM_FORK;
3769157001Sgshapiro					NotFirstDelivery = true;
3770157001Sgshapiro				}
3771157001Sgshapiro			}
3772157001Sgshapiro#endif /* _FFR_DM_ONE */
3773157001Sgshapiro			sendall(ee, mode);
377490792Sgshapiro		}
377590792Sgshapiro		ee->e_to = NULL;
377690792Sgshapiro	}
377790792Sgshapiro
377898121Sgshapiro	/* put back id for SMTP logging in putoutmsg() */
377998121Sgshapiro	oldid = CurEnv->e_id;
378098121Sgshapiro	CurEnv->e_id = id;
378198121Sgshapiro
3782223067Sgshapiro		/* issue success message */
3783157001Sgshapiro#if _FFR_MSG_ACCEPT
3784223067Sgshapiro		if (MessageAccept != NULL && *MessageAccept != '\0')
3785223067Sgshapiro		{
3786223067Sgshapiro			char msg[MAXLINE];
3787157001Sgshapiro
3788223067Sgshapiro			expand(MessageAccept, msg, sizeof(msg), e);
3789223067Sgshapiro			message("250 2.0.0 %s", msg);
3790223067Sgshapiro		}
3791223067Sgshapiro		else
3792157001Sgshapiro#endif /* _FFR_MSG_ACCEPT */
3793223067Sgshapiro		message("250 2.0.0 %s Message accepted for delivery", id);
379498121Sgshapiro	CurEnv->e_id = oldid;
379590792Sgshapiro
379690792Sgshapiro	/* if we just queued, poke it */
379790792Sgshapiro	if (doublequeue)
379890792Sgshapiro	{
379990792Sgshapiro		bool anything_to_send = false;
380090792Sgshapiro
380190792Sgshapiro		sm_getla();
380290792Sgshapiro		for (ee = e; ee != NULL; ee = ee->e_sibling)
380390792Sgshapiro		{
380490792Sgshapiro			if (WILL_BE_QUEUED(ee->e_sendmode))
380590792Sgshapiro				continue;
380690792Sgshapiro			if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
380790792Sgshapiro			{
380890792Sgshapiro				ee->e_sendmode = SM_QUEUE;
380990792Sgshapiro				continue;
381090792Sgshapiro			}
381190792Sgshapiro			else if (QueueMode != QM_QUARANTINE &&
381290792Sgshapiro				 ee->e_quarmsg != NULL)
381390792Sgshapiro			{
381490792Sgshapiro				ee->e_sendmode = SM_QUEUE;
381590792Sgshapiro				continue;
381690792Sgshapiro			}
381790792Sgshapiro			anything_to_send = true;
381890792Sgshapiro
381990792Sgshapiro			/* close all the queue files */
382090792Sgshapiro			closexscript(ee);
382190792Sgshapiro			if (ee->e_dfp != NULL)
382290792Sgshapiro			{
382390792Sgshapiro				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
382490792Sgshapiro				ee->e_dfp = NULL;
382590792Sgshapiro			}
382690792Sgshapiro			unlockqueue(ee);
382790792Sgshapiro		}
382890792Sgshapiro		if (anything_to_send)
382990792Sgshapiro		{
383090792Sgshapiro#if PIPELINING
383190792Sgshapiro			/*
383290792Sgshapiro			**  XXX if we don't do this, we get 250 twice
383390792Sgshapiro			**	because it is also flushed in the child.
383490792Sgshapiro			*/
383590792Sgshapiro
383690792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
383790792Sgshapiro#endif /* PIPELINING */
383890792Sgshapiro			(void) doworklist(e, true, true);
383990792Sgshapiro		}
384090792Sgshapiro	}
384190792Sgshapiro
384290792Sgshapiro  abortmessage:
3843203004Sgshapiro	if (tTd(92, 2))
3844203004Sgshapiro		sm_dprintf("abortmessage: e_id=%s, EF_LOGSENDER=%d, LogLevel=%d\n",
3845203004Sgshapiro			e->e_id, bitset(EF_LOGSENDER, e->e_flags), LogLevel);
384690792Sgshapiro	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
384790792Sgshapiro		logsender(e, NULL);
384890792Sgshapiro	e->e_flags &= ~EF_LOGSENDER;
384990792Sgshapiro
385090792Sgshapiro	/* clean up a bit */
385190792Sgshapiro	smtp->sm_gotmail = false;
385290792Sgshapiro
385390792Sgshapiro	/*
385490792Sgshapiro	**  Call dropenvelope if and only if the envelope is *not*
385590792Sgshapiro	**  being processed by the child process forked by doworklist().
385690792Sgshapiro	*/
385790792Sgshapiro
385890792Sgshapiro	if (aborting || bitset(EF_DISCARD, e->e_flags))
3859203004Sgshapiro		(void) dropenvelope(e, true, false);
386090792Sgshapiro	else
386190792Sgshapiro	{
386290792Sgshapiro		for (ee = e; ee != NULL; ee = ee->e_sibling)
386390792Sgshapiro		{
386490792Sgshapiro			if (!doublequeue &&
386590792Sgshapiro			    QueueMode != QM_QUARANTINE &&
386690792Sgshapiro			    ee->e_quarmsg != NULL)
386790792Sgshapiro			{
3868203004Sgshapiro				(void) dropenvelope(ee, true, false);
386990792Sgshapiro				continue;
387090792Sgshapiro			}
387190792Sgshapiro			if (WILL_BE_QUEUED(ee->e_sendmode))
3872203004Sgshapiro				(void) dropenvelope(ee, true, false);
387390792Sgshapiro		}
387490792Sgshapiro	}
387590792Sgshapiro
387690792Sgshapiro	CurEnv = e;
3877168515Sgshapiro	features = e->e_features;
3878182352Sgshapiro	sm_rpool_free(e->e_rpool);
387990792Sgshapiro	newenvelope(e, e, sm_rpool_new_x(NULL));
388090792Sgshapiro	e->e_flags = BlankEnvelope.e_flags;
3881168515Sgshapiro	e->e_features = features;
388290792Sgshapiro
388390792Sgshapiro	/* restore connection quarantining */
388490792Sgshapiro	if (smtp->sm_quarmsg == NULL)
388590792Sgshapiro	{
388690792Sgshapiro		e->e_quarmsg = NULL;
388790792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
388890792Sgshapiro	}
388990792Sgshapiro	else
389090792Sgshapiro	{
389190792Sgshapiro		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
389290792Sgshapiro		macdefine(&e->e_macro, A_PERM,
389390792Sgshapiro			  macid("{quarantine}"), e->e_quarmsg);
389490792Sgshapiro	}
3895157001Sgshapiro	return rv;
389638032Speter}
389790792Sgshapiro/*
389890792Sgshapiro**  LOGUNDELRCPTS -- log undelivered (or all) recipients.
389990792Sgshapiro**
390090792Sgshapiro**	Parameters:
390190792Sgshapiro**		e -- envelope.
390290792Sgshapiro**		msg -- message for Stat=
390390792Sgshapiro**		level -- log level.
390490792Sgshapiro**		all -- log all recipients.
390590792Sgshapiro**
390690792Sgshapiro**	Returns:
390790792Sgshapiro**		none.
390890792Sgshapiro**
390990792Sgshapiro**	Side Effects:
391090792Sgshapiro**		logs undelivered (or all) recipients
391190792Sgshapiro*/
391290792Sgshapiro
391390792Sgshapirovoid
391490792Sgshapirologundelrcpts(e, msg, level, all)
391590792Sgshapiro	ENVELOPE *e;
391690792Sgshapiro	char *msg;
391790792Sgshapiro	int level;
391890792Sgshapiro	bool all;
391990792Sgshapiro{
392090792Sgshapiro	ADDRESS *a;
392190792Sgshapiro
392290792Sgshapiro	if (LogLevel <= level || msg == NULL || *msg == '\0')
392390792Sgshapiro		return;
392490792Sgshapiro
392590792Sgshapiro	/* Clear $h so relay= doesn't get mislogged by logdelivery() */
392690792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', NULL);
392790792Sgshapiro
392890792Sgshapiro	/* Log who the mail would have gone to */
392990792Sgshapiro	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
393090792Sgshapiro	{
393190792Sgshapiro		if (!QS_IS_UNDELIVERED(a->q_state) && !all)
393290792Sgshapiro			continue;
393390792Sgshapiro		e->e_to = a->q_paddr;
3934203004Sgshapiro		logdelivery(NULL, NULL,
3935203004Sgshapiro#if _FFR_MILTER_ENHSC
3936203004Sgshapiro			    (a->q_status == NULL && e->e_enhsc[0] != '\0')
3937203004Sgshapiro			    ? e->e_enhsc :
3938203004Sgshapiro#endif /* _FFR_MILTER_ENHSC */
3939203004Sgshapiro			    a->q_status,
3940203004Sgshapiro			    msg, NULL, (time_t) 0, e);
394190792Sgshapiro	}
394290792Sgshapiro	e->e_to = NULL;
394390792Sgshapiro}
394490792Sgshapiro/*
394538032Speter**  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
394638032Speter**
394738032Speter**	Parameters:
394838032Speter**		pcounter -- pointer to a counter for this command.
394938032Speter**		maxcount -- maximum value for this counter before we
395038032Speter**			slow down.
395164562Sgshapiro**		waitnow -- sleep now (in this routine)?
395238032Speter**		cname -- command name for logging.
395338032Speter**		e -- the current envelope.
395438032Speter**
395538032Speter**	Returns:
3956132943Sgshapiro**		time to wait,
3957132943Sgshapiro**		STOP_ATTACK if twice as many commands as allowed and
3958132943Sgshapiro**			MaxChildren > 0.
395938032Speter**
396038032Speter**	Side Effects:
396138032Speter**		Slows down if we seem to be under attack.
396238032Speter*/
396338032Speter
396464562Sgshapirostatic time_t
396564562Sgshapirochecksmtpattack(pcounter, maxcount, waitnow, cname, e)
396690792Sgshapiro	volatile unsigned int *pcounter;
3967132943Sgshapiro	unsigned int maxcount;
396864562Sgshapiro	bool waitnow;
396938032Speter	char *cname;
397038032Speter	ENVELOPE *e;
397138032Speter{
397290792Sgshapiro	if (maxcount <= 0)	/* no limit */
397390792Sgshapiro		return (time_t) 0;
397490792Sgshapiro
397538032Speter	if (++(*pcounter) >= maxcount)
397638032Speter	{
3977132943Sgshapiro		unsigned int shift;
397864562Sgshapiro		time_t s;
397964562Sgshapiro
398038032Speter		if (*pcounter == maxcount && LogLevel > 5)
398138032Speter		{
398238032Speter			sm_syslog(LOG_INFO, e->e_id,
3983110560Sgshapiro				  "%s: possible SMTP attack: command=%.40s, count=%u",
398477349Sgshapiro				  CurSmtpClient, cname, *pcounter);
398538032Speter		}
3986132943Sgshapiro		shift = *pcounter - maxcount;
3987132943Sgshapiro		s = 1 << shift;
3988132943Sgshapiro		if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0)
398964562Sgshapiro			s = MAXTIMEOUT;
399090792Sgshapiro
3991132943Sgshapiro#define IS_ATTACK(s)	((MaxChildren > 0 && *pcounter >= maxcount * 2)	\
3992132943Sgshapiro				? STOP_ATTACK : (time_t) s)
3993132943Sgshapiro
399464562Sgshapiro		/* sleep at least 1 second before returning */
399564562Sgshapiro		(void) sleep(*pcounter / maxcount);
399664562Sgshapiro		s -= *pcounter / maxcount;
3997132943Sgshapiro		if (s >= MAXTIMEOUT || s < 0)
3998132943Sgshapiro			s = MAXTIMEOUT;
3999132943Sgshapiro		if (waitnow && s > 0)
400064562Sgshapiro		{
400164562Sgshapiro			(void) sleep(s);
4002132943Sgshapiro			return IS_ATTACK(0);
400364562Sgshapiro		}
4004132943Sgshapiro		return IS_ATTACK(s);
400538032Speter	}
400690792Sgshapiro	return (time_t) 0;
400738032Speter}
400890792Sgshapiro/*
400990792Sgshapiro**  SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
401090792Sgshapiro**
401190792Sgshapiro**	Parameters:
401290792Sgshapiro**		none.
401390792Sgshapiro**
401490792Sgshapiro**	Returns:
401590792Sgshapiro**		nothing.
401690792Sgshapiro**
401790792Sgshapiro**	Side Effects:
401890792Sgshapiro**		may change I/O fd.
401990792Sgshapiro*/
402090792Sgshapiro
402190792Sgshapirostatic void
402290792Sgshapirosetup_smtpd_io()
402390792Sgshapiro{
402490792Sgshapiro	int inchfd, outchfd, outfd;
402590792Sgshapiro
402690792Sgshapiro	inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
402790792Sgshapiro	outchfd  = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
402890792Sgshapiro	outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
402990792Sgshapiro	if (outchfd != outfd)
403090792Sgshapiro	{
403190792Sgshapiro		/* arrange for debugging output to go to remote host */
403290792Sgshapiro		(void) dup2(outchfd, outfd);
403390792Sgshapiro	}
403490792Sgshapiro
403590792Sgshapiro	/*
403690792Sgshapiro	**  if InChannel and OutChannel are stdin/stdout
403790792Sgshapiro	**  and connected to ttys
403890792Sgshapiro	**  and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
403990792Sgshapiro	**  then "chain" them together.
404090792Sgshapiro	*/
404190792Sgshapiro
404290792Sgshapiro	if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
404390792Sgshapiro	    isatty(inchfd) && isatty(outchfd))
404490792Sgshapiro	{
404590792Sgshapiro		int inmode, outmode;
404690792Sgshapiro
404790792Sgshapiro		inmode = fcntl(inchfd, F_GETFL, 0);
404890792Sgshapiro		if (inmode == -1)
404990792Sgshapiro		{
405090792Sgshapiro			if (LogLevel > 11)
405190792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
405290792Sgshapiro					"fcntl(inchfd, F_GETFL) failed: %s",
405390792Sgshapiro					sm_errstring(errno));
405490792Sgshapiro			return;
405590792Sgshapiro		}
405690792Sgshapiro		outmode = fcntl(outchfd, F_GETFL, 0);
405790792Sgshapiro		if (outmode == -1)
405890792Sgshapiro		{
405990792Sgshapiro			if (LogLevel > 11)
406090792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
406190792Sgshapiro					"fcntl(outchfd, F_GETFL) failed: %s",
406290792Sgshapiro					sm_errstring(errno));
406390792Sgshapiro			return;
406490792Sgshapiro		}
406590792Sgshapiro		if (bitset(O_NONBLOCK, inmode) ||
406690792Sgshapiro		    bitset(O_NONBLOCK, outmode) ||
406790792Sgshapiro		    fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
406890792Sgshapiro			return;
406990792Sgshapiro		outmode = fcntl(outchfd, F_GETFL, 0);
407090792Sgshapiro		if (outmode != -1 && bitset(O_NONBLOCK, outmode))
407190792Sgshapiro		{
407290792Sgshapiro			/* changing InChannel also changes OutChannel */
407390792Sgshapiro			sm_io_automode(OutChannel, InChannel);
407490792Sgshapiro			if (tTd(97, 4) && LogLevel > 9)
407590792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
407690792Sgshapiro					  "set automode for I (%d)/O (%d) in SMTP server",
407790792Sgshapiro					  inchfd, outchfd);
407890792Sgshapiro		}
407990792Sgshapiro
408090792Sgshapiro		/* undo change of inchfd */
408190792Sgshapiro		(void) fcntl(inchfd, F_SETFL, inmode);
408290792Sgshapiro	}
408390792Sgshapiro}
408490792Sgshapiro/*
408538032Speter**  SKIPWORD -- skip a fixed word.
408638032Speter**
408738032Speter**	Parameters:
408838032Speter**		p -- place to start looking.
408938032Speter**		w -- word to skip.
409038032Speter**
409138032Speter**	Returns:
409238032Speter**		p following w.
409338032Speter**		NULL on error.
409438032Speter**
409538032Speter**	Side Effects:
409638032Speter**		clobbers the p data area.
409738032Speter*/
409838032Speter
409938032Speterstatic char *
410038032Speterskipword(p, w)
410138032Speter	register char *volatile p;
410238032Speter	char *w;
410338032Speter{
410438032Speter	register char *q;
410538032Speter	char *firstp = p;
410638032Speter
410738032Speter	/* find beginning of word */
410890792Sgshapiro	SKIP_SPACE(p);
410938032Speter	q = p;
411038032Speter
411138032Speter	/* find end of word */
411238032Speter	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
411338032Speter		p++;
411438032Speter	while (isascii(*p) && isspace(*p))
411538032Speter		*p++ = '\0';
411638032Speter	if (*p != ':')
411738032Speter	{
411838032Speter	  syntax:
411964562Sgshapiro		usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
412038032Speter			shortenstring(firstp, MAXSHORTSTR));
412164562Sgshapiro		return NULL;
412238032Speter	}
412338032Speter	*p++ = '\0';
412490792Sgshapiro	SKIP_SPACE(p);
412538032Speter
412638032Speter	if (*p == '\0')
412738032Speter		goto syntax;
412838032Speter
412938032Speter	/* see if the input word matches desired word */
413090792Sgshapiro	if (sm_strcasecmp(q, w))
413138032Speter		goto syntax;
413238032Speter
413364562Sgshapiro	return p;
413438032Speter}
4135159609Sgshapiro
413690792Sgshapiro/*
4137168515Sgshapiro**  RESET_MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
4138168515Sgshapiro**
4139168515Sgshapiro**	Parameters:
4140168515Sgshapiro**		e -- the envelope.
4141168515Sgshapiro**
4142168515Sgshapiro**	Returns:
4143168515Sgshapiro**		none.
4144168515Sgshapiro*/
4145168515Sgshapiro
4146168515Sgshapirovoid
4147168515Sgshapiroreset_mail_esmtp_args(e)
4148168515Sgshapiro	ENVELOPE *e;
4149168515Sgshapiro{
4150168515Sgshapiro	/* "size": no reset */
4151168515Sgshapiro
4152168515Sgshapiro	/* "body" */
4153168515Sgshapiro	SevenBitInput = SevenBitInput_Saved;
4154168515Sgshapiro	e->e_bodytype = NULL;
4155168515Sgshapiro
4156168515Sgshapiro	/* "envid" */
4157168515Sgshapiro	e->e_envid = NULL;
4158168515Sgshapiro	macdefine(&e->e_macro, A_PERM, macid("{dsn_envid}"), NULL);
4159168515Sgshapiro
4160168515Sgshapiro	/* "ret" */
4161173340Sgshapiro	e->e_flags &= ~(EF_RET_PARAM|EF_NO_BODY_RETN);
4162168515Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), NULL);
4163168515Sgshapiro
4164168515Sgshapiro#if SASL
4165168515Sgshapiro	/* "auth" */
4166168515Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), NULL);
4167168515Sgshapiro	e->e_auth_param = "";
4168168515Sgshapiro# if _FFR_AUTH_PASSING
4169168515Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_PERM,
4170168515Sgshapiro				  macid("{auth_author}"), NULL);
4171168515Sgshapiro# endif /* _FFR_AUTH_PASSING */
4172168515Sgshapiro#endif /* SASL */
4173168515Sgshapiro
4174168515Sgshapiro	/* "by" */
4175168515Sgshapiro	e->e_deliver_by = 0;
4176168515Sgshapiro	e->e_dlvr_flag = 0;
4177168515Sgshapiro}
4178168515Sgshapiro
4179168515Sgshapiro/*
418038032Speter**  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
418138032Speter**
418238032Speter**	Parameters:
4183168515Sgshapiro**		a -- address (unused, for compatibility with rcpt_esmtp_args)
418438032Speter**		kp -- the parameter key.
418538032Speter**		vp -- the value of that parameter.
418638032Speter**		e -- the envelope.
418738032Speter**
418838032Speter**	Returns:
418938032Speter**		none.
419038032Speter*/
419138032Speter
4192168515Sgshapirovoid
4193168515Sgshapiromail_esmtp_args(a, kp, vp, e)
4194168515Sgshapiro	ADDRESS *a;
419538032Speter	char *kp;
419638032Speter	char *vp;
419738032Speter	ENVELOPE *e;
419838032Speter{
419990792Sgshapiro	if (sm_strcasecmp(kp, "size") == 0)
420038032Speter	{
420138032Speter		if (vp == NULL)
420238032Speter		{
420364562Sgshapiro			usrerr("501 5.5.2 SIZE requires a value");
420438032Speter			/* NOTREACHED */
420538032Speter		}
420690792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
420790792Sgshapiro		errno = 0;
420871345Sgshapiro		e->e_msgsize = strtol(vp, (char **) NULL, 10);
420966494Sgshapiro		if (e->e_msgsize == LONG_MAX && errno == ERANGE)
421066494Sgshapiro		{
421166494Sgshapiro			usrerr("552 5.2.3 Message size exceeds maximum value");
421266494Sgshapiro			/* NOTREACHED */
421366494Sgshapiro		}
421490792Sgshapiro		if (e->e_msgsize < 0)
421590792Sgshapiro		{
421690792Sgshapiro			usrerr("552 5.2.3 Message size invalid");
421790792Sgshapiro			/* NOTREACHED */
421890792Sgshapiro		}
421938032Speter	}
422090792Sgshapiro	else if (sm_strcasecmp(kp, "body") == 0)
422138032Speter	{
422238032Speter		if (vp == NULL)
422338032Speter		{
422464562Sgshapiro			usrerr("501 5.5.2 BODY requires a value");
422538032Speter			/* NOTREACHED */
422638032Speter		}
422790792Sgshapiro		else if (sm_strcasecmp(vp, "8bitmime") == 0)
422838032Speter		{
422990792Sgshapiro			SevenBitInput = false;
423038032Speter		}
423190792Sgshapiro		else if (sm_strcasecmp(vp, "7bit") == 0)
423238032Speter		{
423390792Sgshapiro			SevenBitInput = true;
423438032Speter		}
423538032Speter		else
423638032Speter		{
423790792Sgshapiro			usrerr("501 5.5.4 Unknown BODY type %s", vp);
423838032Speter			/* NOTREACHED */
423938032Speter		}
424090792Sgshapiro		e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
424138032Speter	}
424290792Sgshapiro	else if (sm_strcasecmp(kp, "envid") == 0)
424338032Speter	{
4244168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
424564562Sgshapiro		{
424664562Sgshapiro			usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
424764562Sgshapiro			/* NOTREACHED */
424864562Sgshapiro		}
424938032Speter		if (vp == NULL)
425038032Speter		{
425164562Sgshapiro			usrerr("501 5.5.2 ENVID requires a value");
425238032Speter			/* NOTREACHED */
425338032Speter		}
425438032Speter		if (!xtextok(vp))
425538032Speter		{
425664562Sgshapiro			usrerr("501 5.5.4 Syntax error in ENVID parameter value");
425738032Speter			/* NOTREACHED */
425838032Speter		}
425938032Speter		if (e->e_envid != NULL)
426038032Speter		{
426164562Sgshapiro			usrerr("501 5.5.0 Duplicate ENVID parameter");
426238032Speter			/* NOTREACHED */
426338032Speter		}
426490792Sgshapiro		e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
426590792Sgshapiro		macdefine(&e->e_macro, A_PERM,
426690792Sgshapiro			macid("{dsn_envid}"), e->e_envid);
426738032Speter	}
426890792Sgshapiro	else if (sm_strcasecmp(kp, "ret") == 0)
426938032Speter	{
4270168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
427164562Sgshapiro		{
427264562Sgshapiro			usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
427364562Sgshapiro			/* NOTREACHED */
427464562Sgshapiro		}
427538032Speter		if (vp == NULL)
427638032Speter		{
427764562Sgshapiro			usrerr("501 5.5.2 RET requires a value");
427838032Speter			/* NOTREACHED */
427938032Speter		}
428038032Speter		if (bitset(EF_RET_PARAM, e->e_flags))
428138032Speter		{
428264562Sgshapiro			usrerr("501 5.5.0 Duplicate RET parameter");
428338032Speter			/* NOTREACHED */
428438032Speter		}
428538032Speter		e->e_flags |= EF_RET_PARAM;
428690792Sgshapiro		if (sm_strcasecmp(vp, "hdrs") == 0)
428738032Speter			e->e_flags |= EF_NO_BODY_RETN;
428890792Sgshapiro		else if (sm_strcasecmp(vp, "full") != 0)
428938032Speter		{
429064562Sgshapiro			usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
429138032Speter			/* NOTREACHED */
429238032Speter		}
429390792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
429438032Speter	}
429590792Sgshapiro#if SASL
429690792Sgshapiro	else if (sm_strcasecmp(kp, "auth") == 0)
429764562Sgshapiro	{
429864562Sgshapiro		int len;
429964562Sgshapiro		char *q;
430064562Sgshapiro		char *auth_param;	/* the value of the AUTH=x */
430164562Sgshapiro		bool saveQuickAbort = QuickAbort;
430264562Sgshapiro		bool saveSuprErrs = SuprErrs;
430390792Sgshapiro		bool saveExitStat = ExitStat;
430464562Sgshapiro
430564562Sgshapiro		if (vp == NULL)
430664562Sgshapiro		{
430764562Sgshapiro			usrerr("501 5.5.2 AUTH= requires a value");
430864562Sgshapiro			/* NOTREACHED */
430964562Sgshapiro		}
431064562Sgshapiro		if (e->e_auth_param != NULL)
431164562Sgshapiro		{
431264562Sgshapiro			usrerr("501 5.5.0 Duplicate AUTH parameter");
431364562Sgshapiro			/* NOTREACHED */
431464562Sgshapiro		}
431564562Sgshapiro		if ((q = strchr(vp, ' ')) != NULL)
431664562Sgshapiro			len = q - vp + 1;
431764562Sgshapiro		else
431864562Sgshapiro			len = strlen(vp) + 1;
431964562Sgshapiro		auth_param = xalloc(len);
432090792Sgshapiro		(void) sm_strlcpy(auth_param, vp, len);
432164562Sgshapiro		if (!xtextok(auth_param))
432264562Sgshapiro		{
432364562Sgshapiro			usrerr("501 5.5.4 Syntax error in AUTH parameter value");
432464562Sgshapiro			/* just a warning? */
432564562Sgshapiro			/* NOTREACHED */
432664562Sgshapiro		}
432764562Sgshapiro
432864562Sgshapiro		/* XXX define this always or only if trusted? */
4329132943Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"),
4330132943Sgshapiro			  auth_param);
433164562Sgshapiro
433264562Sgshapiro		/*
433364562Sgshapiro		**  call Strust_auth to find out whether
433464562Sgshapiro		**  auth_param is acceptable (trusted)
433564562Sgshapiro		**  we shouldn't trust it if not authenticated
433664562Sgshapiro		**  (required by RFC, leave it to ruleset?)
433764562Sgshapiro		*/
433864562Sgshapiro
433990792Sgshapiro		SuprErrs = true;
434090792Sgshapiro		QuickAbort = false;
434164562Sgshapiro		if (strcmp(auth_param, "<>") != 0 &&
4342132943Sgshapiro		     (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM,
4343168515Sgshapiro			      9, NULL, NOQID, NULL) != EX_OK || Errors > 0))
434464562Sgshapiro		{
434564562Sgshapiro			if (tTd(95, 8))
434664562Sgshapiro			{
434764562Sgshapiro				q = e->e_auth_param;
434890792Sgshapiro				sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
4349132943Sgshapiro					auth_param, (q == NULL) ? "" : q);
435064562Sgshapiro			}
435190792Sgshapiro
435264562Sgshapiro			/* not trusted */
435390792Sgshapiro			e->e_auth_param = "<>";
435490792Sgshapiro# if _FFR_AUTH_PASSING
435590792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
435690792Sgshapiro				  macid("{auth_author}"), NULL);
435790792Sgshapiro# endif /* _FFR_AUTH_PASSING */
435864562Sgshapiro		}
435964562Sgshapiro		else
436064562Sgshapiro		{
436164562Sgshapiro			if (tTd(95, 8))
4362132943Sgshapiro				sm_dprintf("auth=\"%.100s\" trusted\n", auth_param);
436390792Sgshapiro			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
436490792Sgshapiro							    auth_param);
436564562Sgshapiro		}
436690792Sgshapiro		sm_free(auth_param); /* XXX */
436777349Sgshapiro
436864562Sgshapiro		/* reset values */
436964562Sgshapiro		Errors = 0;
437064562Sgshapiro		QuickAbort = saveQuickAbort;
437164562Sgshapiro		SuprErrs = saveSuprErrs;
437290792Sgshapiro		ExitStat = saveExitStat;
437364562Sgshapiro	}
437490792Sgshapiro#endif /* SASL */
437590792Sgshapiro#define PRTCHAR(c)	((isascii(c) && isprint(c)) ? (c) : '?')
437690792Sgshapiro
437790792Sgshapiro	/*
437890792Sgshapiro	**  "by" is only accepted if DeliverByMin >= 0.
437990792Sgshapiro	**  We maybe could add this to the list of server_features.
438090792Sgshapiro	*/
438190792Sgshapiro
438290792Sgshapiro	else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
438390792Sgshapiro	{
438490792Sgshapiro		char *s;
438590792Sgshapiro
438690792Sgshapiro		if (vp == NULL)
438790792Sgshapiro		{
438890792Sgshapiro			usrerr("501 5.5.2 BY= requires a value");
438990792Sgshapiro			/* NOTREACHED */
439090792Sgshapiro		}
439190792Sgshapiro		errno = 0;
439290792Sgshapiro		e->e_deliver_by = strtol(vp, &s, 10);
439390792Sgshapiro		if (e->e_deliver_by == LONG_MIN ||
439490792Sgshapiro		    e->e_deliver_by == LONG_MAX ||
439590792Sgshapiro		    e->e_deliver_by > 999999999l ||
439690792Sgshapiro		    e->e_deliver_by < -999999999l)
439790792Sgshapiro		{
439890792Sgshapiro			usrerr("501 5.5.2 BY=%s out of range", vp);
439990792Sgshapiro			/* NOTREACHED */
440090792Sgshapiro		}
440190792Sgshapiro		if (s == NULL || *s != ';')
440290792Sgshapiro		{
440390792Sgshapiro			usrerr("501 5.5.2 BY= missing ';'");
440490792Sgshapiro			/* NOTREACHED */
440590792Sgshapiro		}
440690792Sgshapiro		e->e_dlvr_flag = 0;
440790792Sgshapiro		++s;	/* XXX: spaces allowed? */
440890792Sgshapiro		SKIP_SPACE(s);
440990792Sgshapiro		switch (tolower(*s))
441090792Sgshapiro		{
441190792Sgshapiro		  case 'n':
441290792Sgshapiro			e->e_dlvr_flag = DLVR_NOTIFY;
441390792Sgshapiro			break;
441490792Sgshapiro		  case 'r':
441590792Sgshapiro			e->e_dlvr_flag = DLVR_RETURN;
441690792Sgshapiro			if (e->e_deliver_by <= 0)
441790792Sgshapiro			{
441890792Sgshapiro				usrerr("501 5.5.4 mode R requires BY time > 0");
441990792Sgshapiro				/* NOTREACHED */
442090792Sgshapiro			}
442190792Sgshapiro			if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
442290792Sgshapiro			    e->e_deliver_by < DeliverByMin)
442390792Sgshapiro			{
442490792Sgshapiro				usrerr("555 5.5.2 time %ld less than %ld",
442590792Sgshapiro					e->e_deliver_by, (long) DeliverByMin);
442690792Sgshapiro				/* NOTREACHED */
442790792Sgshapiro			}
442890792Sgshapiro			break;
442990792Sgshapiro		  default:
443090792Sgshapiro			usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
443190792Sgshapiro			/* NOTREACHED */
443290792Sgshapiro		}
443390792Sgshapiro		++s;	/* XXX: spaces allowed? */
443490792Sgshapiro		SKIP_SPACE(s);
443590792Sgshapiro		switch (tolower(*s))
443690792Sgshapiro		{
443790792Sgshapiro		  case 't':
443890792Sgshapiro			e->e_dlvr_flag |= DLVR_TRACE;
443990792Sgshapiro			break;
444090792Sgshapiro		  case '\0':
444190792Sgshapiro			break;
444290792Sgshapiro		  default:
444390792Sgshapiro			usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
444490792Sgshapiro			/* NOTREACHED */
444590792Sgshapiro		}
444690792Sgshapiro
444790792Sgshapiro		/* XXX: check whether more characters follow? */
444890792Sgshapiro	}
444938032Speter	else
445038032Speter	{
445166494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
445238032Speter		/* NOTREACHED */
445338032Speter	}
445438032Speter}
4455168515Sgshapiro
445690792Sgshapiro/*
445738032Speter**  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
445838032Speter**
445938032Speter**	Parameters:
446038032Speter**		a -- the address corresponding to the To: parameter.
446138032Speter**		kp -- the parameter key.
446238032Speter**		vp -- the value of that parameter.
446338032Speter**		e -- the envelope.
446438032Speter**
446538032Speter**	Returns:
446638032Speter**		none.
446738032Speter*/
446838032Speter
4469168515Sgshapirovoid
4470168515Sgshapirorcpt_esmtp_args(a, kp, vp, e)
447138032Speter	ADDRESS *a;
447238032Speter	char *kp;
447338032Speter	char *vp;
447438032Speter	ENVELOPE *e;
447538032Speter{
447690792Sgshapiro	if (sm_strcasecmp(kp, "notify") == 0)
447738032Speter	{
447838032Speter		char *p;
447938032Speter
4480168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
448164562Sgshapiro		{
448264562Sgshapiro			usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
448364562Sgshapiro			/* NOTREACHED */
448464562Sgshapiro		}
448538032Speter		if (vp == NULL)
448638032Speter		{
448764562Sgshapiro			usrerr("501 5.5.2 NOTIFY requires a value");
448838032Speter			/* NOTREACHED */
448938032Speter		}
449038032Speter		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
449138032Speter		a->q_flags |= QHASNOTIFY;
449290792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
449364562Sgshapiro
449490792Sgshapiro		if (sm_strcasecmp(vp, "never") == 0)
449538032Speter			return;
449638032Speter		for (p = vp; p != NULL; vp = p)
449738032Speter		{
4498141858Sgshapiro			char *s;
4499141858Sgshapiro
4500141858Sgshapiro			s = p = strchr(p, ',');
450138032Speter			if (p != NULL)
450238032Speter				*p++ = '\0';
450390792Sgshapiro			if (sm_strcasecmp(vp, "success") == 0)
450438032Speter				a->q_flags |= QPINGONSUCCESS;
450590792Sgshapiro			else if (sm_strcasecmp(vp, "failure") == 0)
450638032Speter				a->q_flags |= QPINGONFAILURE;
450790792Sgshapiro			else if (sm_strcasecmp(vp, "delay") == 0)
450838032Speter				a->q_flags |= QPINGONDELAY;
450938032Speter			else
451038032Speter			{
451164562Sgshapiro				usrerr("501 5.5.4 Bad argument \"%s\"  to NOTIFY",
451238032Speter					vp);
451338032Speter				/* NOTREACHED */
451438032Speter			}
4515141858Sgshapiro			if (s != NULL)
4516141858Sgshapiro				*s = ',';
451738032Speter		}
451838032Speter	}
451990792Sgshapiro	else if (sm_strcasecmp(kp, "orcpt") == 0)
452038032Speter	{
4521249865Sgshapiro		char *p;
4522249865Sgshapiro
4523168515Sgshapiro		if (!bitset(SRV_OFFER_DSN, e->e_features))
452464562Sgshapiro		{
452564562Sgshapiro			usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
452664562Sgshapiro			/* NOTREACHED */
452764562Sgshapiro		}
452838032Speter		if (vp == NULL)
452938032Speter		{
453064562Sgshapiro			usrerr("501 5.5.2 ORCPT requires a value");
453138032Speter			/* NOTREACHED */
453238032Speter		}
4533249865Sgshapiro		if (a->q_orcpt != NULL)
453438032Speter		{
4535249865Sgshapiro			usrerr("501 5.5.0 Duplicate ORCPT parameter");
4536249865Sgshapiro			/* NOTREACHED */
4537249865Sgshapiro		}
4538249865Sgshapiro		p = strchr(vp, ';');
4539249865Sgshapiro		if (p == NULL)
4540249865Sgshapiro		{
454164562Sgshapiro			usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
454238032Speter			/* NOTREACHED */
454338032Speter		}
4544249865Sgshapiro		*p = '\0';
4545249865Sgshapiro		if (!isatom(vp) || !xtextok(p + 1))
454638032Speter		{
4547249865Sgshapiro			*p = ';';
4548249865Sgshapiro			usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
454938032Speter			/* NOTREACHED */
455038032Speter		}
4551249865Sgshapiro		*p = ';';
455290792Sgshapiro		a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
455338032Speter	}
455438032Speter	else
455538032Speter	{
455666494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
455738032Speter		/* NOTREACHED */
455838032Speter	}
455938032Speter}
456090792Sgshapiro/*
456138032Speter**  PRINTVRFYADDR -- print an entry in the verify queue
456238032Speter**
456338032Speter**	Parameters:
456490792Sgshapiro**		a -- the address to print.
456538032Speter**		last -- set if this is the last one.
456638032Speter**		vrfy -- set if this is a VRFY command.
456738032Speter**
456838032Speter**	Returns:
456938032Speter**		none.
457038032Speter**
457138032Speter**	Side Effects:
457238032Speter**		Prints the appropriate 250 codes.
457338032Speter*/
457464562Sgshapiro#define OFFF	(3 + 1 + 5 + 1)	/* offset in fmt: SMTP reply + enh. code */
457538032Speter
457664562Sgshapirostatic void
457738032Speterprintvrfyaddr(a, last, vrfy)
457838032Speter	register ADDRESS *a;
457938032Speter	bool last;
458038032Speter	bool vrfy;
458138032Speter{
458264562Sgshapiro	char fmtbuf[30];
458338032Speter
458438032Speter	if (vrfy && a->q_mailer != NULL &&
458538032Speter	    !bitnset(M_VRFY250, a->q_mailer->m_flags))
4586168515Sgshapiro		(void) sm_strlcpy(fmtbuf, "252", sizeof(fmtbuf));
458738032Speter	else
4588168515Sgshapiro		(void) sm_strlcpy(fmtbuf, "250", sizeof(fmtbuf));
458938032Speter	fmtbuf[3] = last ? ' ' : '-';
4590168515Sgshapiro	(void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof(fmtbuf) - 4);
459138032Speter	if (a->q_fullname == NULL)
459238032Speter	{
459364562Sgshapiro		if ((a->q_mailer == NULL ||
459464562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
459590792Sgshapiro		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
459664562Sgshapiro		    strchr(a->q_user, '@') == NULL)
459790792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
4598168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
459938032Speter		else
460090792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
4601168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
460238032Speter		message(fmtbuf, a->q_user, MyHostName);
460338032Speter	}
460438032Speter	else
460538032Speter	{
460664562Sgshapiro		if ((a->q_mailer == NULL ||
460764562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
460890792Sgshapiro		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
460964562Sgshapiro		    strchr(a->q_user, '@') == NULL)
461090792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
4611168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
461238032Speter		else
461390792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
4614168515Sgshapiro				       sizeof(fmtbuf) - OFFF);
461538032Speter		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
461638032Speter	}
461738032Speter}
461838032Speter
461990792Sgshapiro#if SASL
462090792Sgshapiro/*
462164562Sgshapiro**  SASLMECHS -- get list of possible AUTH mechanisms
462264562Sgshapiro**
462364562Sgshapiro**	Parameters:
462490792Sgshapiro**		conn -- SASL connection info.
462590792Sgshapiro**		mechlist -- output parameter for list of mechanisms.
462664562Sgshapiro**
462764562Sgshapiro**	Returns:
462890792Sgshapiro**		number of mechs.
462964562Sgshapiro*/
463064562Sgshapiro
463164562Sgshapirostatic int
463264562Sgshapirosaslmechs(conn, mechlist)
463364562Sgshapiro	sasl_conn_t *conn;
463464562Sgshapiro	char **mechlist;
463564562Sgshapiro{
463664562Sgshapiro	int len, num, result;
463764562Sgshapiro
463864562Sgshapiro	/* "user" is currently unused */
463998121Sgshapiro# if SASL >= 20000
464098121Sgshapiro	result = sasl_listmech(conn, NULL,
464198121Sgshapiro			       "", " ", "", (const char **) mechlist,
4642120256Sgshapiro			       (unsigned int *)&len, &num);
464398121Sgshapiro# else /* SASL >= 20000 */
464464562Sgshapiro	result = sasl_listmech(conn, "user", /* XXX */
464564562Sgshapiro			       "", " ", "", mechlist,
464690792Sgshapiro			       (unsigned int *)&len, (unsigned int *)&num);
464798121Sgshapiro# endif /* SASL >= 20000 */
464890792Sgshapiro	if (result != SASL_OK)
464964562Sgshapiro	{
465090792Sgshapiro		if (LogLevel > 9)
465190792Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
465290792Sgshapiro				  "AUTH error: listmech=%d, num=%d",
465390792Sgshapiro				  result, num);
465490792Sgshapiro		num = 0;
465590792Sgshapiro	}
465690792Sgshapiro	if (num > 0)
465790792Sgshapiro	{
465864562Sgshapiro		if (LogLevel > 11)
465964562Sgshapiro			sm_syslog(LOG_INFO, NOQID,
466090792Sgshapiro				  "AUTH: available mech=%s, allowed mech=%s",
466164562Sgshapiro				  *mechlist, AuthMechanisms);
466290792Sgshapiro		*mechlist = intersect(AuthMechanisms, *mechlist, NULL);
466364562Sgshapiro	}
466464562Sgshapiro	else
466564562Sgshapiro	{
466690792Sgshapiro		*mechlist = NULL;	/* be paranoid... */
466790792Sgshapiro		if (result == SASL_OK && LogLevel > 9)
466864562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
466990792Sgshapiro				  "AUTH warning: no mechanisms");
467064562Sgshapiro	}
467164562Sgshapiro	return num;
467264562Sgshapiro}
467398121Sgshapiro
467498121Sgshapiro# if SASL >= 20000
467590792Sgshapiro/*
467664562Sgshapiro**  PROXY_POLICY -- define proxy policy for AUTH
467764562Sgshapiro**
467864562Sgshapiro**	Parameters:
467998121Sgshapiro**		conn -- unused.
468090792Sgshapiro**		context -- unused.
468198121Sgshapiro**		requested_user -- authorization identity.
468298121Sgshapiro**		rlen -- authorization identity length.
468390792Sgshapiro**		auth_identity -- authentication identity.
468498121Sgshapiro**		alen -- authentication identity length.
468598121Sgshapiro**		def_realm -- default user realm.
468698121Sgshapiro**		urlen -- user realm length.
468798121Sgshapiro**		propctx -- unused.
468898121Sgshapiro**
468998121Sgshapiro**	Returns:
469098121Sgshapiro**		ok?
469198121Sgshapiro**
469298121Sgshapiro**	Side Effects:
469398121Sgshapiro**		sets {auth_authen} macro.
469498121Sgshapiro*/
469598121Sgshapiro
469698121Sgshapiroint
469798121Sgshapiroproxy_policy(conn, context, requested_user, rlen, auth_identity, alen,
469898121Sgshapiro	     def_realm, urlen, propctx)
469998121Sgshapiro	sasl_conn_t *conn;
470098121Sgshapiro	void *context;
470198121Sgshapiro	const char *requested_user;
470298121Sgshapiro	unsigned rlen;
470398121Sgshapiro	const char *auth_identity;
470498121Sgshapiro	unsigned alen;
470598121Sgshapiro	const char *def_realm;
470698121Sgshapiro	unsigned urlen;
470798121Sgshapiro	struct propctx *propctx;
470898121Sgshapiro{
470998121Sgshapiro	if (auth_identity == NULL)
471098121Sgshapiro		return SASL_FAIL;
471198121Sgshapiro
471298121Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP,
4713223067Sgshapiro		  macid("{auth_authen}"),
4714223067Sgshapiro		  xtextify((char *) auth_identity, "=<>\")"));
471598121Sgshapiro
471698121Sgshapiro	return SASL_OK;
471798121Sgshapiro}
471898121Sgshapiro# else /* SASL >= 20000 */
471998121Sgshapiro
472098121Sgshapiro/*
472198121Sgshapiro**  PROXY_POLICY -- define proxy policy for AUTH
472298121Sgshapiro**
472398121Sgshapiro**	Parameters:
472498121Sgshapiro**		context -- unused.
472598121Sgshapiro**		auth_identity -- authentication identity.
472690792Sgshapiro**		requested_user -- authorization identity.
472790792Sgshapiro**		user -- allowed user (output).
472890792Sgshapiro**		errstr -- possible error string (output).
472964562Sgshapiro**
473064562Sgshapiro**	Returns:
473164562Sgshapiro**		ok?
473264562Sgshapiro*/
473364562Sgshapiro
473464562Sgshapiroint
473564562Sgshapiroproxy_policy(context, auth_identity, requested_user, user, errstr)
473664562Sgshapiro	void *context;
473764562Sgshapiro	const char *auth_identity;
473864562Sgshapiro	const char *requested_user;
473964562Sgshapiro	const char **user;
474064562Sgshapiro	const char **errstr;
474164562Sgshapiro{
474264562Sgshapiro	if (user == NULL || auth_identity == NULL)
474364562Sgshapiro		return SASL_FAIL;
474464562Sgshapiro	*user = newstr(auth_identity);
474564562Sgshapiro	return SASL_OK;
474664562Sgshapiro}
474798121Sgshapiro# endif /* SASL >= 20000 */
474890792Sgshapiro#endif /* SASL */
474964562Sgshapiro
475090792Sgshapiro#if STARTTLS
475190792Sgshapiro/*
475290792Sgshapiro**  INITSRVTLS -- initialize server side TLS
475364562Sgshapiro**
475464562Sgshapiro**	Parameters:
475590792Sgshapiro**		tls_ok -- should tls initialization be done?
475664562Sgshapiro**
475764562Sgshapiro**	Returns:
475890792Sgshapiro**		succeeded?
475964562Sgshapiro**
476064562Sgshapiro**	Side Effects:
476190792Sgshapiro**		sets tls_ok_srv which is a static variable in this module.
476290792Sgshapiro**		Do NOT remove assignments to it!
476364562Sgshapiro*/
476464562Sgshapiro
476566494Sgshapirobool
476690792Sgshapiroinitsrvtls(tls_ok)
476790792Sgshapiro	bool tls_ok;
476864562Sgshapiro{
476990792Sgshapiro	if (!tls_ok)
477090792Sgshapiro		return false;
477164562Sgshapiro
477290792Sgshapiro	/* do NOT remove assignment */
4773203004Sgshapiro	tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, Srv_SSL_Options, true,
4774203004Sgshapiro			     SrvCertFile, SrvKeyFile,
4775203004Sgshapiro			     CACertPath, CACertFile, DHParams);
477690792Sgshapiro	return tls_ok_srv;
477764562Sgshapiro}
477890792Sgshapiro#endif /* STARTTLS */
477964562Sgshapiro/*
478090792Sgshapiro**  SRVFEATURES -- get features for SMTP server
478164562Sgshapiro**
478264562Sgshapiro**	Parameters:
478390792Sgshapiro**		e -- envelope (should be session context).
478490792Sgshapiro**		clientname -- name of client.
478590792Sgshapiro**		features -- default features for this invocation.
478664562Sgshapiro**
478764562Sgshapiro**	Returns:
478890792Sgshapiro**		server features.
478964562Sgshapiro*/
479064562Sgshapiro
479190792Sgshapiro/* table with options: it uses just one character, how about strings? */
479290792Sgshapirostatic struct
479364562Sgshapiro{
479490792Sgshapiro	char		srvf_opt;
479590792Sgshapiro	unsigned int	srvf_flag;
479690792Sgshapiro} srv_feat_table[] =
479764562Sgshapiro{
479890792Sgshapiro	{ 'A',	SRV_OFFER_AUTH	},
4799132943Sgshapiro	{ 'B',	SRV_OFFER_VERB	},
4800132943Sgshapiro	{ 'C',	SRV_REQ_SEC	},
4801132943Sgshapiro	{ 'D',	SRV_OFFER_DSN	},
4802132943Sgshapiro	{ 'E',	SRV_OFFER_ETRN	},
4803132943Sgshapiro	{ 'L',	SRV_REQ_AUTH	},
480490792Sgshapiro#if PIPELINING
480590792Sgshapiro# if _FFR_NO_PIPE
480690792Sgshapiro	{ 'N',	SRV_NO_PIPE	},
480790792Sgshapiro# endif /* _FFR_NO_PIPE */
480890792Sgshapiro	{ 'P',	SRV_OFFER_PIPE	},
480990792Sgshapiro#endif /* PIPELINING */
4810132943Sgshapiro	{ 'R',	SRV_VRFY_CLT	},	/* same as V; not documented */
481190792Sgshapiro	{ 'S',	SRV_OFFER_TLS	},
481290792Sgshapiro/*	{ 'T',	SRV_TMP_FAIL	},	*/
481390792Sgshapiro	{ 'V',	SRV_VRFY_CLT	},
4814132943Sgshapiro	{ 'X',	SRV_OFFER_EXPN	},
481590792Sgshapiro/*	{ 'Y',	SRV_OFFER_VRFY	},	*/
481690792Sgshapiro	{ '\0',	SRV_NONE	}
481790792Sgshapiro};
481864562Sgshapiro
481990792Sgshapirostatic unsigned int
482090792Sgshapirosrvfeatures(e, clientname, features)
482190792Sgshapiro	ENVELOPE *e;
482290792Sgshapiro	char *clientname;
482390792Sgshapiro	unsigned int features;
482477349Sgshapiro{
482590792Sgshapiro	int r, i, j;
482690792Sgshapiro	char **pvp, c, opt;
482790792Sgshapiro	char pvpbuf[PSBUFSIZE];
482877349Sgshapiro
482990792Sgshapiro	pvp = NULL;
483090792Sgshapiro	r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
483190792Sgshapiro		  sizeof(pvpbuf));
483290792Sgshapiro	if (r != EX_OK)
483390792Sgshapiro		return features;
483490792Sgshapiro	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
483590792Sgshapiro		return features;
483690792Sgshapiro	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
483790792Sgshapiro		return SRV_TMP_FAIL;
483877349Sgshapiro
483964562Sgshapiro	/*
484090792Sgshapiro	**  General rule (see sendmail.h, d_flags):
484190792Sgshapiro	**  lower case: required/offered, upper case: Not required/available
484290792Sgshapiro	**
484390792Sgshapiro	**  Since we can change some features per daemon, we have both
484490792Sgshapiro	**  cases here: turn on/off a feature.
484564562Sgshapiro	*/
484664562Sgshapiro
484790792Sgshapiro	for (i = 1; pvp[i] != NULL; i++)
484864562Sgshapiro	{
484990792Sgshapiro		c = pvp[i][0];
485090792Sgshapiro		j = 0;
485190792Sgshapiro		for (;;)
485264562Sgshapiro		{
485390792Sgshapiro			if ((opt = srv_feat_table[j].srvf_opt) == '\0')
485464562Sgshapiro			{
485590792Sgshapiro				if (LogLevel > 9)
485690792Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
485790792Sgshapiro						  "srvfeatures: unknown feature %s",
485890792Sgshapiro						  pvp[i]);
485990792Sgshapiro				break;
486064562Sgshapiro			}
486190792Sgshapiro			if (c == opt)
486264562Sgshapiro			{
486390792Sgshapiro				features &= ~(srv_feat_table[j].srvf_flag);
486490792Sgshapiro				break;
486564562Sgshapiro			}
486690792Sgshapiro			if (c == tolower(opt))
486764562Sgshapiro			{
486890792Sgshapiro				features |= srv_feat_table[j].srvf_flag;
486990792Sgshapiro				break;
487064562Sgshapiro			}
487190792Sgshapiro			++j;
487264562Sgshapiro		}
487364562Sgshapiro	}
487490792Sgshapiro	return features;
487564562Sgshapiro}
487664562Sgshapiro
487790792Sgshapiro/*
487838032Speter**  HELP -- implement the HELP command.
487938032Speter**
488038032Speter**	Parameters:
488138032Speter**		topic -- the topic we want help for.
488290792Sgshapiro**		e -- envelope.
488338032Speter**
488438032Speter**	Returns:
488538032Speter**		none.
488638032Speter**
488738032Speter**	Side Effects:
488838032Speter**		outputs the help file to message output.
488938032Speter*/
489064562Sgshapiro#define HELPVSTR	"#vers	"
489164562Sgshapiro#define HELPVERSION	2
489238032Speter
489338032Spetervoid
489464562Sgshapirohelp(topic, e)
489538032Speter	char *topic;
489664562Sgshapiro	ENVELOPE *e;
489738032Speter{
489890792Sgshapiro	register SM_FILE_T *hf;
489964562Sgshapiro	register char *p;
490038032Speter	int len;
490138032Speter	bool noinfo;
490290792Sgshapiro	bool first = true;
490364562Sgshapiro	long sff = SFF_OPENASROOT|SFF_REGONLY;
490438032Speter	char buf[MAXLINE];
490564562Sgshapiro	char inp[MAXLINE];
490664562Sgshapiro	static int foundvers = -1;
490738032Speter	extern char Version[];
490838032Speter
490938032Speter	if (DontLockReadFiles)
491038032Speter		sff |= SFF_NOLOCK;
491164562Sgshapiro	if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
491238032Speter		sff |= SFF_SAFEDIRPATH;
491338032Speter
491438032Speter	if (HelpFile == NULL ||
491538032Speter	    (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
491638032Speter	{
491738032Speter		/* no help */
491838032Speter		errno = 0;
491964562Sgshapiro		message("502 5.3.0 Sendmail %s -- HELP not implemented",
492064562Sgshapiro			Version);
492138032Speter		return;
492238032Speter	}
492338032Speter
492438032Speter	if (topic == NULL || *topic == '\0')
492538032Speter	{
492638032Speter		topic = "smtp";
492790792Sgshapiro		noinfo = false;
492838032Speter	}
492938032Speter	else
493038032Speter	{
493138032Speter		makelower(topic);
493290792Sgshapiro		noinfo = true;
493338032Speter	}
493438032Speter
493538032Speter	len = strlen(topic);
493638032Speter
4937249865Sgshapiro	while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
493838032Speter	{
493964562Sgshapiro		if (buf[0] == '#')
494064562Sgshapiro		{
494164562Sgshapiro			if (foundvers < 0 &&
494264562Sgshapiro			    strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
494364562Sgshapiro			{
494464562Sgshapiro				int h;
494564562Sgshapiro
494690792Sgshapiro				if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
494790792Sgshapiro						 &h) == 1)
494864562Sgshapiro					foundvers = h;
494964562Sgshapiro			}
495064562Sgshapiro			continue;
495164562Sgshapiro		}
495238032Speter		if (strncmp(buf, topic, len) == 0)
495338032Speter		{
495464562Sgshapiro			if (first)
495564562Sgshapiro			{
495690792Sgshapiro				first = false;
495738032Speter
495864562Sgshapiro				/* print version if no/old vers# in file */
495964562Sgshapiro				if (foundvers < 2 && !noinfo)
496064562Sgshapiro					message("214-2.0.0 This is Sendmail version %s", Version);
496164562Sgshapiro			}
496264562Sgshapiro			p = strpbrk(buf, " \t");
496338032Speter			if (p == NULL)
496464562Sgshapiro				p = buf + strlen(buf) - 1;
496538032Speter			else
496638032Speter				p++;
496790792Sgshapiro			fixcrlf(p, true);
496864562Sgshapiro			if (foundvers >= 2)
496964562Sgshapiro			{
4970168515Sgshapiro				char *lbp;
4971168515Sgshapiro				int lbs = sizeof(buf) - (p - buf);
4972168515Sgshapiro
4973168515Sgshapiro				lbp = translate_dollars(p, p, &lbs);
4974168515Sgshapiro				expand(lbp, inp, sizeof(inp), e);
4975168515Sgshapiro				if (p != lbp)
4976168515Sgshapiro					sm_free(lbp);
497764562Sgshapiro				p = inp;
497864562Sgshapiro			}
497964562Sgshapiro			message("214-2.0.0 %s", p);
498090792Sgshapiro			noinfo = false;
498138032Speter		}
498238032Speter	}
498338032Speter
498438032Speter	if (noinfo)
498564562Sgshapiro		message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
498638032Speter	else
498764562Sgshapiro		message("214 2.0.0 End of HELP info");
498864562Sgshapiro
498964562Sgshapiro	if (foundvers != 0 && foundvers < HELPVERSION)
499064562Sgshapiro	{
499164562Sgshapiro		if (LogLevel > 1)
499264562Sgshapiro			sm_syslog(LOG_WARNING, e->e_id,
499364562Sgshapiro				  "%s too old (require version %d)",
499464562Sgshapiro				  HelpFile, HELPVERSION);
499564562Sgshapiro
499664562Sgshapiro		/* avoid log next time */
499764562Sgshapiro		foundvers = 0;
499864562Sgshapiro	}
499964562Sgshapiro
500090792Sgshapiro	(void) sm_io_close(hf, SM_TIME_DEFAULT);
500138032Speter}
5002120256Sgshapiro
5003120256Sgshapiro#if SASL
5004120256Sgshapiro/*
5005120256Sgshapiro**  RESET_SASLCONN -- reset SASL connection data
5006120256Sgshapiro**
5007120256Sgshapiro**	Parameters:
5008120256Sgshapiro**		conn -- SASL connection context
5009120256Sgshapiro**		hostname -- host name
5010120256Sgshapiro**		various connection data
5011120256Sgshapiro**
5012120256Sgshapiro**	Returns:
5013120256Sgshapiro**		SASL result
5014120256Sgshapiro*/
5015120256Sgshapiro
5016120256Sgshapirostatic int
5017147078Sgshapiroreset_saslconn(sasl_conn_t **conn, char *hostname,
5018120256Sgshapiro# if SASL >= 20000
5019120256Sgshapiro	       char *remoteip, char *localip,
5020120256Sgshapiro	       char *auth_id, sasl_ssf_t * ext_ssf)
5021120256Sgshapiro# else /* SASL >= 20000 */
5022147078Sgshapiro	       struct sockaddr_in *saddr_r, struct sockaddr_in *saddr_l,
5023120256Sgshapiro	       sasl_external_properties_t * ext_ssf)
5024120256Sgshapiro# endif /* SASL >= 20000 */
5025120256Sgshapiro{
5026120256Sgshapiro	int result;
5027120256Sgshapiro
5028120256Sgshapiro	sasl_dispose(conn);
5029120256Sgshapiro# if SASL >= 20000
5030120256Sgshapiro	result = sasl_server_new("smtp", hostname, NULL, NULL, NULL,
5031120256Sgshapiro				 NULL, 0, conn);
5032120256Sgshapiro# elif SASL > 10505
5033120256Sgshapiro	/* use empty realm: only works in SASL > 1.5.5 */
5034120256Sgshapiro	result = sasl_server_new("smtp", hostname, "", NULL, 0, conn);
5035120256Sgshapiro# else /* SASL >= 20000 */
5036120256Sgshapiro	/* use no realm -> realm is set to hostname by SASL lib */
5037120256Sgshapiro	result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
5038120256Sgshapiro				 conn);
5039120256Sgshapiro# endif /* SASL >= 20000 */
5040120256Sgshapiro	if (result != SASL_OK)
5041120256Sgshapiro		return result;
5042120256Sgshapiro
5043120256Sgshapiro# if SASL >= 20000
5044120256Sgshapiro#  if NETINET || NETINET6
5045147078Sgshapiro	if (remoteip != NULL && *remoteip != '\0')
5046120256Sgshapiro		result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip);
5047120256Sgshapiro	if (result != SASL_OK)
5048120256Sgshapiro		return result;
5049120256Sgshapiro
5050147078Sgshapiro	if (localip != NULL && *localip != '\0')
5051120256Sgshapiro		result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip);
5052120256Sgshapiro	if (result != SASL_OK)
5053120256Sgshapiro		return result;
5054120256Sgshapiro#  endif /* NETINET || NETINET6 */
5055120256Sgshapiro
5056120256Sgshapiro	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
5057120256Sgshapiro	if (result != SASL_OK)
5058120256Sgshapiro		return result;
5059120256Sgshapiro
5060120256Sgshapiro	result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id);
5061120256Sgshapiro	if (result != SASL_OK)
5062120256Sgshapiro		return result;
5063120256Sgshapiro# else /* SASL >= 20000 */
5064120256Sgshapiro#  if NETINET
5065120256Sgshapiro	if (saddr_r != NULL)
5066120256Sgshapiro		result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r);
5067120256Sgshapiro	if (result != SASL_OK)
5068120256Sgshapiro		return result;
5069120256Sgshapiro
5070120256Sgshapiro	if (saddr_l != NULL)
5071120256Sgshapiro		result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l);
5072120256Sgshapiro	if (result != SASL_OK)
5073120256Sgshapiro		return result;
5074120256Sgshapiro#  endif /* NETINET */
5075120256Sgshapiro
5076120256Sgshapiro	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
5077120256Sgshapiro	if (result != SASL_OK)
5078120256Sgshapiro		return result;
5079120256Sgshapiro# endif /* SASL >= 20000 */
5080120256Sgshapiro	return SASL_OK;
5081120256Sgshapiro}
5082120256Sgshapiro#endif /* SASL */
5083