srvrsmtp.c revision 132943
138032Speter/*
2132943Sgshapiro * Copyright (c) 1998-2004 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1464562Sgshapiro#include <sendmail.h>
1590792Sgshapiro#if MILTER
16132943Sgshapiro# include <libmilter/mfapi.h>
1790792Sgshapiro# include <libmilter/mfdef.h>
1890792Sgshapiro#endif /* MILTER */
1964562Sgshapiro
20132943SgshapiroSM_RCSID("@(#)$Id: srvrsmtp.c,v 8.900 2004/07/08 23:29:33 ca Exp $")
2164562Sgshapiro
22132943Sgshapiro#include <sys/time.h>
23132943Sgshapiro#include <sm/fdset.h>
24132943Sgshapiro
2590792Sgshapiro#if SASL || STARTTLS
2690792Sgshapiro# include "sfsasl.h"
2790792Sgshapiro#endif /* SASL || STARTTLS */
2890792Sgshapiro#if SASL
2990792Sgshapiro# define ENC64LEN(l)	(((l) + 2) * 4 / 3 + 1)
3064562Sgshapirostatic int saslmechs __P((sasl_conn_t *, char **));
3190792Sgshapiro#endif /* SASL */
3290792Sgshapiro#if STARTTLS
3390792Sgshapiro# include <sysexits.h>
3438032Speter
3590792Sgshapirostatic SSL_CTX	*srv_ctx = NULL;	/* TLS server context */
3690792Sgshapirostatic SSL	*srv_ssl = NULL;	/* per connection context */
3738032Speter
3890792Sgshapirostatic bool	tls_ok_srv = false;
3990792Sgshapiro
4090792Sgshapiroextern void	tls_set_verify __P((SSL_CTX *, SSL *, bool));
4190792Sgshapiro# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
4290792Sgshapiro				bitset(SRV_VRFY_CLT, features))
4390792Sgshapiro#endif /* STARTTLS */
4490792Sgshapiro
4590792Sgshapiro/* server features */
4690792Sgshapiro#define SRV_NONE	0x0000	/* none... */
4790792Sgshapiro#define SRV_OFFER_TLS	0x0001	/* offer STARTTLS */
4890792Sgshapiro#define SRV_VRFY_CLT	0x0002	/* request a cert */
4990792Sgshapiro#define SRV_OFFER_AUTH	0x0004	/* offer AUTH */
5090792Sgshapiro#define SRV_OFFER_ETRN	0x0008	/* offer ETRN */
5190792Sgshapiro#define SRV_OFFER_VRFY	0x0010	/* offer VRFY (not yet used) */
5290792Sgshapiro#define SRV_OFFER_EXPN	0x0020	/* offer EXPN */
5390792Sgshapiro#define SRV_OFFER_VERB	0x0040	/* offer VERB */
5490792Sgshapiro#define SRV_OFFER_DSN	0x0080	/* offer DSN */
5590792Sgshapiro#if PIPELINING
5690792Sgshapiro# define SRV_OFFER_PIPE	0x0100	/* offer PIPELINING */
5790792Sgshapiro# if _FFR_NO_PIPE
5890792Sgshapiro#  define SRV_NO_PIPE	0x0200	/* disable PIPELINING, sleep if used */
5990792Sgshapiro# endif /* _FFR_NO_PIPE */
6090792Sgshapiro#endif /* PIPELINING */
6190792Sgshapiro#define SRV_REQ_AUTH	0x0400	/* require AUTH */
62132943Sgshapiro#define SRV_REQ_SEC	0x0800	/* require security - equiv to AuthOptions=p */
6390792Sgshapiro#define SRV_TMP_FAIL	0x1000	/* ruleset caused a temporary failure */
6490792Sgshapiro
6590792Sgshapirostatic unsigned int	srvfeatures __P((ENVELOPE *, char *, unsigned int));
6690792Sgshapiro
67132943Sgshapiro#define	STOP_ATTACK	((time_t) -1)
68132943Sgshapirostatic time_t	checksmtpattack __P((volatile unsigned int *, unsigned int,
69132943Sgshapiro				     bool, char *, ENVELOPE *));
7064562Sgshapirostatic void	mail_esmtp_args __P((char *, char *, ENVELOPE *));
7164562Sgshapirostatic void	printvrfyaddr __P((ADDRESS *, bool, bool));
7264562Sgshapirostatic void	rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
7364562Sgshapirostatic char	*skipword __P((char *volatile, char *));
7490792Sgshapirostatic void	setup_smtpd_io __P((void));
75120256Sgshapiro
76120256Sgshapiro#if SASL
77120256Sgshapiro# if SASL >= 20000
78120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
79120256Sgshapiro				char *_remoteip, char *_localip,
80120256Sgshapiro				char *_auth_id, sasl_ssf_t *_ext_ssf));
81120256Sgshapiro
82120256Sgshapiro# define RESET_SASLCONN	\
83132943Sgshapiro	result = reset_saslconn(&conn, AuthRealm, remoteip, localip, auth_id, \
84120256Sgshapiro				&ext_ssf);	\
85120256Sgshapiro	if (result != SASL_OK)			\
86120256Sgshapiro	{					\
87120256Sgshapiro		/* This is pretty fatal */	\
88120256Sgshapiro		goto doquit;			\
89120256Sgshapiro	}
90120256Sgshapiro
91120256Sgshapiro# else /* SASL >= 20000 */
92120256Sgshapirostatic int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
93120256Sgshapiro				struct sockaddr_in *_saddr_r,
94120256Sgshapiro				struct sockaddr_in *_saddr_l,
95120256Sgshapiro				sasl_external_properties_t *_ext_ssf));
96120256Sgshapiro# define RESET_SASLCONN	\
97132943Sgshapiro	result = reset_saslconn(&conn, AuthRealm, &saddr_r, &saddr_l, &ext_ssf); \
98120256Sgshapiro	if (result != SASL_OK)			\
99120256Sgshapiro	{					\
100120256Sgshapiro		/* This is pretty fatal */	\
101120256Sgshapiro		goto doquit;			\
102120256Sgshapiro	}
103120256Sgshapiro
104120256Sgshapiro# endif /* SASL >= 20000 */
105120256Sgshapiro#endif /* SASL */
106120256Sgshapiro
10764562Sgshapiroextern ENVELOPE	BlankEnvelope;
10838032Speter
109132943Sgshapiro#define NBADRCPTS						\
110132943Sgshapiro	do							\
111132943Sgshapiro	{							\
112132943Sgshapiro		char buf[16];					\
113132943Sgshapiro		(void) sm_snprintf(buf, sizeof buf, "%d",	\
114132943Sgshapiro			BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \
115132943Sgshapiro				? n_badrcpts - 1 : n_badrcpts);	\
116132943Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
117132943Sgshapiro	} while (0)
118132943Sgshapiro
11990792Sgshapiro#define SKIP_SPACE(s)	while (isascii(*s) && isspace(*s))	\
12090792Sgshapiro				(s)++
12190792Sgshapiro
12238032Speter/*
12338032Speter**  SMTP -- run the SMTP protocol.
12438032Speter**
12538032Speter**	Parameters:
12638032Speter**		nullserver -- if non-NULL, rejection message for
12790792Sgshapiro**			(almost) all SMTP commands.
12890792Sgshapiro**		d_flags -- daemon flags
12938032Speter**		e -- the envelope.
13038032Speter**
13138032Speter**	Returns:
13238032Speter**		never.
13338032Speter**
13438032Speter**	Side Effects:
13590792Sgshapiro**		Reads commands from the input channel and processes them.
13638032Speter*/
13738032Speter
13890792Sgshapiro/*
13990792Sgshapiro**  Notice: The smtp server doesn't have a session context like the client
14090792Sgshapiro**	side has (mci). Therefore some data (session oriented) is allocated
14190792Sgshapiro**	or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
14290792Sgshapiro**	This should be fixed in a successor version.
14390792Sgshapiro*/
14490792Sgshapiro
14538032Speterstruct cmd
14638032Speter{
14764562Sgshapiro	char	*cmd_name;	/* command name */
14864562Sgshapiro	int	cmd_code;	/* internal code, see below */
14938032Speter};
15038032Speter
15164562Sgshapiro/* values for cmd_code */
15290792Sgshapiro#define CMDERROR	0	/* bad command */
15390792Sgshapiro#define CMDMAIL	1	/* mail -- designate sender */
15490792Sgshapiro#define CMDRCPT	2	/* rcpt -- designate recipient */
15590792Sgshapiro#define CMDDATA	3	/* data -- send message text */
15690792Sgshapiro#define CMDRSET	4	/* rset -- reset state */
15790792Sgshapiro#define CMDVRFY	5	/* vrfy -- verify address */
15890792Sgshapiro#define CMDEXPN	6	/* expn -- expand address */
15990792Sgshapiro#define CMDNOOP	7	/* noop -- do nothing */
16090792Sgshapiro#define CMDQUIT	8	/* quit -- close connection and die */
16190792Sgshapiro#define CMDHELO	9	/* helo -- be polite */
16290792Sgshapiro#define CMDHELP	10	/* help -- give usage info */
16390792Sgshapiro#define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
16490792Sgshapiro#define CMDETRN	12	/* etrn -- flush queue */
16590792Sgshapiro#if SASL
16690792Sgshapiro# define CMDAUTH	13	/* auth -- SASL authenticate */
16790792Sgshapiro#endif /* SASL */
16890792Sgshapiro#if STARTTLS
16990792Sgshapiro# define CMDSTLS	14	/* STARTTLS -- start TLS session */
17090792Sgshapiro#endif /* STARTTLS */
17138032Speter/* non-standard commands */
17290792Sgshapiro#define CMDVERB	17	/* verb -- go into verbose mode */
17364562Sgshapiro/* unimplemented commands from RFC 821 */
17490792Sgshapiro#define CMDUNIMPL	19	/* unimplemented rfc821 commands */
17538032Speter/* use this to catch and log "door handle" attempts on your system */
17690792Sgshapiro#define CMDLOGBOGUS	23	/* bogus command that should be logged */
17738032Speter/* debugging-only commands, only enabled if SMTPDEBUG is defined */
17890792Sgshapiro#define CMDDBGQSHOW	24	/* showq -- show send queue */
17990792Sgshapiro#define CMDDBGDEBUG	25	/* debug -- set debug mode */
18038032Speter
18166494Sgshapiro/*
18290792Sgshapiro**  Note: If you change this list, remember to update 'helpfile'
18366494Sgshapiro*/
18466494Sgshapiro
18538032Speterstatic struct cmd	CmdTab[] =
18638032Speter{
18738032Speter	{ "mail",	CMDMAIL		},
18838032Speter	{ "rcpt",	CMDRCPT		},
18938032Speter	{ "data",	CMDDATA		},
19038032Speter	{ "rset",	CMDRSET		},
19138032Speter	{ "vrfy",	CMDVRFY		},
19238032Speter	{ "expn",	CMDEXPN		},
19338032Speter	{ "help",	CMDHELP		},
19438032Speter	{ "noop",	CMDNOOP		},
19538032Speter	{ "quit",	CMDQUIT		},
19638032Speter	{ "helo",	CMDHELO		},
19738032Speter	{ "ehlo",	CMDEHLO		},
19838032Speter	{ "etrn",	CMDETRN		},
19938032Speter	{ "verb",	CMDVERB		},
20064562Sgshapiro	{ "send",	CMDUNIMPL	},
20164562Sgshapiro	{ "saml",	CMDUNIMPL	},
20264562Sgshapiro	{ "soml",	CMDUNIMPL	},
20364562Sgshapiro	{ "turn",	CMDUNIMPL	},
20490792Sgshapiro#if SASL
20564562Sgshapiro	{ "auth",	CMDAUTH,	},
20690792Sgshapiro#endif /* SASL */
20790792Sgshapiro#if STARTTLS
20864562Sgshapiro	{ "starttls",	CMDSTLS,	},
20990792Sgshapiro#endif /* STARTTLS */
21038032Speter    /* remaining commands are here only to trap and log attempts to use them */
21138032Speter	{ "showq",	CMDDBGQSHOW	},
21238032Speter	{ "debug",	CMDDBGDEBUG	},
21338032Speter	{ "wiz",	CMDLOGBOGUS	},
21438032Speter
21538032Speter	{ NULL,		CMDERROR	}
21638032Speter};
21738032Speter
21864562Sgshapirostatic char	*CurSmtpClient;		/* who's at the other end of channel */
21938032Speter
22090792Sgshapiro#ifndef MAXBADCOMMANDS
22190792Sgshapiro# define MAXBADCOMMANDS 25	/* maximum number of bad commands */
22294334Sgshapiro#endif /* ! MAXBADCOMMANDS */
22390792Sgshapiro#ifndef MAXNOOPCOMMANDS
22490792Sgshapiro# define MAXNOOPCOMMANDS 20	/* max "noise" commands before slowdown */
22594334Sgshapiro#endif /* ! MAXNOOPCOMMANDS */
22690792Sgshapiro#ifndef MAXHELOCOMMANDS
22790792Sgshapiro# define MAXHELOCOMMANDS 3	/* max HELO/EHLO commands before slowdown */
22894334Sgshapiro#endif /* ! MAXHELOCOMMANDS */
22990792Sgshapiro#ifndef MAXVRFYCOMMANDS
23090792Sgshapiro# define MAXVRFYCOMMANDS 6	/* max VRFY/EXPN commands before slowdown */
23194334Sgshapiro#endif /* ! MAXVRFYCOMMANDS */
23290792Sgshapiro#ifndef MAXETRNCOMMANDS
23390792Sgshapiro# define MAXETRNCOMMANDS 8	/* max ETRN commands before slowdown */
23494334Sgshapiro#endif /* ! MAXETRNCOMMANDS */
23590792Sgshapiro#ifndef MAXTIMEOUT
23690792Sgshapiro# define MAXTIMEOUT (4 * 60)	/* max timeout for bad commands */
23794334Sgshapiro#endif /* ! MAXTIMEOUT */
23838032Speter
239132943Sgshapiro/*
240132943Sgshapiro**  Maximum shift value to compute timeout for bad commands.
241132943Sgshapiro**  This introduces an upper limit of 2^MAXSHIFT for the timeout.
242132943Sgshapiro*/
243132943Sgshapiro
244132943Sgshapiro#ifndef MAXSHIFT
245132943Sgshapiro# define MAXSHIFT 8
246132943Sgshapiro#endif /* ! MAXSHIFT */
247132943Sgshapiro#if MAXSHIFT > 31
248132943Sgshapiro ERROR _MAXSHIFT > 31 is invalid
249132943Sgshapiro#endif /* MAXSHIFT */
250132943Sgshapiro
251132943Sgshapiro
252132943Sgshapiro#if MAXBADCOMMANDS > 0
253132943Sgshapiro# define STOP_IF_ATTACK(r)	do		\
254132943Sgshapiro	{					\
255132943Sgshapiro		if ((r) == STOP_ATTACK)		\
256132943Sgshapiro			goto stopattack;	\
257132943Sgshapiro	} while (0)
258132943Sgshapiro
259132943Sgshapiro#else /* MAXBADCOMMANDS > 0 */
260132943Sgshapiro# define STOP_IF_ATTACK(r)	r
261132943Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
262132943Sgshapiro
263132943Sgshapiro
26490792Sgshapiro#if SM_HEAP_CHECK
26590792Sgshapirostatic SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
26690792Sgshapiro	"@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
26790792Sgshapiro#endif /* SM_HEAP_CHECK */
26838032Speter
26990792Sgshapirotypedef struct
27090792Sgshapiro{
27190792Sgshapiro	bool	sm_gotmail;	/* mail command received */
27290792Sgshapiro	unsigned int sm_nrcpts;	/* number of successful RCPT commands */
27390792Sgshapiro	bool	sm_discard;
27490792Sgshapiro#if MILTER
27590792Sgshapiro	bool	sm_milterize;
27690792Sgshapiro	bool	sm_milterlist;	/* any filters in the list? */
27790792Sgshapiro#endif /* MILTER */
27890792Sgshapiro	char	*sm_quarmsg;	/* carry quarantining across messages */
27990792Sgshapiro} SMTP_T;
28090792Sgshapiro
281132943Sgshapirostatic bool	smtp_data __P((SMTP_T *, ENVELOPE *));
28290792Sgshapiro
283132943Sgshapiro#define MSG_TEMPFAIL "451 4.3.2 Please try again later"
28490792Sgshapiro
28590792Sgshapiro#if MILTER
28690792Sgshapiro# define MILTER_ABORT(e)	milter_abort((e))
287110560Sgshapiro
28890792Sgshapiro# define MILTER_REPLY(str)						\
28990792Sgshapiro	{								\
29090792Sgshapiro		int savelogusrerrs = LogUsrErrs;			\
29190792Sgshapiro									\
29290792Sgshapiro		switch (state)						\
29390792Sgshapiro		{							\
29490792Sgshapiro		  case SMFIR_REPLYCODE:					\
29590792Sgshapiro			if (MilterLogLevel > 3)				\
29690792Sgshapiro			{						\
29790792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
29890792Sgshapiro					  "Milter: %s=%s, reject=%s",	\
29990792Sgshapiro					  str, addr, response);		\
30090792Sgshapiro				LogUsrErrs = false;			\
30190792Sgshapiro			}						\
302132943Sgshapiro			if (strncmp(response, "421 ", 4) == 0)		\
303132943Sgshapiro			{						\
304132943Sgshapiro				bool tsave = QuickAbort;		\
305132943Sgshapiro									\
306132943Sgshapiro				QuickAbort = false;			\
307132943Sgshapiro				usrerr(response);			\
308132943Sgshapiro				QuickAbort = tsave;			\
309132943Sgshapiro				e->e_sendqueue = NULL;			\
310132943Sgshapiro				goto doquit;				\
311132943Sgshapiro			}						\
312132943Sgshapiro			else						\
313132943Sgshapiro				usrerr(response);			\
31490792Sgshapiro			break;						\
31590792Sgshapiro									\
31690792Sgshapiro		  case SMFIR_REJECT:					\
31790792Sgshapiro			if (MilterLogLevel > 3)				\
31890792Sgshapiro			{						\
31990792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
32090792Sgshapiro					  "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
32190792Sgshapiro					  str, addr);			\
32290792Sgshapiro				LogUsrErrs = false;			\
32390792Sgshapiro			}						\
32490792Sgshapiro			usrerr("550 5.7.1 Command rejected");		\
32590792Sgshapiro			break;						\
32690792Sgshapiro									\
32790792Sgshapiro		  case SMFIR_DISCARD:					\
32890792Sgshapiro			if (MilterLogLevel > 3)				\
32990792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
33090792Sgshapiro					  "Milter: %s=%s, discard",	\
33190792Sgshapiro					  str, addr);			\
33290792Sgshapiro			e->e_flags |= EF_DISCARD;			\
33390792Sgshapiro			break;						\
33490792Sgshapiro									\
33590792Sgshapiro		  case SMFIR_TEMPFAIL:					\
33690792Sgshapiro			if (MilterLogLevel > 3)				\
33790792Sgshapiro			{						\
33890792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
33990792Sgshapiro					  "Milter: %s=%s, reject=%s",	\
34090792Sgshapiro					  str, addr, MSG_TEMPFAIL);	\
34190792Sgshapiro				LogUsrErrs = false;			\
34290792Sgshapiro			}						\
34390792Sgshapiro			usrerr(MSG_TEMPFAIL);				\
34490792Sgshapiro			break;						\
34590792Sgshapiro		}							\
34690792Sgshapiro		LogUsrErrs = savelogusrerrs;				\
34790792Sgshapiro		if (response != NULL)					\
34890792Sgshapiro			sm_free(response); /* XXX */			\
34990792Sgshapiro	}
35090792Sgshapiro
35190792Sgshapiro#else /* MILTER */
35290792Sgshapiro# define MILTER_ABORT(e)
35390792Sgshapiro#endif /* MILTER */
35490792Sgshapiro
35590792Sgshapiro/* clear all SMTP state (for HELO/EHLO/RSET) */
35690792Sgshapiro#define CLEAR_STATE(cmd)					\
357132943Sgshapirodo								\
35890792Sgshapiro{								\
35990792Sgshapiro	/* abort milter filters */				\
36090792Sgshapiro	MILTER_ABORT(e);					\
36190792Sgshapiro								\
36290792Sgshapiro	if (smtp.sm_nrcpts > 0)					\
36390792Sgshapiro	{							\
36490792Sgshapiro		logundelrcpts(e, cmd, 10, false);		\
36590792Sgshapiro		smtp.sm_nrcpts = 0;				\
36690792Sgshapiro		macdefine(&e->e_macro, A_PERM,			\
36790792Sgshapiro			  macid("{nrcpts}"), "0");		\
36890792Sgshapiro	}							\
36990792Sgshapiro								\
37090792Sgshapiro	e->e_sendqueue = NULL;					\
37190792Sgshapiro	e->e_flags |= EF_CLRQUEUE;				\
37290792Sgshapiro								\
37390792Sgshapiro	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))	\
37490792Sgshapiro		logsender(e, NULL);				\
37590792Sgshapiro	e->e_flags &= ~EF_LOGSENDER;				\
37690792Sgshapiro								\
37790792Sgshapiro	/* clean up a bit */					\
37890792Sgshapiro	smtp.sm_gotmail = false;				\
37990792Sgshapiro	SuprErrs = true;					\
38090792Sgshapiro	dropenvelope(e, true, false);				\
38190792Sgshapiro	sm_rpool_free(e->e_rpool);				\
38290792Sgshapiro	e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL));	\
38390792Sgshapiro	CurEnv = e;						\
384132943Sgshapiro								\
385132943Sgshapiro	/* put back discard bit */				\
386132943Sgshapiro	if (smtp.sm_discard)					\
387132943Sgshapiro		e->e_flags |= EF_DISCARD;			\
388132943Sgshapiro								\
389132943Sgshapiro	/* restore connection quarantining */			\
390132943Sgshapiro	if (smtp.sm_quarmsg == NULL)				\
391132943Sgshapiro	{							\
392132943Sgshapiro		e->e_quarmsg = NULL;				\
393132943Sgshapiro		macdefine(&e->e_macro, A_PERM,			\
394132943Sgshapiro			macid("{quarantine}"), "");		\
395132943Sgshapiro	}							\
396132943Sgshapiro	else							\
397132943Sgshapiro	{							\
398132943Sgshapiro		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,	\
399132943Sgshapiro						smtp.sm_quarmsg);	\
400132943Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"),	\
401132943Sgshapiro			  e->e_quarmsg);			\
402132943Sgshapiro	}							\
403132943Sgshapiro} while (0)
40490792Sgshapiro
40590792Sgshapiro/* sleep to flatten out connection load */
40690792Sgshapiro#define MIN_DELAY_LOG	15	/* wait before logging this again */
40790792Sgshapiro
40890792Sgshapiro/* is it worth setting the process title for 1s? */
40990792Sgshapiro#define DELAY_CONN(cmd)						\
41090792Sgshapiro	if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA)	\
41190792Sgshapiro	{							\
41290792Sgshapiro		time_t dnow;					\
41390792Sgshapiro								\
41490792Sgshapiro		sm_setproctitle(true, e,			\
41590792Sgshapiro				"%s: %s: delaying %s: load average: %d", \
41690792Sgshapiro				qid_printname(e), CurSmtpClient,	\
41790792Sgshapiro				cmd, DelayLA);	\
41890792Sgshapiro		if (LogLevel > 8 && (dnow = curtime()) > log_delay)	\
41990792Sgshapiro		{						\
42090792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,		\
42190792Sgshapiro				  "delaying=%s, load average=%d >= %d",	\
42290792Sgshapiro				  cmd, CurrentLA, DelayLA);		\
42390792Sgshapiro			log_delay = dnow + MIN_DELAY_LOG;	\
42490792Sgshapiro		}						\
42590792Sgshapiro		(void) sleep(1);				\
42690792Sgshapiro		sm_setproctitle(true, e, "%s %s: %.80s",	\
42790792Sgshapiro				qid_printname(e), CurSmtpClient, inp);	\
42890792Sgshapiro	}
42990792Sgshapiro
43090792Sgshapiro
43138032Spetervoid
43264562Sgshapirosmtp(nullserver, d_flags, e)
43364562Sgshapiro	char *volatile nullserver;
43464562Sgshapiro	BITMAP256 d_flags;
43538032Speter	register ENVELOPE *volatile e;
43638032Speter{
43738032Speter	register char *volatile p;
43864562Sgshapiro	register struct cmd *volatile c = NULL;
43938032Speter	char *cmd;
44038032Speter	auto ADDRESS *vrfyqueue;
44138032Speter	ADDRESS *a;
44238032Speter	volatile bool gothello;		/* helo command received */
44338032Speter	bool vrfy;			/* set if this is a vrfy command */
44438032Speter	char *volatile protocol;	/* sending protocol */
44538032Speter	char *volatile sendinghost;	/* sending hostname */
44638032Speter	char *volatile peerhostname;	/* name of SMTP peer or "localhost" */
44738032Speter	auto char *delimptr;
44838032Speter	char *id;
44990792Sgshapiro	volatile unsigned int n_badcmds = 0;	/* count of bad commands */
45090792Sgshapiro	volatile unsigned int n_badrcpts = 0;	/* number of rejected RCPT */
45190792Sgshapiro	volatile unsigned int n_verifies = 0;	/* count of VRFY/EXPN */
45290792Sgshapiro	volatile unsigned int n_etrn = 0;	/* count of ETRN */
45390792Sgshapiro	volatile unsigned int n_noop = 0;	/* count of NOOP/VERB/etc */
45490792Sgshapiro	volatile unsigned int n_helo = 0;	/* count of HELO/EHLO */
455120256Sgshapiro	volatile int save_sevenbitinput;
45638032Speter	bool ok;
457132943Sgshapiro#if _FFR_BLOCK_PROXIES
45890792Sgshapiro	volatile bool first;
459132943Sgshapiro#endif /* _FFR_BLOCK_PROXIES */
46090792Sgshapiro	volatile bool tempfail = false;
46164562Sgshapiro	volatile time_t wt;		/* timeout after too many commands */
46264562Sgshapiro	volatile time_t previous;	/* time after checksmtpattack() */
46390792Sgshapiro	volatile bool lognullconnection = true;
46438032Speter	register char *q;
46590792Sgshapiro	SMTP_T smtp;
46664562Sgshapiro	char *addr;
46764562Sgshapiro	char *greetcode = "220";
46890792Sgshapiro	char *hostname;			/* my hostname ($j) */
46938032Speter	QUEUE_CHAR *new;
47064562Sgshapiro	int argno;
47164562Sgshapiro	char *args[MAXSMTPARGS];
47238032Speter	char inp[MAXLINE];
47338032Speter	char cmdbuf[MAXLINE];
47490792Sgshapiro#if SASL
47564562Sgshapiro	sasl_conn_t *conn;
47664562Sgshapiro	volatile bool sasl_ok;
47790792Sgshapiro	volatile unsigned int n_auth = 0;	/* count of AUTH commands */
47864562Sgshapiro	bool ismore;
47964562Sgshapiro	int result;
48064562Sgshapiro	volatile int authenticating;
48164562Sgshapiro	char *user;
48298121Sgshapiro	char *in, *out2;
48398121Sgshapiro# if SASL >= 20000
484102528Sgshapiro	char *auth_id;
48598121Sgshapiro	const char *out;
486102528Sgshapiro	sasl_ssf_t ext_ssf;
487120256Sgshapiro	char localip[60], remoteip[60];
48898121Sgshapiro# else /* SASL >= 20000 */
48998121Sgshapiro	char *out;
49064562Sgshapiro	const char *errstr;
49198121Sgshapiro	sasl_external_properties_t ext_ssf;
492120256Sgshapiro	struct sockaddr_in saddr_l;
493120256Sgshapiro	struct sockaddr_in saddr_r;
49498121Sgshapiro# endif /* SASL >= 20000 */
49598121Sgshapiro	sasl_security_properties_t ssp;
49698121Sgshapiro	sasl_ssf_t *ssf;
49790792Sgshapiro	unsigned int inlen, out2len;
49864562Sgshapiro	unsigned int outlen;
49964562Sgshapiro	char *volatile auth_type;
50064562Sgshapiro	char *mechlist;
50190792Sgshapiro	volatile unsigned int n_mechs;
50290792Sgshapiro	unsigned int len;
50390792Sgshapiro#endif /* SASL */
504132943Sgshapiro	int r;
50590792Sgshapiro#if STARTTLS
506132943Sgshapiro	int fdfl;
50766494Sgshapiro	int rfd, wfd;
50890792Sgshapiro	volatile bool tls_active = false;
509132943Sgshapiro	volatile bool smtps = bitnset(D_SMTPS, d_flags);
51064562Sgshapiro	bool saveQuickAbort;
51164562Sgshapiro	bool saveSuprErrs;
51290792Sgshapiro	time_t tlsstart;
51390792Sgshapiro#endif /* STARTTLS */
51490792Sgshapiro	volatile unsigned int features;
51590792Sgshapiro#if PIPELINING
51690792Sgshapiro# if _FFR_NO_PIPE
51790792Sgshapiro	int np_log = 0;
51890792Sgshapiro# endif /* _FFR_NO_PIPE */
51990792Sgshapiro#endif /* PIPELINING */
52090792Sgshapiro	volatile time_t log_delay = (time_t) 0;
52138032Speter
522120256Sgshapiro	save_sevenbitinput = SevenBitInput;
52390792Sgshapiro	smtp.sm_nrcpts = 0;
52490792Sgshapiro#if MILTER
52590792Sgshapiro	smtp.sm_milterize = (nullserver == NULL);
52690792Sgshapiro	smtp.sm_milterlist = false;
52790792Sgshapiro#endif /* MILTER */
52890792Sgshapiro
52990792Sgshapiro	/* setup I/O fd correctly for the SMTP server */
53090792Sgshapiro	setup_smtpd_io();
53190792Sgshapiro
53290792Sgshapiro#if SM_HEAP_CHECK
53390792Sgshapiro	if (sm_debug_active(&DebugLeakSmtp, 1))
53438032Speter	{
53590792Sgshapiro		sm_heap_newgroup();
53690792Sgshapiro		sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
53738032Speter	}
53890792Sgshapiro#endif /* SM_HEAP_CHECK */
53964562Sgshapiro
54090792Sgshapiro	/* XXX the rpool should be set when e is initialized in main() */
54190792Sgshapiro	e->e_rpool = sm_rpool_new_x(NULL);
54290792Sgshapiro	e->e_macro.mac_rpool = e->e_rpool;
54390792Sgshapiro
54438032Speter	settime(e);
54590792Sgshapiro	sm_getla();
54638032Speter	peerhostname = RealHostName;
54738032Speter	if (peerhostname == NULL)
54838032Speter		peerhostname = "localhost";
54938032Speter	CurHostName = peerhostname;
55038032Speter	CurSmtpClient = macvalue('_', e);
55138032Speter	if (CurSmtpClient == NULL)
55238032Speter		CurSmtpClient = CurHostName;
55338032Speter
55438032Speter	/* check_relay may have set discard bit, save for later */
55590792Sgshapiro	smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
55638032Speter
55790792Sgshapiro#if PIPELINING
55890792Sgshapiro	/* auto-flush output when reading input */
55990792Sgshapiro	(void) sm_io_autoflush(InChannel, OutChannel);
56090792Sgshapiro#endif /* PIPELINING */
56164562Sgshapiro
56290792Sgshapiro	sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
56390792Sgshapiro
56490792Sgshapiro	/* Set default features for server. */
56590792Sgshapiro	features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
56690792Sgshapiro		     bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
56790792Sgshapiro		| (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
56890792Sgshapiro		| (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
56990792Sgshapiro			: (SRV_OFFER_EXPN
57090792Sgshapiro			  | (bitset(PRIV_NOVERB, PrivacyFlags)
57190792Sgshapiro			     ? SRV_NONE : SRV_OFFER_VERB)))
57290792Sgshapiro		| (bitset(PRIV_NORECEIPTS, PrivacyFlags) ? SRV_NONE
57390792Sgshapiro							 : SRV_OFFER_DSN)
57490792Sgshapiro#if SASL
57590792Sgshapiro		| (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
576132943Sgshapiro		| (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC
577132943Sgshapiro							  : SRV_NONE)
57890792Sgshapiro#endif /* SASL */
57990792Sgshapiro#if PIPELINING
58090792Sgshapiro		| SRV_OFFER_PIPE
58190792Sgshapiro#endif /* PIPELINING */
58290792Sgshapiro#if STARTTLS
58390792Sgshapiro		| (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
58490792Sgshapiro		| (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
58590792Sgshapiro						       : SRV_VRFY_CLT)
58690792Sgshapiro#endif /* STARTTLS */
58790792Sgshapiro		;
58890792Sgshapiro	if (nullserver == NULL)
58990792Sgshapiro	{
59090792Sgshapiro		features = srvfeatures(e, CurSmtpClient, features);
59190792Sgshapiro		if (bitset(SRV_TMP_FAIL, features))
59290792Sgshapiro		{
59390792Sgshapiro			if (LogLevel > 4)
59490792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
59590792Sgshapiro					  "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
59690792Sgshapiro					  CurSmtpClient);
59790792Sgshapiro			nullserver = "450 4.3.0 Please try again later.";
59890792Sgshapiro		}
599132943Sgshapiro		else
600132943Sgshapiro		{
60190792Sgshapiro#if PIPELINING
60290792Sgshapiro# if _FFR_NO_PIPE
603132943Sgshapiro			if (bitset(SRV_NO_PIPE, features))
604132943Sgshapiro			{
605132943Sgshapiro				/* for consistency */
606132943Sgshapiro				features &= ~SRV_OFFER_PIPE;
607132943Sgshapiro			}
60890792Sgshapiro# endif /* _FFR_NO_PIPE */
60990792Sgshapiro#endif /* PIPELINING */
610132943Sgshapiro#if SASL
611132943Sgshapiro			if (bitset(SRV_REQ_SEC, features))
612132943Sgshapiro				SASLOpts |= SASL_SEC_NOPLAINTEXT;
613132943Sgshapiro			else
614132943Sgshapiro				SASLOpts &= ~SASL_SEC_NOPLAINTEXT;
615132943Sgshapiro#endif /* SASL */
616132943Sgshapiro		}
61790792Sgshapiro	}
618132943Sgshapiro	else if (strncmp(nullserver, "421 ", 4) == 0)
619132943Sgshapiro	{
620132943Sgshapiro		message(nullserver);
621132943Sgshapiro		goto doquit;
622132943Sgshapiro	}
62390792Sgshapiro
62490792Sgshapiro	hostname = macvalue('j', e);
62590792Sgshapiro#if SASL
626132943Sgshapiro	if (AuthRealm == NULL)
627132943Sgshapiro		AuthRealm = hostname;
62890792Sgshapiro	sasl_ok = bitset(SRV_OFFER_AUTH, features);
62964562Sgshapiro	n_mechs = 0;
63090792Sgshapiro	authenticating = SASL_NOT_AUTH;
63164562Sgshapiro
63264562Sgshapiro	/* SASL server new connection */
63390792Sgshapiro	if (sasl_ok)
63438032Speter	{
63598121Sgshapiro# if SASL >= 20000
636132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL,
63798121Sgshapiro					 NULL, 0, &conn);
63898121Sgshapiro# elif SASL > 10505
63990792Sgshapiro		/* use empty realm: only works in SASL > 1.5.5 */
640132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn);
64198121Sgshapiro# else /* SASL >= 20000 */
64290792Sgshapiro		/* use no realm -> realm is set to hostname by SASL lib */
643132943Sgshapiro		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0,
64490792Sgshapiro					 &conn);
64598121Sgshapiro# endif /* SASL >= 20000 */
64690792Sgshapiro		sasl_ok = result == SASL_OK;
64790792Sgshapiro		if (!sasl_ok)
64890792Sgshapiro		{
64990792Sgshapiro			if (LogLevel > 9)
65090792Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
65190792Sgshapiro					  "AUTH error: sasl_server_new failed=%d",
65290792Sgshapiro					  result);
65390792Sgshapiro		}
65490792Sgshapiro	}
65590792Sgshapiro	if (sasl_ok)
65690792Sgshapiro	{
65764562Sgshapiro		/*
65864562Sgshapiro		**  SASL set properties for sasl
65964562Sgshapiro		**  set local/remote IP
66098121Sgshapiro		**  XXX Cyrus SASL v1 only supports IPv4
66164562Sgshapiro		**
66264562Sgshapiro		**  XXX where exactly are these used/required?
66364562Sgshapiro		**  Kerberos_v4
66464562Sgshapiro		*/
66564562Sgshapiro
66698121Sgshapiro# if SASL >= 20000
66798121Sgshapiro#  if NETINET || NETINET6
66890792Sgshapiro		in = macvalue(macid("{daemon_family}"), e);
66998121Sgshapiro		if (in != NULL && (
67098121Sgshapiro#   if NETINET6
67198121Sgshapiro		    strcmp(in, "inet6") == 0 ||
67298121Sgshapiro#   endif /* NETINET6 */
67398121Sgshapiro		    strcmp(in, "inet") == 0))
67498121Sgshapiro		{
67598121Sgshapiro			SOCKADDR_LEN_T addrsize;
67698121Sgshapiro			SOCKADDR saddr_l;
67798121Sgshapiro			SOCKADDR saddr_r;
67898121Sgshapiro
67998121Sgshapiro			addrsize = sizeof(saddr_r);
68098121Sgshapiro			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
68198121Sgshapiro						      NULL),
68298121Sgshapiro					(struct sockaddr *) &saddr_r,
68398121Sgshapiro					&addrsize) == 0)
68498121Sgshapiro			{
68598121Sgshapiro				if (iptostring(&saddr_r, addrsize,
68698121Sgshapiro					       remoteip, sizeof remoteip))
68798121Sgshapiro				{
68898121Sgshapiro					sasl_setprop(conn, SASL_IPREMOTEPORT,
68998121Sgshapiro						     remoteip);
69098121Sgshapiro				}
69198121Sgshapiro				addrsize = sizeof(saddr_l);
69298121Sgshapiro				if (getsockname(sm_io_getinfo(InChannel,
69398121Sgshapiro							      SM_IO_WHAT_FD,
69498121Sgshapiro							      NULL),
69598121Sgshapiro						(struct sockaddr *) &saddr_l,
69698121Sgshapiro						&addrsize) == 0)
69798121Sgshapiro				{
69898121Sgshapiro					if (iptostring(&saddr_l, addrsize,
69998121Sgshapiro						       localip,
70098121Sgshapiro						       sizeof localip))
70198121Sgshapiro					{
70298121Sgshapiro						sasl_setprop(conn,
70398121Sgshapiro							     SASL_IPLOCALPORT,
70498121Sgshapiro							     localip);
70598121Sgshapiro					}
70698121Sgshapiro				}
70798121Sgshapiro			}
70898121Sgshapiro		}
70998121Sgshapiro#  endif /* NETINET || NETINET6 */
71098121Sgshapiro# else /* SASL >= 20000 */
71198121Sgshapiro#  if NETINET
71298121Sgshapiro		in = macvalue(macid("{daemon_family}"), e);
71364562Sgshapiro		if (in != NULL && strcmp(in, "inet") == 0)
71464562Sgshapiro		{
71564562Sgshapiro			SOCKADDR_LEN_T addrsize;
71664562Sgshapiro
71764562Sgshapiro			addrsize = sizeof(struct sockaddr_in);
71890792Sgshapiro			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
71990792Sgshapiro						      NULL),
72064562Sgshapiro					(struct sockaddr *)&saddr_r,
72164562Sgshapiro					&addrsize) == 0)
72264562Sgshapiro			{
72364562Sgshapiro				sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
72464562Sgshapiro				addrsize = sizeof(struct sockaddr_in);
72590792Sgshapiro				if (getsockname(sm_io_getinfo(InChannel,
72690792Sgshapiro							      SM_IO_WHAT_FD,
72790792Sgshapiro							      NULL),
72864562Sgshapiro						(struct sockaddr *)&saddr_l,
72964562Sgshapiro						&addrsize) == 0)
73064562Sgshapiro					sasl_setprop(conn, SASL_IP_LOCAL,
73164562Sgshapiro						     &saddr_l);
73264562Sgshapiro			}
73364562Sgshapiro		}
73498121Sgshapiro#  endif /* NETINET */
73598121Sgshapiro# endif /* SASL >= 20000 */
73664562Sgshapiro
73764562Sgshapiro		auth_type = NULL;
73864562Sgshapiro		mechlist = NULL;
73964562Sgshapiro		user = NULL;
74090792Sgshapiro# if 0
74190792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
74290792Sgshapiro			macid("{auth_author}"), NULL);
74390792Sgshapiro# endif /* 0 */
74464562Sgshapiro
74564562Sgshapiro		/* set properties */
74664562Sgshapiro		(void) memset(&ssp, '\0', sizeof ssp);
74790792Sgshapiro
74864562Sgshapiro		/* XXX should these be options settable via .cf ? */
74964562Sgshapiro		/* ssp.min_ssf = 0; is default due to memset() */
75094334Sgshapiro# if STARTTLS
75194334Sgshapiro# endif /* STARTTLS */
75264562Sgshapiro		{
75390792Sgshapiro			ssp.max_ssf = MaxSLBits;
75464562Sgshapiro			ssp.maxbufsize = MAXOUTLEN;
75564562Sgshapiro		}
75664562Sgshapiro		ssp.security_flags = SASLOpts & SASL_SEC_MASK;
75764562Sgshapiro		sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
75864562Sgshapiro
75964562Sgshapiro		if (sasl_ok)
76064562Sgshapiro		{
76164562Sgshapiro			/*
76264562Sgshapiro			**  external security strength factor;
76390792Sgshapiro			**	currently we have none so zero
76464562Sgshapiro			*/
76590792Sgshapiro
76698121Sgshapiro# if SASL >= 20000
76798121Sgshapiro			ext_ssf = 0;
76898121Sgshapiro			auth_id = NULL;
76998121Sgshapiro			sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL,
77098121Sgshapiro						 &ext_ssf) == SASL_OK) &&
77198121Sgshapiro				   (sasl_setprop(conn, SASL_AUTH_EXTERNAL,
772102528Sgshapiro						 auth_id) == SASL_OK));
77398121Sgshapiro# else /* SASL >= 20000 */
77464562Sgshapiro			ext_ssf.ssf = 0;
77564562Sgshapiro			ext_ssf.auth_id = NULL;
77664562Sgshapiro			sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
77764562Sgshapiro					       &ext_ssf) == SASL_OK;
77898121Sgshapiro# endif /* SASL >= 20000 */
77964562Sgshapiro		}
78064562Sgshapiro		if (sasl_ok)
78164562Sgshapiro			n_mechs = saslmechs(conn, &mechlist);
78238032Speter	}
78390792Sgshapiro#endif /* SASL */
78438032Speter
785120256Sgshapiro#if STARTTLS
786120256Sgshapiro#endif /* STARTTLS */
787120256Sgshapiro
78890792Sgshapiro#if MILTER
78990792Sgshapiro	if (smtp.sm_milterize)
79064562Sgshapiro	{
79164562Sgshapiro		char state;
79264562Sgshapiro
79364562Sgshapiro		/* initialize mail filter connection */
79490792Sgshapiro		smtp.sm_milterlist = milter_init(e, &state);
79564562Sgshapiro		switch (state)
79664562Sgshapiro		{
79764562Sgshapiro		  case SMFIR_REJECT:
79890792Sgshapiro			if (MilterLogLevel > 3)
79990792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
80094334Sgshapiro					  "Milter: initialization failed, rejecting commands");
80164562Sgshapiro			greetcode = "554";
80264562Sgshapiro			nullserver = "Command rejected";
80390792Sgshapiro			smtp.sm_milterize = false;
80464562Sgshapiro			break;
80564562Sgshapiro
80664562Sgshapiro		  case SMFIR_TEMPFAIL:
80790792Sgshapiro			if (MilterLogLevel > 3)
80890792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
80994334Sgshapiro					  "Milter: initialization failed, temp failing commands");
81090792Sgshapiro			tempfail = true;
81190792Sgshapiro			smtp.sm_milterize = false;
81264562Sgshapiro			break;
81364562Sgshapiro		}
81464562Sgshapiro	}
81564562Sgshapiro
81690792Sgshapiro	if (smtp.sm_milterlist && smtp.sm_milterize &&
81790792Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
81864562Sgshapiro	{
81964562Sgshapiro		char state;
82090792Sgshapiro		char *response;
82164562Sgshapiro
82290792Sgshapiro		response = milter_connect(peerhostname, RealHostAddr,
82390792Sgshapiro					  e, &state);
82464562Sgshapiro		switch (state)
82564562Sgshapiro		{
82664562Sgshapiro		  case SMFIR_REPLYCODE:	/* REPLYCODE shouldn't happen */
82764562Sgshapiro		  case SMFIR_REJECT:
82890792Sgshapiro			if (MilterLogLevel > 3)
82990792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
83090792Sgshapiro					  "Milter: connect: host=%s, addr=%s, rejecting commands",
83190792Sgshapiro					  peerhostname,
83290792Sgshapiro					  anynet_ntoa(&RealHostAddr));
83364562Sgshapiro			greetcode = "554";
83464562Sgshapiro			nullserver = "Command rejected";
83590792Sgshapiro			smtp.sm_milterize = false;
83664562Sgshapiro			break;
83764562Sgshapiro
83864562Sgshapiro		  case SMFIR_TEMPFAIL:
83990792Sgshapiro			if (MilterLogLevel > 3)
84090792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
84190792Sgshapiro					  "Milter: connect: host=%s, addr=%s, temp failing commands",
84290792Sgshapiro					  peerhostname,
84390792Sgshapiro					  anynet_ntoa(&RealHostAddr));
84490792Sgshapiro			tempfail = true;
84590792Sgshapiro			smtp.sm_milterize = false;
84664562Sgshapiro			break;
847110560Sgshapiro
848110560Sgshapiro		  case SMFIR_SHUTDOWN:
849110560Sgshapiro			if (MilterLogLevel > 3)
850110560Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
851110560Sgshapiro					  "Milter: connect: host=%s, addr=%s, shutdown",
852110560Sgshapiro					  peerhostname,
853110560Sgshapiro					  anynet_ntoa(&RealHostAddr));
854110560Sgshapiro			tempfail = true;
855110560Sgshapiro			smtp.sm_milterize = false;
856110560Sgshapiro			message("421 4.7.0 %s closing connection",
857110560Sgshapiro					MyHostName);
858110560Sgshapiro
859110560Sgshapiro			/* arrange to ignore send list */
860110560Sgshapiro			e->e_sendqueue = NULL;
861110560Sgshapiro			goto doquit;
86264562Sgshapiro		}
86390792Sgshapiro		if (response != NULL)
86490792Sgshapiro			sm_free(response); /* XXX */
86564562Sgshapiro	}
86690792Sgshapiro#endif /* MILTER */
86764562Sgshapiro
868132943Sgshapiro	/*
869132943Sgshapiro	**  Broken proxies and SMTP slammers
870132943Sgshapiro	**  push data without waiting, catch them
871132943Sgshapiro	*/
872132943Sgshapiro
873132943Sgshapiro	if (
87490792Sgshapiro#if STARTTLS
875132943Sgshapiro	    !smtps &&
876132943Sgshapiro#endif /* STARTTLS */
877132943Sgshapiro	    *greetcode == '2')
878132943Sgshapiro	{
879132943Sgshapiro		time_t msecs = 0;
880132943Sgshapiro		char **pvp;
881132943Sgshapiro		char pvpbuf[PSBUFSIZE];
882132943Sgshapiro
883132943Sgshapiro		/* Ask the rulesets how long to pause */
884132943Sgshapiro		pvp = NULL;
885132943Sgshapiro		r = rscap("greet_pause", peerhostname,
886132943Sgshapiro			  anynet_ntoa(&RealHostAddr), e,
887132943Sgshapiro			  &pvp, pvpbuf, sizeof(pvpbuf));
888132943Sgshapiro		if (r == EX_OK && pvp != NULL && pvp[0] != NULL &&
889132943Sgshapiro		    (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
890132943Sgshapiro		{
891132943Sgshapiro			msecs = strtol(pvp[1], NULL, 10);
892132943Sgshapiro		}
893132943Sgshapiro
894132943Sgshapiro		if (msecs > 0)
895132943Sgshapiro		{
896132943Sgshapiro			int fd;
897132943Sgshapiro			fd_set readfds;
898132943Sgshapiro			struct timeval timeout;
899132943Sgshapiro
900132943Sgshapiro			/* pause for a moment */
901132943Sgshapiro			timeout.tv_sec = msecs / 1000;
902132943Sgshapiro			timeout.tv_usec = (msecs % 1000) * 1000;
903132943Sgshapiro
904132943Sgshapiro			/* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
905132943Sgshapiro			if (timeout.tv_sec >= 300)
906132943Sgshapiro			{
907132943Sgshapiro				timeout.tv_sec = 300;
908132943Sgshapiro				timeout.tv_usec = 0;
909132943Sgshapiro			}
910132943Sgshapiro
911132943Sgshapiro			/* check if data is on the socket during the pause */
912132943Sgshapiro			fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
913132943Sgshapiro			FD_ZERO(&readfds);
914132943Sgshapiro			SM_FD_SET(fd, &readfds);
915132943Sgshapiro			if (select(fd + 1, FDSET_CAST &readfds,
916132943Sgshapiro			    NULL, NULL, &timeout) > 0 &&
917132943Sgshapiro			    FD_ISSET(fd, &readfds))
918132943Sgshapiro			{
919132943Sgshapiro				greetcode = "554";
920132943Sgshapiro				nullserver = "Command rejected";
921132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
922132943Sgshapiro					  "rejecting commands from %s [%s] due to pre-greeting traffic",
923132943Sgshapiro					  peerhostname,
924132943Sgshapiro					  anynet_ntoa(&RealHostAddr));
925132943Sgshapiro			}
926132943Sgshapiro		}
927132943Sgshapiro	}
928132943Sgshapiro
929132943Sgshapiro#if STARTTLS
93090792Sgshapiro	/* If this an smtps connection, start TLS now */
93190792Sgshapiro	if (smtps)
932120256Sgshapiro	{
933120256Sgshapiro		Errors = 0;
93490792Sgshapiro		goto starttls;
935120256Sgshapiro	}
93690792Sgshapiro
93790792Sgshapiro  greeting:
93890792Sgshapiro
93990792Sgshapiro#endif /* STARTTLS */
94090792Sgshapiro
94138032Speter	/* output the first line, inserting "ESMTP" as second word */
94290792Sgshapiro	if (*greetcode == '5')
94390792Sgshapiro		(void) sm_snprintf(inp, sizeof inp, "%s not accepting messages",
94490792Sgshapiro				   hostname);
94590792Sgshapiro	else
94690792Sgshapiro		expand(SmtpGreeting, inp, sizeof inp, e);
94790792Sgshapiro
94838032Speter	p = strchr(inp, '\n');
94938032Speter	if (p != NULL)
95038032Speter		*p++ = '\0';
95138032Speter	id = strchr(inp, ' ');
95238032Speter	if (id == NULL)
95338032Speter		id = &inp[strlen(inp)];
95464562Sgshapiro	if (p == NULL)
95590792Sgshapiro		(void) sm_snprintf(cmdbuf, sizeof cmdbuf,
95664562Sgshapiro			 "%s %%.*s ESMTP%%s", greetcode);
95764562Sgshapiro	else
95890792Sgshapiro		(void) sm_snprintf(cmdbuf, sizeof cmdbuf,
95964562Sgshapiro			 "%s-%%.*s ESMTP%%s", greetcode);
96066494Sgshapiro	message(cmdbuf, (int) (id - inp), inp, id);
96138032Speter
96238032Speter	/* output remaining lines */
96338032Speter	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
96438032Speter	{
96538032Speter		*p++ = '\0';
96638032Speter		if (isascii(*id) && isspace(*id))
96738032Speter			id++;
96890792Sgshapiro		(void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, "-%s");
96964562Sgshapiro		message(cmdbuf, id);
97038032Speter	}
97138032Speter	if (id != NULL)
97238032Speter	{
97338032Speter		if (isascii(*id) && isspace(*id))
97438032Speter			id++;
97590792Sgshapiro		(void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, " %s");
97664562Sgshapiro		message(cmdbuf, id);
97738032Speter	}
97838032Speter
97938032Speter	protocol = NULL;
98038032Speter	sendinghost = macvalue('s', e);
98190792Sgshapiro
98290792Sgshapiro	/* If quarantining by a connect/ehlo action, save between messages */
98390792Sgshapiro	if (e->e_quarmsg == NULL)
98490792Sgshapiro		smtp.sm_quarmsg = NULL;
98590792Sgshapiro	else
98690792Sgshapiro		smtp.sm_quarmsg = newstr(e->e_quarmsg);
98790792Sgshapiro
98890792Sgshapiro	/* sendinghost's storage must outlive the current envelope */
98990792Sgshapiro	if (sendinghost != NULL)
99090792Sgshapiro		sendinghost = sm_strdup_x(sendinghost);
991132943Sgshapiro#if _FFR_BLOCK_PROXIES
99290792Sgshapiro	first = true;
993132943Sgshapiro#endif /* _FFR_BLOCK_PROXIES */
99490792Sgshapiro	gothello = false;
99590792Sgshapiro	smtp.sm_gotmail = false;
99638032Speter	for (;;)
99738032Speter	{
99890792Sgshapiro	    SM_TRY
99990792Sgshapiro	    {
100090792Sgshapiro		QuickAbort = false;
100190792Sgshapiro		HoldErrs = false;
100290792Sgshapiro		SuprErrs = false;
100390792Sgshapiro		LogUsrErrs = false;
100490792Sgshapiro		OnlyOneError = true;
100538032Speter		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
100638032Speter
100738032Speter		/* setup for the read */
100838032Speter		e->e_to = NULL;
100938032Speter		Errors = 0;
101064562Sgshapiro		FileName = NULL;
101190792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
101238032Speter
101338032Speter		/* read the input line */
101438032Speter		SmtpPhase = "server cmd read";
101590792Sgshapiro		sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
101690792Sgshapiro#if SASL
101764562Sgshapiro		/*
101890792Sgshapiro		**  XXX SMTP AUTH requires accepting any length,
101990792Sgshapiro		**	at least for challenge/response
102064562Sgshapiro		*/
102190792Sgshapiro#endif /* SASL */
102238032Speter
102338032Speter		/* handle errors */
102490792Sgshapiro		if (sm_io_error(OutChannel) ||
102564562Sgshapiro		    (p = sfgets(inp, sizeof inp, InChannel,
102664562Sgshapiro				TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
102738032Speter		{
102864562Sgshapiro			char *d;
102964562Sgshapiro
103090792Sgshapiro			d = macvalue(macid("{daemon_name}"), e);
103164562Sgshapiro			if (d == NULL)
103264562Sgshapiro				d = "stdin";
103338032Speter			/* end of file, just die */
103438032Speter			disconnect(1, e);
103564562Sgshapiro
103690792Sgshapiro#if MILTER
103764562Sgshapiro			/* close out milter filters */
103864562Sgshapiro			milter_quit(e);
103990792Sgshapiro#endif /* MILTER */
104064562Sgshapiro
104164562Sgshapiro			message("421 4.4.1 %s Lost input channel from %s",
104238032Speter				MyHostName, CurSmtpClient);
104390792Sgshapiro			if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
104438032Speter				sm_syslog(LOG_NOTICE, e->e_id,
1045110560Sgshapiro					  "lost input channel from %s to %s after %s",
104664562Sgshapiro					  CurSmtpClient, d,
104764562Sgshapiro					  (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
104838032Speter			/*
104942575Speter			**  If have not accepted mail (DATA), do not bounce
105042575Speter			**  bad addresses back to sender.
105138032Speter			*/
105264562Sgshapiro
105338032Speter			if (bitset(EF_CLRQUEUE, e->e_flags))
105438032Speter				e->e_sendqueue = NULL;
105564562Sgshapiro			goto doquit;
105638032Speter		}
105738032Speter
1058132943Sgshapiro#if _FFR_BLOCK_PROXIES
105990792Sgshapiro		if (first)
106090792Sgshapiro		{
1061110560Sgshapiro			size_t inplen, cmdlen;
1062110560Sgshapiro			int idx;
1063110560Sgshapiro			char *http_cmd;
1064110560Sgshapiro			static char *http_cmds[] = { "GET", "POST",
1065110560Sgshapiro						     "CONNECT", "USER", NULL };
1066110560Sgshapiro
1067110560Sgshapiro			inplen = strlen(inp);
1068110560Sgshapiro			for (idx = 0; (http_cmd = http_cmds[idx]) != NULL;
1069110560Sgshapiro			     idx++)
1070110560Sgshapiro			{
1071110560Sgshapiro				cmdlen = strlen(http_cmd);
1072110560Sgshapiro				if (cmdlen < inplen &&
1073110560Sgshapiro				    sm_strncasecmp(inp, http_cmd, cmdlen) == 0 &&
1074110560Sgshapiro				    isascii(inp[cmdlen]) && isspace(inp[cmdlen]))
1075110560Sgshapiro				{
1076110560Sgshapiro					/* Open proxy, drop it */
1077110560Sgshapiro					message("421 4.7.0 %s Rejecting open proxy %s",
1078110560Sgshapiro						MyHostName, CurSmtpClient);
1079110560Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1080110560Sgshapiro						  "%s: probable open proxy: command=%.40s",
1081110560Sgshapiro						  CurSmtpClient, inp);
1082110560Sgshapiro					goto doquit;
1083110560Sgshapiro				}
1084110560Sgshapiro			}
108590792Sgshapiro			first = false;
108690792Sgshapiro		}
1087132943Sgshapiro#endif /* _FFR_BLOCK_PROXIES */
108890792Sgshapiro
108938032Speter		/* clean up end of line */
109090792Sgshapiro		fixcrlf(inp, true);
109138032Speter
109290792Sgshapiro#if PIPELINING
109390792Sgshapiro# if _FFR_NO_PIPE
109490792Sgshapiro		/*
109590792Sgshapiro		**  if there is more input and pipelining is disabled:
109690792Sgshapiro		**	delay ... (and maybe discard the input?)
109790792Sgshapiro		**  XXX this doesn't really work, at least in tests using
109890792Sgshapiro		**  telnet SM_IO_IS_READABLE only returns 1 if there were
109990792Sgshapiro		**  more than 2 input lines available.
110090792Sgshapiro		*/
110190792Sgshapiro
110290792Sgshapiro		if (bitset(SRV_NO_PIPE, features) &&
1103110560Sgshapiro		    sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
110490792Sgshapiro		{
110590792Sgshapiro			if (++np_log < 3)
110690792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
110790792Sgshapiro					  "unauthorized PIPELINING, sleeping");
110890792Sgshapiro			sleep(1);
110990792Sgshapiro		}
111090792Sgshapiro
111190792Sgshapiro# endif /* _FFR_NO_PIPE */
111290792Sgshapiro#endif /* PIPELINING */
111390792Sgshapiro
111490792Sgshapiro#if SASL
111564562Sgshapiro		if (authenticating == SASL_PROC_AUTH)
111664562Sgshapiro		{
111790792Sgshapiro# if 0
111864562Sgshapiro			if (*inp == '\0')
111964562Sgshapiro			{
112064562Sgshapiro				authenticating = SASL_NOT_AUTH;
112164562Sgshapiro				message("501 5.5.2 missing input");
1122120256Sgshapiro				RESET_SASLCONN;
112364562Sgshapiro				continue;
112464562Sgshapiro			}
112590792Sgshapiro# endif /* 0 */
112664562Sgshapiro			if (*inp == '*' && *(inp + 1) == '\0')
112764562Sgshapiro			{
112864562Sgshapiro				authenticating = SASL_NOT_AUTH;
112964562Sgshapiro
113064562Sgshapiro				/* rfc 2254 4. */
113164562Sgshapiro				message("501 5.0.0 AUTH aborted");
1132120256Sgshapiro				RESET_SASLCONN;
113364562Sgshapiro				continue;
113464562Sgshapiro			}
113564562Sgshapiro
113664562Sgshapiro			/* could this be shorter? XXX */
113798121Sgshapiro# if SASL >= 20000
113898121Sgshapiro			in = xalloc(strlen(inp) + 1);
113998121Sgshapiro			result = sasl_decode64(inp, strlen(inp), in,
114098121Sgshapiro					       strlen(inp), &inlen);
114198121Sgshapiro# else /* SASL >= 20000 */
114264562Sgshapiro			out = xalloc(strlen(inp));
114364562Sgshapiro			result = sasl_decode64(inp, strlen(inp), out, &outlen);
114498121Sgshapiro# endif /* SASL >= 20000 */
114564562Sgshapiro			if (result != SASL_OK)
114664562Sgshapiro			{
114764562Sgshapiro				authenticating = SASL_NOT_AUTH;
114864562Sgshapiro
114964562Sgshapiro				/* rfc 2254 4. */
115064562Sgshapiro				message("501 5.5.4 cannot decode AUTH parameter %s",
115164562Sgshapiro					inp);
115298121Sgshapiro# if SASL >= 20000
115398121Sgshapiro				sm_free(in);
115498121Sgshapiro# endif /* SASL >= 20000 */
1155120256Sgshapiro				RESET_SASLCONN;
115664562Sgshapiro				continue;
115764562Sgshapiro			}
115864562Sgshapiro
115998121Sgshapiro# if SASL >= 20000
116098121Sgshapiro			result = sasl_server_step(conn,	in, inlen,
116198121Sgshapiro						  &out, &outlen);
116298121Sgshapiro			sm_free(in);
116398121Sgshapiro# else /* SASL >= 20000 */
116464562Sgshapiro			result = sasl_server_step(conn,	out, outlen,
116564562Sgshapiro						  &out, &outlen, &errstr);
116698121Sgshapiro# endif /* SASL >= 20000 */
116764562Sgshapiro
116864562Sgshapiro			/* get an OK if we're done */
116964562Sgshapiro			if (result == SASL_OK)
117064562Sgshapiro			{
117164562Sgshapiro  authenticated:
117264562Sgshapiro				message("235 2.0.0 OK Authenticated");
117364562Sgshapiro				authenticating = SASL_IS_AUTH;
117490792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
117590792Sgshapiro					macid("{auth_type}"), auth_type);
117664562Sgshapiro
117798121Sgshapiro# if SASL >= 20000
117898121Sgshapiro				user = macvalue(macid("{auth_authen}"), e);
117998121Sgshapiro
118098121Sgshapiro				/* get security strength (features) */
118198121Sgshapiro				result = sasl_getprop(conn, SASL_SSF,
118298121Sgshapiro						      (const void **) &ssf);
118398121Sgshapiro# else /* SASL >= 20000 */
118464562Sgshapiro				result = sasl_getprop(conn, SASL_USERNAME,
118564562Sgshapiro						      (void **)&user);
118664562Sgshapiro				if (result != SASL_OK)
118764562Sgshapiro				{
118864562Sgshapiro					user = "";
118990792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
119090792Sgshapiro						  A_PERM,
119190792Sgshapiro						  macid("{auth_authen}"), NULL);
119264562Sgshapiro				}
119364562Sgshapiro				else
119464562Sgshapiro				{
119590792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
119690792Sgshapiro						  A_TEMP,
1197132943Sgshapiro						  macid("{auth_authen}"),
1198132943Sgshapiro						  xtextify(user, "<>\")"));
119964562Sgshapiro				}
120064562Sgshapiro
120190792Sgshapiro# if 0
120264562Sgshapiro				/* get realm? */
120364562Sgshapiro				sasl_getprop(conn, SASL_REALM, (void **) &data);
120490792Sgshapiro# endif /* 0 */
120564562Sgshapiro
120664562Sgshapiro				/* get security strength (features) */
120764562Sgshapiro				result = sasl_getprop(conn, SASL_SSF,
120864562Sgshapiro						      (void **) &ssf);
120998121Sgshapiro# endif /* SASL >= 20000 */
121064562Sgshapiro				if (result != SASL_OK)
121164562Sgshapiro				{
121290792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
121390792Sgshapiro						  A_PERM,
121490792Sgshapiro						  macid("{auth_ssf}"), "0");
121564562Sgshapiro					ssf = NULL;
121664562Sgshapiro				}
121764562Sgshapiro				else
121864562Sgshapiro				{
121964562Sgshapiro					char pbuf[8];
122064562Sgshapiro
122190792Sgshapiro					(void) sm_snprintf(pbuf, sizeof pbuf,
122290792Sgshapiro							   "%u", *ssf);
122390792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
122490792Sgshapiro						  A_TEMP,
122590792Sgshapiro						  macid("{auth_ssf}"), pbuf);
122664562Sgshapiro					if (tTd(95, 8))
122790792Sgshapiro						sm_dprintf("AUTH auth_ssf: %u\n",
122890792Sgshapiro							   *ssf);
122964562Sgshapiro				}
123090792Sgshapiro
123164562Sgshapiro				/*
123290792Sgshapiro				**  Only switch to encrypted connection
123364562Sgshapiro				**  if a security layer has been negotiated
123464562Sgshapiro				*/
123590792Sgshapiro
123664562Sgshapiro				if (ssf != NULL && *ssf > 0)
123764562Sgshapiro				{
123864562Sgshapiro					/*
123990792Sgshapiro					**  Convert I/O layer to use SASL.
124090792Sgshapiro					**  If the call fails, the connection
124190792Sgshapiro					**  is aborted.
124264562Sgshapiro					*/
124390792Sgshapiro
124490792Sgshapiro					if (sfdcsasl(&InChannel, &OutChannel,
124590792Sgshapiro						     conn) == 0)
124664562Sgshapiro					{
124764562Sgshapiro						/* restart dialogue */
124864562Sgshapiro						n_helo = 0;
124994334Sgshapiro# if PIPELINING
125090792Sgshapiro						(void) sm_io_autoflush(InChannel,
125190792Sgshapiro								       OutChannel);
125294334Sgshapiro# endif /* PIPELINING */
125364562Sgshapiro					}
125464562Sgshapiro					else
125564562Sgshapiro						syserr("503 5.3.3 SASL TLS failed");
125664562Sgshapiro				}
125790792Sgshapiro
125890792Sgshapiro				/* NULL pointer ok since it's our function */
125990792Sgshapiro				if (LogLevel > 8)
126064562Sgshapiro					sm_syslog(LOG_INFO, NOQID,
1261110560Sgshapiro						  "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d",
126290792Sgshapiro						  CurSmtpClient,
126390792Sgshapiro						  shortenstring(user, 128),
126490792Sgshapiro						  auth_type, *ssf);
126564562Sgshapiro			}
126664562Sgshapiro			else if (result == SASL_CONTINUE)
126764562Sgshapiro			{
126864562Sgshapiro				len = ENC64LEN(outlen);
126964562Sgshapiro				out2 = xalloc(len);
127064562Sgshapiro				result = sasl_encode64(out, outlen, out2, len,
127190792Sgshapiro						       &out2len);
127264562Sgshapiro				if (result != SASL_OK)
127364562Sgshapiro				{
127464562Sgshapiro					/* correct code? XXX */
127564562Sgshapiro					/* 454 Temp. authentication failure */
127664562Sgshapiro					message("454 4.5.4 Internal error: unable to encode64");
127764562Sgshapiro					if (LogLevel > 5)
127864562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
127990792Sgshapiro							  "AUTH encode64 error [%d for \"%s\"]",
128064562Sgshapiro							  result, out);
128164562Sgshapiro					/* start over? */
128264562Sgshapiro					authenticating = SASL_NOT_AUTH;
128364562Sgshapiro				}
128464562Sgshapiro				else
128564562Sgshapiro				{
128664562Sgshapiro					message("334 %s", out2);
128764562Sgshapiro					if (tTd(95, 2))
128890792Sgshapiro						sm_dprintf("AUTH continue: msg='%s' len=%u\n",
128990792Sgshapiro							   out2, out2len);
129064562Sgshapiro				}
129198121Sgshapiro# if SASL >= 20000
129298121Sgshapiro				sm_free(out2);
129398121Sgshapiro# endif /* SASL >= 20000 */
129464562Sgshapiro			}
129564562Sgshapiro			else
129664562Sgshapiro			{
129764562Sgshapiro				/* not SASL_OK or SASL_CONT */
129898121Sgshapiro				message("535 5.7.0 authentication failed");
129964562Sgshapiro				if (LogLevel > 9)
130064562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
130190792Sgshapiro						  "AUTH failure (%s): %s (%d) %s",
130264562Sgshapiro						  auth_type,
130364562Sgshapiro						  sasl_errstring(result, NULL,
130464562Sgshapiro								 NULL),
130590792Sgshapiro						  result,
130698121Sgshapiro# if SASL >= 20000
130798121Sgshapiro						  sasl_errdetail(conn));
130898121Sgshapiro# else /* SASL >= 20000 */
130990792Sgshapiro						  errstr == NULL ? "" : errstr);
131098121Sgshapiro# endif /* SASL >= 20000 */
1311120256Sgshapiro				RESET_SASLCONN;
131264562Sgshapiro				authenticating = SASL_NOT_AUTH;
131364562Sgshapiro			}
131464562Sgshapiro		}
131564562Sgshapiro		else
131664562Sgshapiro		{
131764562Sgshapiro			/* don't want to do any of this if authenticating */
131890792Sgshapiro#endif /* SASL */
131964562Sgshapiro
132038032Speter		/* echo command to transcript */
132138032Speter		if (e->e_xfp != NULL)
132290792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
132390792Sgshapiro					     "<<< %s\n", inp);
132438032Speter
132590792Sgshapiro		if (LogLevel > 14)
132690792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
132738032Speter
132838032Speter		/* break off command */
132938032Speter		for (p = inp; isascii(*p) && isspace(*p); p++)
133038032Speter			continue;
133138032Speter		cmd = cmdbuf;
133238032Speter		while (*p != '\0' &&
133338032Speter		       !(isascii(*p) && isspace(*p)) &&
133438032Speter		       cmd < &cmdbuf[sizeof cmdbuf - 2])
133538032Speter			*cmd++ = *p++;
133638032Speter		*cmd = '\0';
133738032Speter
133838032Speter		/* throw away leading whitespace */
133990792Sgshapiro		SKIP_SPACE(p);
134038032Speter
134138032Speter		/* decode command */
134264562Sgshapiro		for (c = CmdTab; c->cmd_name != NULL; c++)
134338032Speter		{
134490792Sgshapiro			if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
134538032Speter				break;
134638032Speter		}
134738032Speter
134838032Speter		/* reset errors */
134938032Speter		errno = 0;
135038032Speter
135190792Sgshapiro		/* check whether a "non-null" command has been used */
135290792Sgshapiro		switch (c->cmd_code)
135390792Sgshapiro		{
135490792Sgshapiro#if SASL
135590792Sgshapiro		  case CMDAUTH:
135690792Sgshapiro			/* avoid information leak; take first two words? */
135790792Sgshapiro			q = "AUTH";
135890792Sgshapiro			break;
135990792Sgshapiro#endif /* SASL */
136090792Sgshapiro
136190792Sgshapiro		  case CMDMAIL:
136290792Sgshapiro		  case CMDEXPN:
136390792Sgshapiro		  case CMDVRFY:
136490792Sgshapiro		  case CMDETRN:
136590792Sgshapiro			lognullconnection = false;
136690792Sgshapiro			/* FALLTHROUGH */
136790792Sgshapiro		  default:
136890792Sgshapiro			q = inp;
136990792Sgshapiro			break;
137090792Sgshapiro		}
137190792Sgshapiro
137290792Sgshapiro		if (e->e_id == NULL)
137390792Sgshapiro			sm_setproctitle(true, e, "%s: %.80s",
137490792Sgshapiro					CurSmtpClient, q);
137590792Sgshapiro		else
137690792Sgshapiro			sm_setproctitle(true, e, "%s %s: %.80s",
137790792Sgshapiro					qid_printname(e),
137890792Sgshapiro					CurSmtpClient, q);
137990792Sgshapiro
138038032Speter		/*
138138032Speter		**  Process command.
138238032Speter		**
138338032Speter		**	If we are running as a null server, return 550
138490792Sgshapiro		**	to almost everything.
138538032Speter		*/
138638032Speter
138764562Sgshapiro		if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
138838032Speter		{
138964562Sgshapiro			switch (c->cmd_code)
139038032Speter			{
139138032Speter			  case CMDQUIT:
139238032Speter			  case CMDHELO:
139338032Speter			  case CMDEHLO:
139438032Speter			  case CMDNOOP:
139564562Sgshapiro			  case CMDRSET:
1396125820Sgshapiro			  case CMDERROR:
139738032Speter				/* process normally */
139838032Speter				break;
139938032Speter
140064562Sgshapiro			  case CMDETRN:
140164562Sgshapiro				if (bitnset(D_ETRNONLY, d_flags) &&
140264562Sgshapiro				    nullserver == NULL)
140364562Sgshapiro					break;
140490792Sgshapiro				DELAY_CONN("ETRN");
140580785Sgshapiro				/* FALLTHROUGH */
140664562Sgshapiro
140738032Speter			  default:
140890792Sgshapiro#if MAXBADCOMMANDS > 0
140990792Sgshapiro				/* theoretically this could overflow */
141090792Sgshapiro				if (nullserver != NULL &&
141190792Sgshapiro				    ++n_badcmds > MAXBADCOMMANDS)
141264562Sgshapiro				{
141390792Sgshapiro					message("421 4.7.0 %s Too many bad commands; closing connection",
141490792Sgshapiro						MyHostName);
141590792Sgshapiro
141690792Sgshapiro					/* arrange to ignore send list */
141790792Sgshapiro					e->e_sendqueue = NULL;
141890792Sgshapiro					goto doquit;
141964562Sgshapiro				}
142090792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
142164562Sgshapiro				if (nullserver != NULL)
142264562Sgshapiro				{
142364562Sgshapiro					if (ISSMTPREPLY(nullserver))
142464562Sgshapiro						usrerr(nullserver);
142564562Sgshapiro					else
142690792Sgshapiro						usrerr("550 5.0.0 %s",
142790792Sgshapiro						       nullserver);
142864562Sgshapiro				}
142964562Sgshapiro				else
143064562Sgshapiro					usrerr("452 4.4.5 Insufficient disk space; try again later");
143138032Speter				continue;
143238032Speter			}
143338032Speter		}
143438032Speter
143564562Sgshapiro		switch (c->cmd_code)
143638032Speter		{
143790792Sgshapiro#if SASL
143864562Sgshapiro		  case CMDAUTH: /* sasl */
143990792Sgshapiro			DELAY_CONN("AUTH");
144090792Sgshapiro			if (!sasl_ok || n_mechs <= 0)
144164562Sgshapiro			{
144264562Sgshapiro				message("503 5.3.3 AUTH not available");
144364562Sgshapiro				break;
144464562Sgshapiro			}
144564562Sgshapiro			if (authenticating == SASL_IS_AUTH)
144664562Sgshapiro			{
144764562Sgshapiro				message("503 5.5.0 Already Authenticated");
144864562Sgshapiro				break;
144964562Sgshapiro			}
145090792Sgshapiro			if (smtp.sm_gotmail)
145164562Sgshapiro			{
145264562Sgshapiro				message("503 5.5.0 AUTH not permitted during a mail transaction");
145364562Sgshapiro				break;
145464562Sgshapiro			}
145564562Sgshapiro			if (tempfail)
145664562Sgshapiro			{
145764562Sgshapiro				if (LogLevel > 9)
145864562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1459110560Sgshapiro						  "SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)",
146064562Sgshapiro						  p, CurSmtpClient);
1461132943Sgshapiro				usrerr("454 4.3.0 Please try again later");
146264562Sgshapiro				break;
146364562Sgshapiro			}
146464562Sgshapiro
146590792Sgshapiro			ismore = false;
146664562Sgshapiro
146764562Sgshapiro			/* crude way to avoid crack attempts */
1468132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1,
1469132943Sgshapiro							true, "AUTH", e));
147064562Sgshapiro
147190792Sgshapiro			/* make sure mechanism (p) is a valid string */
147264562Sgshapiro			for (q = p; *q != '\0' && isascii(*q); q++)
147364562Sgshapiro			{
147464562Sgshapiro				if (isspace(*q))
147564562Sgshapiro				{
147664562Sgshapiro					*q = '\0';
147764562Sgshapiro					while (*++q != '\0' &&
147864562Sgshapiro					       isascii(*q) && isspace(*q))
147964562Sgshapiro						continue;
148064562Sgshapiro					*(q - 1) = '\0';
148164562Sgshapiro					ismore = (*q != '\0');
148264562Sgshapiro					break;
148364562Sgshapiro				}
148464562Sgshapiro			}
148564562Sgshapiro
148698121Sgshapiro			if (*p == '\0')
148798121Sgshapiro			{
148898121Sgshapiro				message("501 5.5.2 AUTH mechanism must be specified");
148998121Sgshapiro				break;
149098121Sgshapiro			}
149198121Sgshapiro
149264562Sgshapiro			/* check whether mechanism is available */
149364562Sgshapiro			if (iteminlist(p, mechlist, " ") == NULL)
149464562Sgshapiro			{
149598121Sgshapiro				message("504 5.3.3 AUTH mechanism %.32s not available",
149664562Sgshapiro					p);
149764562Sgshapiro				break;
149864562Sgshapiro			}
149964562Sgshapiro
150064562Sgshapiro			if (ismore)
150164562Sgshapiro			{
150264562Sgshapiro				/* could this be shorter? XXX */
150398121Sgshapiro# if SASL >= 20000
150498121Sgshapiro				in = xalloc(strlen(q) + 1);
1505102528Sgshapiro				result = sasl_decode64(q, strlen(q), in,
150698121Sgshapiro						       strlen(q), &inlen);
150798121Sgshapiro# else /* SASL >= 20000 */
150890792Sgshapiro				in = sm_rpool_malloc(e->e_rpool, strlen(q));
150964562Sgshapiro				result = sasl_decode64(q, strlen(q), in,
151090792Sgshapiro						       &inlen);
151198121Sgshapiro# endif /* SASL >= 20000 */
151264562Sgshapiro				if (result != SASL_OK)
151364562Sgshapiro				{
151464562Sgshapiro					message("501 5.5.4 cannot BASE64 decode '%s'",
151564562Sgshapiro						q);
151664562Sgshapiro					if (LogLevel > 5)
151764562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
151890792Sgshapiro							  "AUTH decode64 error [%d for \"%s\"]",
151964562Sgshapiro							  result, q);
152064562Sgshapiro					/* start over? */
152164562Sgshapiro					authenticating = SASL_NOT_AUTH;
152298121Sgshapiro# if SASL >= 20000
152398121Sgshapiro					sm_free(in);
152498121Sgshapiro# endif /* SASL >= 20000 */
152564562Sgshapiro					in = NULL;
152664562Sgshapiro					inlen = 0;
152764562Sgshapiro					break;
152864562Sgshapiro				}
152964562Sgshapiro			}
153064562Sgshapiro			else
153164562Sgshapiro			{
153264562Sgshapiro				in = NULL;
153364562Sgshapiro				inlen = 0;
153464562Sgshapiro			}
153564562Sgshapiro
153664562Sgshapiro			/* see if that auth type exists */
153798121Sgshapiro# if SASL >= 20000
1538102528Sgshapiro			result = sasl_server_start(conn, p, in, inlen,
153998121Sgshapiro						   &out, &outlen);
154098121Sgshapiro			if (in != NULL)
154198121Sgshapiro				sm_free(in);
154298121Sgshapiro# else /* SASL >= 20000 */
154364562Sgshapiro			result = sasl_server_start(conn, p, in, inlen,
154464562Sgshapiro						   &out, &outlen, &errstr);
154598121Sgshapiro# endif /* SASL >= 20000 */
154664562Sgshapiro
154764562Sgshapiro			if (result != SASL_OK && result != SASL_CONTINUE)
154864562Sgshapiro			{
154998121Sgshapiro				message("535 5.7.0 authentication failed");
155064562Sgshapiro				if (LogLevel > 9)
155164562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
155290792Sgshapiro						  "AUTH failure (%s): %s (%d) %s",
155364562Sgshapiro						  p,
155464562Sgshapiro						  sasl_errstring(result, NULL,
155564562Sgshapiro								 NULL),
155690792Sgshapiro						  result,
155798121Sgshapiro# if SASL >= 20000
155898121Sgshapiro						  sasl_errdetail(conn));
155998121Sgshapiro# else /* SASL >= 20000 */
156090792Sgshapiro						  errstr);
156198121Sgshapiro# endif /* SASL >= 20000 */
1562120256Sgshapiro				RESET_SASLCONN;
156364562Sgshapiro				break;
156464562Sgshapiro			}
156564562Sgshapiro			auth_type = newstr(p);
156664562Sgshapiro
156764562Sgshapiro			if (result == SASL_OK)
156864562Sgshapiro			{
156964562Sgshapiro				/* ugly, but same code */
157064562Sgshapiro				goto authenticated;
157164562Sgshapiro				/* authenticated by the initial response */
157264562Sgshapiro			}
157364562Sgshapiro
157464562Sgshapiro			/* len is at least 2 */
157564562Sgshapiro			len = ENC64LEN(outlen);
157664562Sgshapiro			out2 = xalloc(len);
157764562Sgshapiro			result = sasl_encode64(out, outlen, out2, len,
157890792Sgshapiro					       &out2len);
157964562Sgshapiro
158064562Sgshapiro			if (result != SASL_OK)
158164562Sgshapiro			{
158264562Sgshapiro				message("454 4.5.4 Temporary authentication failure");
158364562Sgshapiro				if (LogLevel > 5)
158464562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
158590792Sgshapiro						  "AUTH encode64 error [%d for \"%s\"]",
158664562Sgshapiro						  result, out);
158764562Sgshapiro
158864562Sgshapiro				/* start over? */
158964562Sgshapiro				authenticating = SASL_NOT_AUTH;
1590120256Sgshapiro				RESET_SASLCONN;
159164562Sgshapiro			}
159264562Sgshapiro			else
159364562Sgshapiro			{
159464562Sgshapiro				message("334 %s", out2);
159564562Sgshapiro				authenticating = SASL_PROC_AUTH;
159664562Sgshapiro			}
159798121Sgshapiro# if SASL >= 20000
159898121Sgshapiro			sm_free(out2);
159998121Sgshapiro# endif /* SASL >= 20000 */
160064562Sgshapiro			break;
160190792Sgshapiro#endif /* SASL */
160264562Sgshapiro
160390792Sgshapiro#if STARTTLS
160464562Sgshapiro		  case CMDSTLS: /* starttls */
160590792Sgshapiro			DELAY_CONN("STARTTLS");
160664562Sgshapiro			if (*p != '\0')
160764562Sgshapiro			{
160864562Sgshapiro				message("501 5.5.2 Syntax error (no parameters allowed)");
160964562Sgshapiro				break;
161064562Sgshapiro			}
161190792Sgshapiro			if (!bitset(SRV_OFFER_TLS, features))
161264562Sgshapiro			{
161364562Sgshapiro				message("503 5.5.0 TLS not available");
161464562Sgshapiro				break;
161564562Sgshapiro			}
161677349Sgshapiro			if (!tls_ok_srv)
161764562Sgshapiro			{
161864562Sgshapiro				message("454 4.3.3 TLS not available after start");
161964562Sgshapiro				break;
162064562Sgshapiro			}
162190792Sgshapiro			if (smtp.sm_gotmail)
162264562Sgshapiro			{
162364562Sgshapiro				message("503 5.5.0 TLS not permitted during a mail transaction");
162464562Sgshapiro				break;
162564562Sgshapiro			}
162664562Sgshapiro			if (tempfail)
162764562Sgshapiro			{
162864562Sgshapiro				if (LogLevel > 9)
162964562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
1630110560Sgshapiro						  "SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)",
163164562Sgshapiro						  p, CurSmtpClient);
1632132943Sgshapiro				usrerr("454 4.7.0 Please try again later");
163364562Sgshapiro				break;
163464562Sgshapiro			}
163590792Sgshapiro  starttls:
163664562Sgshapiro# if TLS_NO_RSA
163764562Sgshapiro			/*
163864562Sgshapiro			**  XXX do we need a temp key ?
163964562Sgshapiro			*/
164064562Sgshapiro# else /* TLS_NO_RSA */
164164562Sgshapiro# endif /* TLS_NO_RSA */
164290792Sgshapiro
164390792Sgshapiro# if TLS_VRFY_PER_CTX
164490792Sgshapiro			/*
164590792Sgshapiro			**  Note: this sets the verification globally
164690792Sgshapiro			**  (per SSL_CTX)
164790792Sgshapiro			**  it's ok since it applies only to one transaction
164890792Sgshapiro			*/
164990792Sgshapiro
165090792Sgshapiro			TLS_VERIFY_CLIENT();
165190792Sgshapiro# endif /* TLS_VRFY_PER_CTX */
165290792Sgshapiro
165364562Sgshapiro			if (srv_ssl != NULL)
165464562Sgshapiro				SSL_clear(srv_ssl);
165564562Sgshapiro			else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
165664562Sgshapiro			{
165764562Sgshapiro				message("454 4.3.3 TLS not available: error generating SSL handle");
1658120256Sgshapiro				if (LogLevel > 8)
1659120256Sgshapiro					tlslogerr("server");
166090792Sgshapiro				goto tls_done;
166164562Sgshapiro			}
166290792Sgshapiro
166390792Sgshapiro# if !TLS_VRFY_PER_CTX
166490792Sgshapiro			/*
166590792Sgshapiro			**  this could be used if it were possible to set
166690792Sgshapiro			**  verification per SSL (connection)
166790792Sgshapiro			**  not just per SSL_CTX (global)
166890792Sgshapiro			*/
166990792Sgshapiro
167090792Sgshapiro			TLS_VERIFY_CLIENT();
167190792Sgshapiro# endif /* !TLS_VRFY_PER_CTX */
167290792Sgshapiro
167390792Sgshapiro			rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
167490792Sgshapiro			wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
167590792Sgshapiro
167666494Sgshapiro			if (rfd < 0 || wfd < 0 ||
167766494Sgshapiro			    SSL_set_rfd(srv_ssl, rfd) <= 0 ||
167866494Sgshapiro			    SSL_set_wfd(srv_ssl, wfd) <= 0)
167964562Sgshapiro			{
168064562Sgshapiro				message("454 4.3.3 TLS not available: error set fd");
168164562Sgshapiro				SSL_free(srv_ssl);
168264562Sgshapiro				srv_ssl = NULL;
168390792Sgshapiro				goto tls_done;
168464562Sgshapiro			}
168590792Sgshapiro			if (!smtps)
168690792Sgshapiro				message("220 2.0.0 Ready to start TLS");
168790792Sgshapiro# if PIPELINING
168890792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
168990792Sgshapiro# endif /* PIPELINING */
169090792Sgshapiro
169164562Sgshapiro			SSL_set_accept_state(srv_ssl);
169264562Sgshapiro
169364562Sgshapiro#  define SSL_ACC(s)	SSL_accept(s)
169490792Sgshapiro
169590792Sgshapiro			tlsstart = curtime();
1696132943Sgshapiro			fdfl = fcntl(rfd, F_GETFL);
1697132943Sgshapiro			if (fdfl != -1)
1698132943Sgshapiro				fcntl(rfd, F_SETFL, fdfl|O_NONBLOCK);
169990792Sgshapiro  ssl_retry:
170064562Sgshapiro			if ((r = SSL_ACC(srv_ssl)) <= 0)
170164562Sgshapiro			{
170264562Sgshapiro				int i;
170390792Sgshapiro				bool timedout;
170490792Sgshapiro				time_t left;
170590792Sgshapiro				time_t now = curtime();
170690792Sgshapiro				struct timeval tv;
170764562Sgshapiro
170864562Sgshapiro				/* what to do in this case? */
170964562Sgshapiro				i = SSL_get_error(srv_ssl, r);
171090792Sgshapiro
171190792Sgshapiro				/*
171290792Sgshapiro				**  For SSL_ERROR_WANT_{READ,WRITE}:
171390792Sgshapiro				**  There is no SSL record available yet
171490792Sgshapiro				**  or there is only a partial SSL record
171590792Sgshapiro				**  removed from the network (socket) buffer
171690792Sgshapiro				**  into the SSL buffer. The SSL_accept will
171790792Sgshapiro				**  only succeed when a full SSL record is
171890792Sgshapiro				**  available (assuming a "real" error
171990792Sgshapiro				**  doesn't happen). To handle when a "real"
172090792Sgshapiro				**  error does happen the select is set for
172190792Sgshapiro				**  exceptions too.
172290792Sgshapiro				**  The connection may be re-negotiated
172390792Sgshapiro				**  during this time so both read and write
172490792Sgshapiro				**  "want errors" need to be handled.
172590792Sgshapiro				**  A select() exception loops back so that
172690792Sgshapiro				**  a proper SSL error message can be gotten.
172790792Sgshapiro				*/
172890792Sgshapiro
172990792Sgshapiro				left = TimeOuts.to_starttls - (now - tlsstart);
173090792Sgshapiro				timedout = left <= 0;
173190792Sgshapiro				if (!timedout)
173290792Sgshapiro				{
173390792Sgshapiro					tv.tv_sec = left;
173490792Sgshapiro					tv.tv_usec = 0;
173590792Sgshapiro				}
173690792Sgshapiro
1737110560Sgshapiro				if (!timedout && FD_SETSIZE > 0 &&
1738110560Sgshapiro				    (rfd >= FD_SETSIZE ||
1739110560Sgshapiro				     (i == SSL_ERROR_WANT_WRITE &&
1740110560Sgshapiro				      wfd >= FD_SETSIZE)))
1741110560Sgshapiro				{
1742110560Sgshapiro					if (LogLevel > 5)
1743110560Sgshapiro					{
1744110560Sgshapiro						sm_syslog(LOG_ERR, NOQID,
1745110560Sgshapiro							  "STARTTLS=server, error: fd %d/%d too large",
1746110560Sgshapiro							  rfd, wfd);
1747110560Sgshapiro						if (LogLevel > 8)
1748110560Sgshapiro							tlslogerr("server");
1749110560Sgshapiro					}
1750110560Sgshapiro					goto tlsfail;
1751110560Sgshapiro				}
1752110560Sgshapiro
175390792Sgshapiro				/* XXX what about SSL_pending() ? */
175490792Sgshapiro				if (!timedout && i == SSL_ERROR_WANT_READ)
175590792Sgshapiro				{
175690792Sgshapiro					fd_set ssl_maskr, ssl_maskx;
175790792Sgshapiro
175890792Sgshapiro					FD_ZERO(&ssl_maskr);
175990792Sgshapiro					FD_SET(rfd, &ssl_maskr);
176090792Sgshapiro					FD_ZERO(&ssl_maskx);
176190792Sgshapiro					FD_SET(rfd, &ssl_maskx);
176290792Sgshapiro					if (select(rfd + 1, &ssl_maskr, NULL,
176390792Sgshapiro						   &ssl_maskx, &tv) > 0)
176490792Sgshapiro						goto ssl_retry;
176590792Sgshapiro				}
176690792Sgshapiro				if (!timedout && i == SSL_ERROR_WANT_WRITE)
176790792Sgshapiro				{
176890792Sgshapiro					fd_set ssl_maskw, ssl_maskx;
176990792Sgshapiro
177090792Sgshapiro					FD_ZERO(&ssl_maskw);
177190792Sgshapiro					FD_SET(wfd, &ssl_maskw);
177290792Sgshapiro					FD_ZERO(&ssl_maskx);
177390792Sgshapiro					FD_SET(rfd, &ssl_maskx);
177490792Sgshapiro					if (select(wfd + 1, NULL, &ssl_maskw,
177590792Sgshapiro						   &ssl_maskx, &tv) > 0)
177690792Sgshapiro						goto ssl_retry;
177790792Sgshapiro				}
177864562Sgshapiro				if (LogLevel > 5)
177964562Sgshapiro				{
178090792Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
1781111823Sgshapiro						  "STARTTLS=server, error: accept failed=%d, SSL_error=%d, timedout=%d, errno=%d",
1782111823Sgshapiro						  r, i, (int) timedout, errno);
178390792Sgshapiro					if (LogLevel > 8)
178490792Sgshapiro						tlslogerr("server");
178564562Sgshapiro				}
1786110560Sgshapirotlsfail:
178790792Sgshapiro				tls_ok_srv = false;
178864562Sgshapiro				SSL_free(srv_ssl);
178964562Sgshapiro				srv_ssl = NULL;
179064562Sgshapiro
179164562Sgshapiro				/*
179264562Sgshapiro				**  according to the next draft of
179364562Sgshapiro				**  RFC 2487 the connection should be dropped
179464562Sgshapiro				*/
179564562Sgshapiro
179664562Sgshapiro				/* arrange to ignore any current send list */
179764562Sgshapiro				e->e_sendqueue = NULL;
179864562Sgshapiro				goto doquit;
179964562Sgshapiro			}
180064562Sgshapiro
1801132943Sgshapiro			if (fdfl != -1)
1802132943Sgshapiro				fcntl(rfd, F_SETFL, fdfl);
1803132943Sgshapiro
180464562Sgshapiro			/* ignore return code for now, it's in {verify} */
180590792Sgshapiro			(void) tls_get_info(srv_ssl, true,
180690792Sgshapiro					    CurSmtpClient,
180790792Sgshapiro					    &BlankEnvelope.e_macro,
180890792Sgshapiro					    bitset(SRV_VRFY_CLT, features));
180964562Sgshapiro
181064562Sgshapiro			/*
181164562Sgshapiro			**  call Stls_client to find out whether
181264562Sgshapiro			**  to accept the connection from the client
181364562Sgshapiro			*/
181464562Sgshapiro
181564562Sgshapiro			saveQuickAbort = QuickAbort;
181664562Sgshapiro			saveSuprErrs = SuprErrs;
181790792Sgshapiro			SuprErrs = true;
181890792Sgshapiro			QuickAbort = false;
181964562Sgshapiro			if (rscheck("tls_client",
182090792Sgshapiro				     macvalue(macid("{verify}"), e),
1821102528Sgshapiro				     "STARTTLS", e,
1822102528Sgshapiro				     RSF_RMCOMM|RSF_COUNT,
1823102528Sgshapiro				     5, NULL, NOQID) != EX_OK ||
182490792Sgshapiro			    Errors > 0)
182564562Sgshapiro			{
182664562Sgshapiro				extern char MsgBuf[];
182764562Sgshapiro
182864562Sgshapiro				if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
182964562Sgshapiro					nullserver = newstr(MsgBuf);
183064562Sgshapiro				else
183164562Sgshapiro					nullserver = "503 5.7.0 Authentication required.";
183264562Sgshapiro			}
183364562Sgshapiro			QuickAbort = saveQuickAbort;
183464562Sgshapiro			SuprErrs = saveSuprErrs;
183564562Sgshapiro
183690792Sgshapiro			tls_ok_srv = false;	/* don't offer STARTTLS again */
183764562Sgshapiro			n_helo = 0;
183890792Sgshapiro# if SASL
183964562Sgshapiro			if (sasl_ok)
184064562Sgshapiro			{
1841132943Sgshapiro				int cipher_bits;
1842132943Sgshapiro				bool verified;
1843132943Sgshapiro				char *s, *v, *c;
184464562Sgshapiro
184590792Sgshapiro				s = macvalue(macid("{cipher_bits}"), e);
1846132943Sgshapiro				v = macvalue(macid("{verify}"), e);
1847132943Sgshapiro				c = macvalue(macid("{cert_subject}"), e);
1848132943Sgshapiro				verified = (v != NULL && strcmp(v, "OK") == 0);
1849132943Sgshapiro				if (s != NULL && (cipher_bits = atoi(s)) > 0)
1850132943Sgshapiro				{
185198121Sgshapiro#  if SASL >= 20000
1852132943Sgshapiro					ext_ssf = cipher_bits;
1853132943Sgshapiro					auth_id = verified ? c : NULL;
1854132943Sgshapiro					sasl_ok = ((sasl_setprop(conn,
1855132943Sgshapiro							SASL_SSF_EXTERNAL,
1856132943Sgshapiro							&ext_ssf) == SASL_OK) &&
1857132943Sgshapiro						   (sasl_setprop(conn,
1858132943Sgshapiro							SASL_AUTH_EXTERNAL,
1859132943Sgshapiro							auth_id) == SASL_OK));
186098121Sgshapiro#  else /* SASL >= 20000 */
1861132943Sgshapiro					ext_ssf.ssf = cipher_bits;
1862132943Sgshapiro					ext_ssf.auth_id = verified ? c : NULL;
1863132943Sgshapiro					sasl_ok = sasl_setprop(conn,
1864132943Sgshapiro							SASL_SSF_EXTERNAL,
1865132943Sgshapiro							&ext_ssf) == SASL_OK;
186698121Sgshapiro#  endif /* SASL >= 20000 */
186764562Sgshapiro					mechlist = NULL;
186864562Sgshapiro					if (sasl_ok)
186964562Sgshapiro						n_mechs = saslmechs(conn,
187064562Sgshapiro								    &mechlist);
187164562Sgshapiro				}
187264562Sgshapiro			}
187390792Sgshapiro# endif /* SASL */
187464562Sgshapiro
187564562Sgshapiro			/* switch to secure connection */
187690792Sgshapiro			if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
187790792Sgshapiro			{
187890792Sgshapiro				tls_active = true;
187990792Sgshapiro# if PIPELINING
188090792Sgshapiro				(void) sm_io_autoflush(InChannel, OutChannel);
188190792Sgshapiro# endif /* PIPELINING */
188290792Sgshapiro			}
188364562Sgshapiro			else
188464562Sgshapiro			{
188564562Sgshapiro				/*
188664562Sgshapiro				**  XXX this is an internal error
188764562Sgshapiro				**  how to deal with it?
188864562Sgshapiro				**  we can't generate an error message
188964562Sgshapiro				**  since the other side switched to an
189064562Sgshapiro				**  encrypted layer, but we could not...
189164562Sgshapiro				**  just "hang up"?
189264562Sgshapiro				*/
189390792Sgshapiro
189464562Sgshapiro				nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
189590792Sgshapiro				syserr("STARTTLS: can't switch to encrypted layer");
189664562Sgshapiro			}
189790792Sgshapiro		  tls_done:
189890792Sgshapiro			if (smtps)
189990792Sgshapiro			{
190090792Sgshapiro				if (tls_active)
190190792Sgshapiro					goto greeting;
190290792Sgshapiro				else
190390792Sgshapiro					goto doquit;
190490792Sgshapiro			}
190564562Sgshapiro			break;
190690792Sgshapiro#endif /* STARTTLS */
190764562Sgshapiro
190838032Speter		  case CMDHELO:		/* hello -- introduce yourself */
190938032Speter		  case CMDEHLO:		/* extended hello */
191090792Sgshapiro			DELAY_CONN("EHLO");
191164562Sgshapiro			if (c->cmd_code == CMDEHLO)
191238032Speter			{
191338032Speter				protocol = "ESMTP";
191438032Speter				SmtpPhase = "server EHLO";
191538032Speter			}
191638032Speter			else
191738032Speter			{
191838032Speter				protocol = "SMTP";
191938032Speter				SmtpPhase = "server HELO";
192038032Speter			}
192138032Speter
192238032Speter			/* avoid denial-of-service */
1923132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
1924132943Sgshapiro							true, "HELO/EHLO", e));
192538032Speter
192690792Sgshapiro#if 0
192790792Sgshapiro			/* RFC2821 4.1.4 allows duplicate HELO/EHLO */
192838032Speter			/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
192938032Speter			if (gothello)
193038032Speter			{
193138032Speter				usrerr("503 %s Duplicate HELO/EHLO",
193273188Sgshapiro				       MyHostName);
193338032Speter				break;
193438032Speter			}
193590792Sgshapiro#endif /* 0 */
193638032Speter
193738032Speter			/* check for valid domain name (re 1123 5.2.5) */
193838032Speter			if (*p == '\0' && !AllowBogusHELO)
193938032Speter			{
194038032Speter				usrerr("501 %s requires domain address",
194138032Speter					cmdbuf);
194238032Speter				break;
194338032Speter			}
194438032Speter
194538032Speter			/* check for long domain name (hides Received: info) */
194638032Speter			if (strlen(p) > MAXNAME)
194738032Speter			{
194838032Speter				usrerr("501 Invalid domain name");
194964562Sgshapiro				if (LogLevel > 9)
195064562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
1951110560Sgshapiro						  "invalid domain name (too long) from %s",
195264562Sgshapiro						  CurSmtpClient);
195338032Speter				break;
195438032Speter			}
195538032Speter
195698121Sgshapiro			ok = true;
195738032Speter			for (q = p; *q != '\0'; q++)
195838032Speter			{
195938032Speter				if (!isascii(*q))
196038032Speter					break;
196138032Speter				if (isalnum(*q))
196238032Speter					continue;
196338032Speter				if (isspace(*q))
196438032Speter				{
196538032Speter					*q = '\0';
196698121Sgshapiro
196798121Sgshapiro					/* only complain if strict check */
196898121Sgshapiro					ok = AllowBogusHELO;
196938032Speter					break;
197038032Speter				}
1971120256Sgshapiro				if (strchr("[].-_#:", *q) == NULL)
197238032Speter					break;
197338032Speter			}
197464562Sgshapiro
197598121Sgshapiro			if (*q == '\0' && ok)
197638032Speter			{
197738032Speter				q = "pleased to meet you";
197890792Sgshapiro				sendinghost = sm_strdup_x(p);
197938032Speter			}
198038032Speter			else if (!AllowBogusHELO)
198138032Speter			{
198238032Speter				usrerr("501 Invalid domain name");
198364562Sgshapiro				if (LogLevel > 9)
198464562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
1985110560Sgshapiro						  "invalid domain name (%s) from %.100s",
198664562Sgshapiro						  p, CurSmtpClient);
198738032Speter				break;
198838032Speter			}
198938032Speter			else
199038032Speter			{
199138032Speter				q = "accepting invalid domain name";
199238032Speter			}
199338032Speter
199490792Sgshapiro			if (gothello)
199590792Sgshapiro			{
199690792Sgshapiro				CLEAR_STATE(cmdbuf);
199790792Sgshapiro			}
199890792Sgshapiro
199990792Sgshapiro#if MILTER
200090792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
200190792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
200264562Sgshapiro			{
200364562Sgshapiro				char state;
200464562Sgshapiro				char *response;
200564562Sgshapiro
200664562Sgshapiro				response = milter_helo(p, e, &state);
200764562Sgshapiro				switch (state)
200864562Sgshapiro				{
200964562Sgshapiro				  case SMFIR_REPLYCODE:
201090792Sgshapiro					if (MilterLogLevel > 3)
201190792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
201290792Sgshapiro							  "Milter: helo=%s, reject=%s",
201390792Sgshapiro							  p, response);
201490792Sgshapiro					nullserver = newstr(response);
201590792Sgshapiro					smtp.sm_milterize = false;
201664562Sgshapiro					break;
201764562Sgshapiro
201864562Sgshapiro				  case SMFIR_REJECT:
201990792Sgshapiro					if (MilterLogLevel > 3)
202090792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
202190792Sgshapiro							  "Milter: helo=%s, reject=Command rejected",
202290792Sgshapiro							  p);
202364562Sgshapiro					nullserver = "Command rejected";
202490792Sgshapiro					smtp.sm_milterize = false;
202564562Sgshapiro					break;
202664562Sgshapiro
202764562Sgshapiro				  case SMFIR_TEMPFAIL:
202890792Sgshapiro					if (MilterLogLevel > 3)
202990792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
203090792Sgshapiro							  "Milter: helo=%s, reject=%s",
203190792Sgshapiro							  p, MSG_TEMPFAIL);
203290792Sgshapiro					tempfail = true;
203390792Sgshapiro					smtp.sm_milterize = false;
203464562Sgshapiro					break;
203564562Sgshapiro				}
203690792Sgshapiro				if (response != NULL)
203790792Sgshapiro					sm_free(response);
203890792Sgshapiro
203990792Sgshapiro				/*
204090792Sgshapiro				**  If quarantining by a connect/ehlo action,
204190792Sgshapiro				**  save between messages
204290792Sgshapiro				*/
204390792Sgshapiro
204490792Sgshapiro				if (smtp.sm_quarmsg == NULL &&
204590792Sgshapiro				    e->e_quarmsg != NULL)
204690792Sgshapiro					smtp.sm_quarmsg = newstr(e->e_quarmsg);
204764562Sgshapiro			}
204890792Sgshapiro#endif /* MILTER */
204990792Sgshapiro			gothello = true;
205064562Sgshapiro
205138032Speter			/* print HELO response message */
205264562Sgshapiro			if (c->cmd_code != CMDEHLO)
205338032Speter			{
205438032Speter				message("250 %s Hello %s, %s",
205538032Speter					MyHostName, CurSmtpClient, q);
205638032Speter				break;
205738032Speter			}
205838032Speter
205938032Speter			message("250-%s Hello %s, %s",
206038032Speter				MyHostName, CurSmtpClient, q);
206138032Speter
206264562Sgshapiro			/* offer ENHSC even for nullserver */
206364562Sgshapiro			if (nullserver != NULL)
206464562Sgshapiro			{
206564562Sgshapiro				message("250 ENHANCEDSTATUSCODES");
206664562Sgshapiro				break;
206764562Sgshapiro			}
206864562Sgshapiro
206966494Sgshapiro			/*
207066494Sgshapiro			**  print EHLO features list
207166494Sgshapiro			**
207266494Sgshapiro			**  Note: If you change this list,
207390792Sgshapiro			**	  remember to update 'helpfile'
207466494Sgshapiro			*/
207566494Sgshapiro
207664562Sgshapiro			message("250-ENHANCEDSTATUSCODES");
207790792Sgshapiro#if PIPELINING
207890792Sgshapiro			if (bitset(SRV_OFFER_PIPE, features))
207990792Sgshapiro				message("250-PIPELINING");
208090792Sgshapiro#endif /* PIPELINING */
208190792Sgshapiro			if (bitset(SRV_OFFER_EXPN, features))
208238032Speter			{
208338032Speter				message("250-EXPN");
208490792Sgshapiro				if (bitset(SRV_OFFER_VERB, features))
208538032Speter					message("250-VERB");
208638032Speter			}
208790792Sgshapiro#if MIME8TO7
208838032Speter			message("250-8BITMIME");
208990792Sgshapiro#endif /* MIME8TO7 */
209038032Speter			if (MaxMessageSize > 0)
209138032Speter				message("250-SIZE %ld", MaxMessageSize);
209238032Speter			else
209338032Speter				message("250-SIZE");
209490792Sgshapiro#if DSN
209590792Sgshapiro			if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
209638032Speter				message("250-DSN");
209790792Sgshapiro#endif /* DSN */
209890792Sgshapiro			if (bitset(SRV_OFFER_ETRN, features))
209938032Speter				message("250-ETRN");
210090792Sgshapiro#if SASL
210164562Sgshapiro			if (sasl_ok && mechlist != NULL && *mechlist != '\0')
210264562Sgshapiro				message("250-AUTH %s", mechlist);
210390792Sgshapiro#endif /* SASL */
210490792Sgshapiro#if STARTTLS
210598841Sgshapiro			if (tls_ok_srv &&
210698841Sgshapiro			    bitset(SRV_OFFER_TLS, features))
210764562Sgshapiro				message("250-STARTTLS");
210890792Sgshapiro#endif /* STARTTLS */
210990792Sgshapiro			if (DeliverByMin > 0)
211090792Sgshapiro				message("250-DELIVERBY %ld",
211190792Sgshapiro					(long) DeliverByMin);
211290792Sgshapiro			else if (DeliverByMin == 0)
211390792Sgshapiro				message("250-DELIVERBY");
211490792Sgshapiro
211590792Sgshapiro			/* < 0: no deliver-by */
211690792Sgshapiro
211738032Speter			message("250 HELP");
211838032Speter			break;
211938032Speter
212038032Speter		  case CMDMAIL:		/* mail -- designate sender */
212138032Speter			SmtpPhase = "server MAIL";
212290792Sgshapiro			DELAY_CONN("MAIL");
212338032Speter
212438032Speter			/* check for validity of this command */
212538032Speter			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
212638032Speter			{
212764562Sgshapiro				usrerr("503 5.0.0 Polite people say HELO first");
212838032Speter				break;
212938032Speter			}
213090792Sgshapiro			if (smtp.sm_gotmail)
213138032Speter			{
213264562Sgshapiro				usrerr("503 5.5.0 Sender already specified");
213338032Speter				break;
213438032Speter			}
213590792Sgshapiro#if SASL
213690792Sgshapiro			if (bitset(SRV_REQ_AUTH, features) &&
213764562Sgshapiro			    authenticating != SASL_IS_AUTH)
213864562Sgshapiro			{
213964562Sgshapiro				usrerr("530 5.7.0 Authentication required");
214064562Sgshapiro				break;
214164562Sgshapiro			}
214290792Sgshapiro#endif /* SASL */
214338032Speter
214464562Sgshapiro			p = skipword(p, "from");
214564562Sgshapiro			if (p == NULL)
214664562Sgshapiro				break;
214764562Sgshapiro			if (tempfail)
214864562Sgshapiro			{
214964562Sgshapiro				if (LogLevel > 9)
215064562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2151110560Sgshapiro						  "SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)",
215264562Sgshapiro						  p, CurSmtpClient);
215390792Sgshapiro				usrerr(MSG_TEMPFAIL);
215464562Sgshapiro				break;
215564562Sgshapiro			}
215664562Sgshapiro
215738032Speter			/* make sure we know who the sending host is */
215838032Speter			if (sendinghost == NULL)
215938032Speter				sendinghost = peerhostname;
216038032Speter
216138032Speter
216290792Sgshapiro#if SM_HEAP_CHECK
216390792Sgshapiro			if (sm_debug_active(&DebugLeakSmtp, 1))
216490792Sgshapiro			{
216590792Sgshapiro				sm_heap_newgroup();
216690792Sgshapiro				sm_dprintf("smtp() heap group #%d\n",
216790792Sgshapiro					sm_heap_group());
216890792Sgshapiro			}
216990792Sgshapiro#endif /* SM_HEAP_CHECK */
217064562Sgshapiro
217138032Speter			if (Errors > 0)
217290792Sgshapiro				goto undo_no_pm;
217338032Speter			if (!gothello)
217438032Speter			{
217590792Sgshapiro				auth_warning(e, "%s didn't use HELO protocol",
217690792Sgshapiro					     CurSmtpClient);
217738032Speter			}
217890792Sgshapiro#ifdef PICKY_HELO_CHECK
217990792Sgshapiro			if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
218090792Sgshapiro			    (sm_strcasecmp(peerhostname, "localhost") != 0 ||
218190792Sgshapiro			     sm_strcasecmp(sendinghost, MyHostName) != 0))
218238032Speter			{
218338032Speter				auth_warning(e, "Host %s claimed to be %s",
218490792Sgshapiro					     CurSmtpClient, sendinghost);
218538032Speter			}
218690792Sgshapiro#endif /* PICKY_HELO_CHECK */
218738032Speter
218838032Speter			if (protocol == NULL)
218938032Speter				protocol = "SMTP";
219090792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'r', protocol);
219190792Sgshapiro			macdefine(&e->e_macro, A_PERM, 's', sendinghost);
219264562Sgshapiro
219338032Speter			if (Errors > 0)
219490792Sgshapiro				goto undo_no_pm;
219590792Sgshapiro			smtp.sm_nrcpts = 0;
219690792Sgshapiro			n_badrcpts = 0;
219790792Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
219890792Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
2199132943Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"),
2200132943Sgshapiro				"0");
220164562Sgshapiro			e->e_flags |= EF_CLRQUEUE;
220290792Sgshapiro			sm_setproctitle(true, e, "%s %s: %.80s",
220364562Sgshapiro					qid_printname(e),
220464562Sgshapiro					CurSmtpClient, inp);
220538032Speter
220690792Sgshapiro			/* do the processing */
220790792Sgshapiro		    SM_TRY
220890792Sgshapiro		    {
220994334Sgshapiro			extern char *FullName;
221094334Sgshapiro
221190792Sgshapiro			QuickAbort = true;
221294334Sgshapiro			SM_FREE_CLR(FullName);
221364562Sgshapiro
221438032Speter			/* must parse sender first */
221538032Speter			delimptr = NULL;
221690792Sgshapiro			setsender(p, e, &delimptr, ' ', false);
221738032Speter			if (delimptr != NULL && *delimptr != '\0')
221838032Speter				*delimptr++ = '\0';
221938032Speter			if (Errors > 0)
222090792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
222138032Speter
222264562Sgshapiro			/* Successfully set e_from, allow logging */
222364562Sgshapiro			e->e_flags |= EF_LOGSENDER;
222438032Speter
222564562Sgshapiro			/* put resulting triple from parseaddr() into macros */
222664562Sgshapiro			if (e->e_from.q_mailer != NULL)
222790792Sgshapiro				 macdefine(&e->e_macro, A_PERM,
222890792Sgshapiro					macid("{mail_mailer}"),
222990792Sgshapiro					e->e_from.q_mailer->m_name);
223064562Sgshapiro			else
223190792Sgshapiro				 macdefine(&e->e_macro, A_PERM,
223290792Sgshapiro					macid("{mail_mailer}"), NULL);
223364562Sgshapiro			if (e->e_from.q_host != NULL)
223490792Sgshapiro				macdefine(&e->e_macro, A_PERM,
223590792Sgshapiro					macid("{mail_host}"),
223690792Sgshapiro					e->e_from.q_host);
223764562Sgshapiro			else
223890792Sgshapiro				macdefine(&e->e_macro, A_PERM,
223990792Sgshapiro					macid("{mail_host}"), "localhost");
224064562Sgshapiro			if (e->e_from.q_user != NULL)
224190792Sgshapiro				macdefine(&e->e_macro, A_PERM,
224290792Sgshapiro					macid("{mail_addr}"),
224390792Sgshapiro					e->e_from.q_user);
224464562Sgshapiro			else
224590792Sgshapiro				macdefine(&e->e_macro, A_PERM,
224690792Sgshapiro					macid("{mail_addr}"), NULL);
224764562Sgshapiro			if (Errors > 0)
224890792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
224964562Sgshapiro
225038032Speter			/* check for possible spoofing */
225138032Speter			if (RealUid != 0 && OpMode == MD_SMTP &&
225238032Speter			    !wordinclass(RealUserName, 't') &&
225364562Sgshapiro			    (!bitnset(M_LOCALMAILER,
225464562Sgshapiro				      e->e_from.q_mailer->m_flags) ||
225564562Sgshapiro			     strcmp(e->e_from.q_user, RealUserName) != 0))
225638032Speter			{
225738032Speter				auth_warning(e, "%s owned process doing -bs",
225838032Speter					RealUserName);
225938032Speter			}
226038032Speter
2261120256Sgshapiro			/* reset to default value */
2262120256Sgshapiro			SevenBitInput = save_sevenbitinput;
2263120256Sgshapiro
226438032Speter			/* now parse ESMTP arguments */
226538032Speter			e->e_msgsize = 0;
226664562Sgshapiro			addr = p;
226764562Sgshapiro			argno = 0;
226864562Sgshapiro			args[argno++] = p;
226938032Speter			p = delimptr;
227038032Speter			while (p != NULL && *p != '\0')
227138032Speter			{
227238032Speter				char *kp;
227338032Speter				char *vp = NULL;
227464562Sgshapiro				char *equal = NULL;
227538032Speter
227638032Speter				/* locate the beginning of the keyword */
227790792Sgshapiro				SKIP_SPACE(p);
227838032Speter				if (*p == '\0')
227938032Speter					break;
228038032Speter				kp = p;
228138032Speter
228238032Speter				/* skip to the value portion */
228338032Speter				while ((isascii(*p) && isalnum(*p)) || *p == '-')
228438032Speter					p++;
228538032Speter				if (*p == '=')
228638032Speter				{
228764562Sgshapiro					equal = p;
228838032Speter					*p++ = '\0';
228938032Speter					vp = p;
229038032Speter
229138032Speter					/* skip to the end of the value */
229238032Speter					while (*p != '\0' && *p != ' ' &&
229338032Speter					       !(isascii(*p) && iscntrl(*p)) &&
229438032Speter					       *p != '=')
229538032Speter						p++;
229638032Speter				}
229738032Speter
229838032Speter				if (*p != '\0')
229938032Speter					*p++ = '\0';
230038032Speter
230138032Speter				if (tTd(19, 1))
230290792Sgshapiro					sm_dprintf("MAIL: got arg %s=\"%s\"\n", kp,
230338032Speter						vp == NULL ? "<null>" : vp);
230438032Speter
230538032Speter				mail_esmtp_args(kp, vp, e);
230664562Sgshapiro				if (equal != NULL)
230764562Sgshapiro					*equal = '=';
230864562Sgshapiro				args[argno++] = kp;
230964562Sgshapiro				if (argno >= MAXSMTPARGS - 1)
231064562Sgshapiro					usrerr("501 5.5.4 Too many parameters");
231138032Speter				if (Errors > 0)
231290792Sgshapiro					sm_exc_raisenew_x(&EtypeQuickAbort, 1);
231338032Speter			}
231464562Sgshapiro			args[argno] = NULL;
231538032Speter			if (Errors > 0)
231690792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
231738032Speter
231890792Sgshapiro#if SASL
231990792Sgshapiro# if _FFR_AUTH_PASSING
232090792Sgshapiro			/* set the default AUTH= if the sender didn't */
232190792Sgshapiro			if (e->e_auth_param == NULL)
232290792Sgshapiro			{
232390792Sgshapiro				/* XXX only do this for an MSA? */
232490792Sgshapiro				e->e_auth_param = macvalue(macid("{auth_authen}"),
232590792Sgshapiro							   e);
232690792Sgshapiro				if (e->e_auth_param == NULL)
232790792Sgshapiro					e->e_auth_param = "<>";
232890792Sgshapiro
232990792Sgshapiro				/*
233090792Sgshapiro				**  XXX should we invoke Strust_auth now?
233190792Sgshapiro				**  authorizing as the client that just
233290792Sgshapiro				**  authenticated, so we'll trust implicitly
233390792Sgshapiro				*/
233490792Sgshapiro			}
233590792Sgshapiro# endif /* _FFR_AUTH_PASSING */
233690792Sgshapiro#endif /* SASL */
233790792Sgshapiro
233864562Sgshapiro			/* do config file checking of the sender */
233990792Sgshapiro			macdefine(&e->e_macro, A_PERM,
234090792Sgshapiro				macid("{addr_type}"), "e s");
234190792Sgshapiro#if _FFR_MAIL_MACRO
234290792Sgshapiro			/* make the "real" sender address available */
234390792Sgshapiro			macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
234490792Sgshapiro				  e->e_from.q_paddr);
234590792Sgshapiro#endif /* _FFR_MAIL_MACRO */
234664562Sgshapiro			if (rscheck("check_mail", addr,
2347102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
2348102528Sgshapiro				    NULL, e->e_id) != EX_OK ||
234964562Sgshapiro			    Errors > 0)
235090792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
235190792Sgshapiro			macdefine(&e->e_macro, A_PERM,
235290792Sgshapiro				  macid("{addr_type}"), NULL);
235364562Sgshapiro
235466494Sgshapiro			if (MaxMessageSize > 0 &&
235577349Sgshapiro			    (e->e_msgsize > MaxMessageSize ||
235677349Sgshapiro			     e->e_msgsize < 0))
235738032Speter			{
235864562Sgshapiro				usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
235938032Speter					MaxMessageSize);
236090792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
236138032Speter			}
236264562Sgshapiro
236390792Sgshapiro			/*
236490792Sgshapiro			**  XXX always check whether there is at least one fs
236590792Sgshapiro			**  with enough space?
236690792Sgshapiro			**  However, this may not help much: the queue group
236790792Sgshapiro			**  selection may later on select a FS that hasn't
236890792Sgshapiro			**  enough space.
236990792Sgshapiro			*/
237090792Sgshapiro
237190792Sgshapiro			if ((NumFileSys == 1 || NumQueue == 1) &&
237290792Sgshapiro			    !enoughdiskspace(e->e_msgsize, e)
237390792Sgshapiro#if _FFR_ANY_FREE_FS
237490792Sgshapiro			    && !filesys_free(e->e_msgsize)
237590792Sgshapiro#endif /* _FFR_ANY_FREE_FS */
237690792Sgshapiro			   )
237738032Speter			{
237890792Sgshapiro				/*
237990792Sgshapiro				**  We perform this test again when the
238090792Sgshapiro				**  queue directory is selected, in collect.
238190792Sgshapiro				*/
238290792Sgshapiro
238364562Sgshapiro				usrerr("452 4.4.5 Insufficient disk space; try again later");
238490792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
238538032Speter			}
238638032Speter			if (Errors > 0)
238790792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
238864562Sgshapiro
238990792Sgshapiro			LogUsrErrs = true;
239090792Sgshapiro#if MILTER
239190792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
239290792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
239364562Sgshapiro			{
239464562Sgshapiro				char state;
239564562Sgshapiro				char *response;
239664562Sgshapiro
239764562Sgshapiro				response = milter_envfrom(args, e, &state);
239890792Sgshapiro				MILTER_REPLY("from");
239964562Sgshapiro			}
240090792Sgshapiro#endif /* MILTER */
240164562Sgshapiro			if (Errors > 0)
240290792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
240364562Sgshapiro
240464562Sgshapiro			message("250 2.1.0 Sender ok");
240590792Sgshapiro			smtp.sm_gotmail = true;
240690792Sgshapiro		    }
240790792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
240890792Sgshapiro		    {
240990792Sgshapiro			/*
241090792Sgshapiro			**  An error occurred while processing a MAIL command.
241190792Sgshapiro			**  Jump to the common error handling code.
241290792Sgshapiro			*/
241390792Sgshapiro
241490792Sgshapiro			sm_exc_free(exc);
241590792Sgshapiro			goto undo_no_pm;
241690792Sgshapiro		    }
241790792Sgshapiro		    SM_END_TRY
241838032Speter			break;
241938032Speter
242090792Sgshapiro		  undo_no_pm:
242190792Sgshapiro			e->e_flags &= ~EF_PM_NOTIFY;
242290792Sgshapiro		  undo:
242390792Sgshapiro			break;
242490792Sgshapiro
242538032Speter		  case CMDRCPT:		/* rcpt -- designate recipient */
242690792Sgshapiro			DELAY_CONN("RCPT");
2427125820Sgshapiro			if (BadRcptThrottle > 0 &&
2428125820Sgshapiro			    n_badrcpts >= BadRcptThrottle)
2429125820Sgshapiro			{
2430125820Sgshapiro				if (LogLevel > 5 &&
2431125820Sgshapiro				    n_badrcpts == BadRcptThrottle)
2432125820Sgshapiro				{
2433125820Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2434125820Sgshapiro						  "%s: Possible SMTP RCPT flood, throttling.",
2435125820Sgshapiro						  CurSmtpClient);
2436125820Sgshapiro
2437125820Sgshapiro					/* To avoid duplicated message */
2438125820Sgshapiro					n_badrcpts++;
2439125820Sgshapiro				}
2440132943Sgshapiro				NBADRCPTS;
2441125820Sgshapiro
2442125820Sgshapiro				/*
2443125820Sgshapiro				**  Don't use exponential backoff for now.
2444125820Sgshapiro				**  Some servers will open more connections
2445125820Sgshapiro				**  and actually overload the receiver even
2446125820Sgshapiro				**  more.
2447125820Sgshapiro				*/
2448125820Sgshapiro
2449125820Sgshapiro				(void) sleep(1);
2450125820Sgshapiro			}
245190792Sgshapiro			if (!smtp.sm_gotmail)
245238032Speter			{
245364562Sgshapiro				usrerr("503 5.0.0 Need MAIL before RCPT");
245438032Speter				break;
245538032Speter			}
245638032Speter			SmtpPhase = "server RCPT";
245790792Sgshapiro		    SM_TRY
245890792Sgshapiro		    {
245990792Sgshapiro			QuickAbort = true;
246090792Sgshapiro			LogUsrErrs = true;
246138032Speter
246238032Speter			/* limit flooding of our machine */
246390792Sgshapiro			if (MaxRcptPerMsg > 0 &&
246490792Sgshapiro			    smtp.sm_nrcpts >= MaxRcptPerMsg)
246538032Speter			{
246690792Sgshapiro				/* sleep(1); / * slow down? */
246764562Sgshapiro				usrerr("452 4.5.3 Too many recipients");
246890792Sgshapiro				goto rcpt_done;
246938032Speter			}
247038032Speter
247138032Speter			if (e->e_sendmode != SM_DELIVER)
247238032Speter				e->e_flags |= EF_VRFYONLY;
247338032Speter
247490792Sgshapiro#if MILTER
247564562Sgshapiro			/*
247664562Sgshapiro			**  If the filter will be deleting recipients,
247764562Sgshapiro			**  don't expand them at RCPT time (in the call
247864562Sgshapiro			**  to recipient()).  If they are expanded, it
247964562Sgshapiro			**  is impossible for removefromlist() to figure
248064562Sgshapiro			**  out the expanded members of the original
248164562Sgshapiro			**  recipient and mark them as QS_DONTSEND.
248264562Sgshapiro			*/
248364562Sgshapiro
248464562Sgshapiro			if (milter_can_delrcpts())
248564562Sgshapiro				e->e_flags |= EF_VRFYONLY;
248690792Sgshapiro#endif /* MILTER */
248764562Sgshapiro
248838032Speter			p = skipword(p, "to");
248938032Speter			if (p == NULL)
249090792Sgshapiro				goto rcpt_done;
249190792Sgshapiro			macdefine(&e->e_macro, A_PERM,
249290792Sgshapiro				macid("{addr_type}"), "e r");
249390792Sgshapiro			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
249490792Sgshapiro				      e, true);
249590792Sgshapiro			macdefine(&e->e_macro, A_PERM,
249690792Sgshapiro				macid("{addr_type}"), NULL);
249742575Speter			if (Errors > 0)
249890792Sgshapiro				goto rcpt_done;
249942575Speter			if (a == NULL)
250042575Speter			{
250164562Sgshapiro				usrerr("501 5.0.0 Missing recipient");
250290792Sgshapiro				goto rcpt_done;
250342575Speter			}
250442575Speter
250538032Speter			if (delimptr != NULL && *delimptr != '\0')
250638032Speter				*delimptr++ = '\0';
250738032Speter
250864562Sgshapiro			/* put resulting triple from parseaddr() into macros */
250964562Sgshapiro			if (a->q_mailer != NULL)
251090792Sgshapiro				macdefine(&e->e_macro, A_PERM,
251190792Sgshapiro					macid("{rcpt_mailer}"),
251290792Sgshapiro					a->q_mailer->m_name);
251364562Sgshapiro			else
251490792Sgshapiro				macdefine(&e->e_macro, A_PERM,
251590792Sgshapiro					macid("{rcpt_mailer}"), NULL);
251664562Sgshapiro			if (a->q_host != NULL)
251790792Sgshapiro				macdefine(&e->e_macro, A_PERM,
251890792Sgshapiro					macid("{rcpt_host}"), a->q_host);
251964562Sgshapiro			else
252090792Sgshapiro				macdefine(&e->e_macro, A_PERM,
252190792Sgshapiro					macid("{rcpt_host}"), "localhost");
252264562Sgshapiro			if (a->q_user != NULL)
252390792Sgshapiro				macdefine(&e->e_macro, A_PERM,
252490792Sgshapiro					macid("{rcpt_addr}"), a->q_user);
252564562Sgshapiro			else
252690792Sgshapiro				macdefine(&e->e_macro, A_PERM,
252790792Sgshapiro					macid("{rcpt_addr}"), NULL);
252864562Sgshapiro			if (Errors > 0)
252990792Sgshapiro				goto rcpt_done;
253038032Speter
253138032Speter			/* now parse ESMTP arguments */
253264562Sgshapiro			addr = p;
253364562Sgshapiro			argno = 0;
253464562Sgshapiro			args[argno++] = p;
253538032Speter			p = delimptr;
253638032Speter			while (p != NULL && *p != '\0')
253738032Speter			{
253838032Speter				char *kp;
253938032Speter				char *vp = NULL;
254064562Sgshapiro				char *equal = NULL;
254138032Speter
254238032Speter				/* locate the beginning of the keyword */
254390792Sgshapiro				SKIP_SPACE(p);
254438032Speter				if (*p == '\0')
254538032Speter					break;
254638032Speter				kp = p;
254738032Speter
254838032Speter				/* skip to the value portion */
254938032Speter				while ((isascii(*p) && isalnum(*p)) || *p == '-')
255038032Speter					p++;
255138032Speter				if (*p == '=')
255238032Speter				{
255364562Sgshapiro					equal = p;
255438032Speter					*p++ = '\0';
255538032Speter					vp = p;
255638032Speter
255738032Speter					/* skip to the end of the value */
255838032Speter					while (*p != '\0' && *p != ' ' &&
255938032Speter					       !(isascii(*p) && iscntrl(*p)) &&
256038032Speter					       *p != '=')
256138032Speter						p++;
256238032Speter				}
256338032Speter
256438032Speter				if (*p != '\0')
256538032Speter					*p++ = '\0';
256638032Speter
256738032Speter				if (tTd(19, 1))
256890792Sgshapiro					sm_dprintf("RCPT: got arg %s=\"%s\"\n", kp,
256938032Speter						vp == NULL ? "<null>" : vp);
257038032Speter
257138032Speter				rcpt_esmtp_args(a, kp, vp, e);
257264562Sgshapiro				if (equal != NULL)
257364562Sgshapiro					*equal = '=';
257464562Sgshapiro				args[argno++] = kp;
257564562Sgshapiro				if (argno >= MAXSMTPARGS - 1)
257664562Sgshapiro					usrerr("501 5.5.4 Too many parameters");
257738032Speter				if (Errors > 0)
257838032Speter					break;
257938032Speter			}
258064562Sgshapiro			args[argno] = NULL;
258138032Speter			if (Errors > 0)
258290792Sgshapiro				goto rcpt_done;
258338032Speter
258464562Sgshapiro			/* do config file checking of the recipient */
258590792Sgshapiro			macdefine(&e->e_macro, A_PERM,
258690792Sgshapiro				macid("{addr_type}"), "e r");
258764562Sgshapiro			if (rscheck("check_rcpt", addr,
2588102528Sgshapiro				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
2589102528Sgshapiro				    NULL, e->e_id) != EX_OK ||
259064562Sgshapiro			    Errors > 0)
259190792Sgshapiro				goto rcpt_done;
259290792Sgshapiro			macdefine(&e->e_macro, A_PERM,
259390792Sgshapiro				macid("{addr_type}"), NULL);
259464562Sgshapiro
2595102528Sgshapiro			/* If discarding, don't bother to verify user */
2596102528Sgshapiro			if (bitset(EF_DISCARD, e->e_flags))
2597102528Sgshapiro				a->q_state = QS_VERIFIED;
2598102528Sgshapiro
259990792Sgshapiro#if MILTER
260090792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
260190792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
260264562Sgshapiro			{
260364562Sgshapiro				char state;
260464562Sgshapiro				char *response;
260564562Sgshapiro
260664562Sgshapiro				response = milter_envrcpt(args, e, &state);
260790792Sgshapiro				MILTER_REPLY("to");
260864562Sgshapiro			}
260990792Sgshapiro#endif /* MILTER */
261064562Sgshapiro
261190792Sgshapiro			macdefine(&e->e_macro, A_PERM,
261290792Sgshapiro				macid("{rcpt_mailer}"), NULL);
261390792Sgshapiro			macdefine(&e->e_macro, A_PERM,
261498121Sgshapiro				macid("{rcpt_host}"), NULL);
261590792Sgshapiro			macdefine(&e->e_macro, A_PERM,
261690792Sgshapiro				macid("{rcpt_addr}"), NULL);
261790792Sgshapiro			macdefine(&e->e_macro, A_PERM,
261890792Sgshapiro				macid("{dsn_notify}"), NULL);
261964562Sgshapiro			if (Errors > 0)
262090792Sgshapiro				goto rcpt_done;
262164562Sgshapiro
262238032Speter			/* save in recipient list after ESMTP mods */
262338032Speter			a = recipient(a, &e->e_sendqueue, 0, e);
262438032Speter			if (Errors > 0)
262590792Sgshapiro				goto rcpt_done;
262638032Speter
262738032Speter			/* no errors during parsing, but might be a duplicate */
262838032Speter			e->e_to = a->q_paddr;
262964562Sgshapiro			if (!QS_IS_BADADDR(a->q_state))
263038032Speter			{
263190792Sgshapiro				if (smtp.sm_nrcpts == 0)
263264562Sgshapiro					initsys(e);
263364562Sgshapiro				message("250 2.1.5 Recipient ok%s",
263464562Sgshapiro					QS_IS_QUEUEUP(a->q_state) ?
263538032Speter						" (will queue)" : "");
263690792Sgshapiro				smtp.sm_nrcpts++;
263738032Speter			}
263838032Speter			else
263938032Speter			{
264038032Speter				/* punt -- should keep message in ADDRESS.... */
264164562Sgshapiro				usrerr("550 5.1.1 Addressee unknown");
264238032Speter			}
264390792Sgshapiro		    rcpt_done:
264490792Sgshapiro			if (Errors > 0)
2645132943Sgshapiro			{
264690792Sgshapiro				++n_badrcpts;
2647132943Sgshapiro				NBADRCPTS;
2648132943Sgshapiro			}
264990792Sgshapiro		    }
265090792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
265190792Sgshapiro		    {
265290792Sgshapiro			/* An exception occurred while processing RCPT */
265390792Sgshapiro			e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
265490792Sgshapiro			++n_badrcpts;
2655132943Sgshapiro			NBADRCPTS;
265690792Sgshapiro		    }
265790792Sgshapiro		    SM_END_TRY
265838032Speter			break;
265938032Speter
266038032Speter		  case CMDDATA:		/* data -- text of mail */
266190792Sgshapiro			DELAY_CONN("DATA");
2662132943Sgshapiro			if (!smtp_data(&smtp, e))
2663132943Sgshapiro				goto doquit;
266438032Speter			break;
266538032Speter
266638032Speter		  case CMDRSET:		/* rset -- reset state */
266738032Speter			if (tTd(94, 100))
266864562Sgshapiro				message("451 4.0.0 Test failure");
266938032Speter			else
267064562Sgshapiro				message("250 2.0.0 Reset state");
267190792Sgshapiro			CLEAR_STATE(cmdbuf);
267238032Speter			break;
267338032Speter
267438032Speter		  case CMDVRFY:		/* vrfy -- verify address */
267538032Speter		  case CMDEXPN:		/* expn -- expand address */
267690792Sgshapiro			vrfy = c->cmd_code == CMDVRFY;
267790792Sgshapiro			DELAY_CONN(vrfy ? "VRFY" : "EXPN");
267864562Sgshapiro			if (tempfail)
267964562Sgshapiro			{
268064562Sgshapiro				if (LogLevel > 9)
268164562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2682110560Sgshapiro						  "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)",
268390792Sgshapiro						  vrfy ? "VRFY" : "EXPN",
268464562Sgshapiro						  p, CurSmtpClient);
268590792Sgshapiro
268690792Sgshapiro				/* RFC 821 doesn't allow 4xy reply code */
268764562Sgshapiro				usrerr("550 5.7.1 Please try again later");
268864562Sgshapiro				break;
268964562Sgshapiro			}
269090792Sgshapiro			wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
269190792Sgshapiro					     false, vrfy ? "VRFY" : "EXPN", e);
2692132943Sgshapiro			STOP_IF_ATTACK(wt);
269364562Sgshapiro			previous = curtime();
2694110560Sgshapiro			if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) ||
2695110560Sgshapiro			    (!vrfy && !bitset(SRV_OFFER_EXPN, features)))
269638032Speter			{
269738032Speter				if (vrfy)
269864562Sgshapiro					message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
269938032Speter				else
270064562Sgshapiro					message("502 5.7.0 Sorry, we do not allow this operation");
270138032Speter				if (LogLevel > 5)
270238032Speter					sm_syslog(LOG_INFO, e->e_id,
2703110560Sgshapiro						  "%s: %s [rejected]",
270464562Sgshapiro						  CurSmtpClient,
270564562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
270638032Speter				break;
270738032Speter			}
270838032Speter			else if (!gothello &&
270938032Speter				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
271038032Speter						PrivacyFlags))
271138032Speter			{
271264562Sgshapiro				usrerr("503 5.0.0 I demand that you introduce yourself first");
271338032Speter				break;
271438032Speter			}
271590792Sgshapiro			if (Errors > 0)
271638032Speter				break;
271738032Speter			if (LogLevel > 5)
2718110560Sgshapiro				sm_syslog(LOG_INFO, e->e_id, "%s: %s",
271964562Sgshapiro					  CurSmtpClient,
272064562Sgshapiro					  shortenstring(inp, MAXSHORTSTR));
272190792Sgshapiro		    SM_TRY
272290792Sgshapiro		    {
272390792Sgshapiro			QuickAbort = true;
272438032Speter			vrfyqueue = NULL;
272538032Speter			if (vrfy)
272638032Speter				e->e_flags |= EF_VRFYONLY;
272738032Speter			while (*p != '\0' && isascii(*p) && isspace(*p))
272838032Speter				p++;
272938032Speter			if (*p == '\0')
273038032Speter			{
273164562Sgshapiro				usrerr("501 5.5.2 Argument required");
273238032Speter			}
273338032Speter			else
273438032Speter			{
273564562Sgshapiro				/* do config file checking of the address */
273664562Sgshapiro				if (rscheck(vrfy ? "check_vrfy" : "check_expn",
2737102528Sgshapiro					    p, NULL, e, RSF_RMCOMM,
2738102528Sgshapiro					    3, NULL, NOQID) != EX_OK ||
273990792Sgshapiro				    Errors > 0)
274090792Sgshapiro					sm_exc_raisenew_x(&EtypeQuickAbort, 1);
274138032Speter				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
274238032Speter			}
274364562Sgshapiro			if (wt > 0)
274471345Sgshapiro			{
274571345Sgshapiro				time_t t;
274671345Sgshapiro
274771345Sgshapiro				t = wt - (curtime() - previous);
274871345Sgshapiro				if (t > 0)
274971345Sgshapiro					(void) sleep(t);
275071345Sgshapiro			}
275138032Speter			if (Errors > 0)
275290792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
275338032Speter			if (vrfyqueue == NULL)
275438032Speter			{
275564562Sgshapiro				usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
275638032Speter			}
275738032Speter			while (vrfyqueue != NULL)
275838032Speter			{
275964562Sgshapiro				if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
276064562Sgshapiro				{
276164562Sgshapiro					vrfyqueue = vrfyqueue->q_next;
276264562Sgshapiro					continue;
276364562Sgshapiro				}
276438032Speter
276564562Sgshapiro				/* see if there is more in the vrfy list */
276638032Speter				a = vrfyqueue;
276738032Speter				while ((a = a->q_next) != NULL &&
276866494Sgshapiro				       (!QS_IS_UNDELIVERED(a->q_state)))
276938032Speter					continue;
277064562Sgshapiro				printvrfyaddr(vrfyqueue, a == NULL, vrfy);
277164562Sgshapiro				vrfyqueue = a;
277238032Speter			}
277390792Sgshapiro		    }
277490792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
277590792Sgshapiro		    {
277690792Sgshapiro			/*
277790792Sgshapiro			**  An exception occurred while processing VRFY/EXPN
277890792Sgshapiro			*/
277990792Sgshapiro
278090792Sgshapiro			sm_exc_free(exc);
278190792Sgshapiro			goto undo;
278290792Sgshapiro		    }
278390792Sgshapiro		    SM_END_TRY
278438032Speter			break;
278538032Speter
278638032Speter		  case CMDETRN:		/* etrn -- force queue flush */
278790792Sgshapiro			DELAY_CONN("ETRN");
278890792Sgshapiro
278990792Sgshapiro			/* Don't leak queue information via debug flags */
279090792Sgshapiro			if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
279190792Sgshapiro			    (RealUid != 0 && RealUid != TrustedUid &&
279290792Sgshapiro			     OpMode == MD_SMTP))
279338032Speter			{
279464562Sgshapiro				/* different message for MSA ? */
279564562Sgshapiro				message("502 5.7.0 Sorry, we do not allow this operation");
279638032Speter				if (LogLevel > 5)
279738032Speter					sm_syslog(LOG_INFO, e->e_id,
2798110560Sgshapiro						  "%s: %s [rejected]",
279964562Sgshapiro						  CurSmtpClient,
280064562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
280138032Speter				break;
280238032Speter			}
280364562Sgshapiro			if (tempfail)
280464562Sgshapiro			{
280564562Sgshapiro				if (LogLevel > 9)
280664562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
2807110560Sgshapiro						  "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)",
280864562Sgshapiro						  p, CurSmtpClient);
280990792Sgshapiro				usrerr(MSG_TEMPFAIL);
281064562Sgshapiro				break;
281164562Sgshapiro			}
281238032Speter
281338032Speter			if (strlen(p) <= 0)
281438032Speter			{
281564562Sgshapiro				usrerr("500 5.5.2 Parameter required");
281638032Speter				break;
281738032Speter			}
281838032Speter
281938032Speter			/* crude way to avoid denial-of-service attacks */
2820132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS,
2821132943Sgshapiro							true, "ETRN", e));
282238032Speter
282390792Sgshapiro			/*
282490792Sgshapiro			**  Do config file checking of the parameter.
282590792Sgshapiro			**  Even though we have srv_features now, we still
282690792Sgshapiro			**  need this ruleset because the former is called
282790792Sgshapiro			**  when the connection has been established, while
282890792Sgshapiro			**  this ruleset is called when the command is
282990792Sgshapiro			**  actually issued and therefore has all information
283090792Sgshapiro			**  available to make a decision.
283190792Sgshapiro			*/
283290792Sgshapiro
2833102528Sgshapiro			if (rscheck("check_etrn", p, NULL, e,
2834102528Sgshapiro				    RSF_RMCOMM, 3, NULL, NOQID) != EX_OK ||
2835102528Sgshapiro			    Errors > 0)
283664562Sgshapiro				break;
283764562Sgshapiro
283838032Speter			if (LogLevel > 5)
283938032Speter				sm_syslog(LOG_INFO, e->e_id,
2840110560Sgshapiro					  "%s: ETRN %s", CurSmtpClient,
284164562Sgshapiro					  shortenstring(p, MAXSHORTSTR));
284238032Speter
284338032Speter			id = p;
284490792Sgshapiro			if (*id == '#')
284590792Sgshapiro			{
2846111823Sgshapiro				int i, qgrp;
284790792Sgshapiro
284890792Sgshapiro				id++;
2849111823Sgshapiro				qgrp = name2qid(id);
2850111823Sgshapiro				if (!ISVALIDQGRP(qgrp))
285190792Sgshapiro				{
285290792Sgshapiro					usrerr("459 4.5.4 Queue %s unknown",
285390792Sgshapiro					       id);
285490792Sgshapiro					break;
285590792Sgshapiro				}
2856111823Sgshapiro				for (i = 0; i < NumQueue && Queue[i] != NULL;
2857111823Sgshapiro				     i++)
2858111823Sgshapiro					Queue[i]->qg_nextrun = (time_t) -1;
2859111823Sgshapiro				Queue[qgrp]->qg_nextrun = 0;
2860111823Sgshapiro				ok = run_work_group(Queue[qgrp]->qg_wgrp,
2861111823Sgshapiro						    RWG_FORK|RWG_FORCE);
286290792Sgshapiro				if (ok && Errors == 0)
286390792Sgshapiro					message("250 2.0.0 Queuing for queue group %s started", id);
286490792Sgshapiro				break;
286590792Sgshapiro			}
286690792Sgshapiro
286738032Speter			if (*id == '@')
286838032Speter				id++;
286938032Speter			else
287038032Speter				*--id = '@';
287164562Sgshapiro
287290792Sgshapiro			new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
287390792Sgshapiro			if (new == NULL)
287490792Sgshapiro			{
287590792Sgshapiro				syserr("500 5.5.0 ETRN out of memory");
287690792Sgshapiro				break;
287790792Sgshapiro			}
287838032Speter			new->queue_match = id;
287990792Sgshapiro			new->queue_negate = false;
288038032Speter			new->queue_next = NULL;
288138032Speter			QueueLimitRecipient = new;
288290792Sgshapiro			ok = runqueue(true, false, false, true);
288390792Sgshapiro			sm_free(QueueLimitRecipient); /* XXX */
288438032Speter			QueueLimitRecipient = NULL;
288538032Speter			if (ok && Errors == 0)
288664562Sgshapiro				message("250 2.0.0 Queuing for node %s started", p);
288738032Speter			break;
288838032Speter
288938032Speter		  case CMDHELP:		/* help -- give user info */
289090792Sgshapiro			DELAY_CONN("HELP");
289164562Sgshapiro			help(p, e);
289238032Speter			break;
289338032Speter
289438032Speter		  case CMDNOOP:		/* noop -- do nothing */
289590792Sgshapiro			DELAY_CONN("NOOP");
2896132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS,
2897132943Sgshapiro							true, "NOOP", e));
289864562Sgshapiro			message("250 2.0.0 OK");
289938032Speter			break;
290038032Speter
290138032Speter		  case CMDQUIT:		/* quit -- leave mail */
290264562Sgshapiro			message("221 2.0.0 %s closing connection", MyHostName);
290390792Sgshapiro#if PIPELINING
290490792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
290590792Sgshapiro#endif /* PIPELINING */
290638032Speter
290790792Sgshapiro			if (smtp.sm_nrcpts > 0)
290890792Sgshapiro				logundelrcpts(e, "aborted by sender", 9, false);
290990792Sgshapiro
291038032Speter			/* arrange to ignore any current send list */
291138032Speter			e->e_sendqueue = NULL;
291238032Speter
291390792Sgshapiro#if STARTTLS
291464562Sgshapiro			/* shutdown TLS connection */
291564562Sgshapiro			if (tls_active)
291664562Sgshapiro			{
291764562Sgshapiro				(void) endtls(srv_ssl, "server");
291890792Sgshapiro				tls_active = false;
291964562Sgshapiro			}
292090792Sgshapiro#endif /* STARTTLS */
292190792Sgshapiro#if SASL
292264562Sgshapiro			if (authenticating == SASL_IS_AUTH)
292364562Sgshapiro			{
292464562Sgshapiro				sasl_dispose(&conn);
292564562Sgshapiro				authenticating = SASL_NOT_AUTH;
292690792Sgshapiro				/* XXX sasl_done(); this is a child */
292764562Sgshapiro			}
292890792Sgshapiro#endif /* SASL */
292964562Sgshapiro
293064562Sgshapirodoquit:
293138032Speter			/* avoid future 050 messages */
293238032Speter			disconnect(1, e);
293338032Speter
293490792Sgshapiro#if MILTER
293564562Sgshapiro			/* close out milter filters */
293664562Sgshapiro			milter_quit(e);
293790792Sgshapiro#endif /* MILTER */
293864562Sgshapiro
293964562Sgshapiro			if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
294064562Sgshapiro				logsender(e, NULL);
294164562Sgshapiro			e->e_flags &= ~EF_LOGSENDER;
294264562Sgshapiro
294398121Sgshapiro			if (lognullconnection && LogLevel > 5 &&
294498121Sgshapiro			    nullserver == NULL)
294564562Sgshapiro			{
294664562Sgshapiro				char *d;
294764562Sgshapiro
294890792Sgshapiro				d = macvalue(macid("{daemon_name}"), e);
294964562Sgshapiro				if (d == NULL)
295064562Sgshapiro					d = "stdin";
295190792Sgshapiro
295290792Sgshapiro				/*
295390792Sgshapiro				**  even though this id is "bogus", it makes
295490792Sgshapiro				**  it simpler to "grep" related events, e.g.,
295590792Sgshapiro				**  timeouts for the same connection.
295690792Sgshapiro				*/
295790792Sgshapiro
295890792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
2959110560Sgshapiro					  "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
296064562Sgshapiro					  CurSmtpClient, d);
296164562Sgshapiro			}
2962110560Sgshapiro			if (tTd(93, 100))
2963110560Sgshapiro			{
2964110560Sgshapiro				/* return to handle next connection */
2965110560Sgshapiro				return;
2966110560Sgshapiro			}
296790792Sgshapiro			finis(true, true, ExitStat);
296864562Sgshapiro			/* NOTREACHED */
296938032Speter
297038032Speter		  case CMDVERB:		/* set verbose mode */
297190792Sgshapiro			DELAY_CONN("VERB");
2972110560Sgshapiro			if (!bitset(SRV_OFFER_EXPN, features) ||
2973110560Sgshapiro			    !bitset(SRV_OFFER_VERB, features))
297438032Speter			{
297538032Speter				/* this would give out the same info */
297664562Sgshapiro				message("502 5.7.0 Verbose unavailable");
297738032Speter				break;
297838032Speter			}
2979132943Sgshapiro			STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS,
2980132943Sgshapiro							true, "VERB", e));
298138032Speter			Verbose = 1;
298264562Sgshapiro			set_delivery_mode(SM_DELIVER, e);
298364562Sgshapiro			message("250 2.0.0 Verbose mode");
298438032Speter			break;
298538032Speter
298690792Sgshapiro#if SMTPDEBUG
298738032Speter		  case CMDDBGQSHOW:	/* show queues */
298890792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
298990792Sgshapiro					     "Send Queue=");
2990132943Sgshapiro			printaddr(smioout, e->e_sendqueue, true);
299138032Speter			break;
299238032Speter
299338032Speter		  case CMDDBGDEBUG:	/* set debug mode */
299438032Speter			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
299538032Speter			tTflag(p);
299664562Sgshapiro			message("200 2.0.0 Debug set");
299738032Speter			break;
299838032Speter
299990792Sgshapiro#else /* SMTPDEBUG */
300038032Speter		  case CMDDBGQSHOW:	/* show queues */
300138032Speter		  case CMDDBGDEBUG:	/* set debug mode */
300290792Sgshapiro#endif /* SMTPDEBUG */
300338032Speter		  case CMDLOGBOGUS:	/* bogus command */
300490792Sgshapiro			DELAY_CONN("Bogus");
300538032Speter			if (LogLevel > 0)
300638032Speter				sm_syslog(LOG_CRIT, e->e_id,
3007110560Sgshapiro					  "\"%s\" command from %s (%.100s)",
300864562Sgshapiro					  c->cmd_name, CurSmtpClient,
300964562Sgshapiro					  anynet_ntoa(&RealHostAddr));
301064562Sgshapiro			/* FALLTHROUGH */
301138032Speter
301238032Speter		  case CMDERROR:	/* unknown command */
301390792Sgshapiro#if MAXBADCOMMANDS > 0
301490792Sgshapiro			if (++n_badcmds > MAXBADCOMMANDS)
301538032Speter			{
3016132943Sgshapiro  stopattack:
301764562Sgshapiro				message("421 4.7.0 %s Too many bad commands; closing connection",
301838032Speter					MyHostName);
301964562Sgshapiro
302064562Sgshapiro				/* arrange to ignore any current send list */
302164562Sgshapiro				e->e_sendqueue = NULL;
302238032Speter				goto doquit;
302338032Speter			}
302490792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
302538032Speter
3026132943Sgshapiro#if MILTER && SMFI_VERSION > 2
3027132943Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
3028132943Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
3029132943Sgshapiro			{
3030132943Sgshapiro				char state;
3031132943Sgshapiro				char *response;
3032132943Sgshapiro
3033132943Sgshapiro				if (MilterLogLevel > 9)
3034132943Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
3035132943Sgshapiro						"Sending \"%s\" to Milter", inp);
3036132943Sgshapiro				response = milter_unknown(inp, e, &state);
3037132943Sgshapiro				MILTER_REPLY("unknown");
3038132943Sgshapiro				if (state == SMFIR_REPLYCODE ||
3039132943Sgshapiro				    state == SMFIR_REJECT ||
3040132943Sgshapiro				    state == SMFIR_TEMPFAIL)
3041132943Sgshapiro				{
3042132943Sgshapiro					/* MILTER_REPLY already gave an error */
3043132943Sgshapiro					break;
3044132943Sgshapiro				}
3045132943Sgshapiro			}
3046132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 2 */
3047132943Sgshapiro
304864562Sgshapiro			usrerr("500 5.5.1 Command unrecognized: \"%s\"",
304964562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
305038032Speter			break;
305138032Speter
305264562Sgshapiro		  case CMDUNIMPL:
305390792Sgshapiro			DELAY_CONN("Unimpl");
305464562Sgshapiro			usrerr("502 5.5.1 Command not implemented: \"%s\"",
305564562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
305664562Sgshapiro			break;
305764562Sgshapiro
305838032Speter		  default:
305990792Sgshapiro			DELAY_CONN("default");
306038032Speter			errno = 0;
306164562Sgshapiro			syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
306238032Speter			break;
306338032Speter		}
306490792Sgshapiro#if SASL
306564562Sgshapiro		}
306690792Sgshapiro#endif /* SASL */
306790792Sgshapiro	    }
306890792Sgshapiro	    SM_EXCEPT(exc, "[!F]*")
306990792Sgshapiro	    {
307090792Sgshapiro		/*
307190792Sgshapiro		**  The only possible exception is "E:mta.quickabort".
307290792Sgshapiro		**  There is nothing to do except fall through and loop.
307390792Sgshapiro		*/
307490792Sgshapiro	    }
307590792Sgshapiro	    SM_END_TRY
307638032Speter	}
307790792Sgshapiro}
307890792Sgshapiro/*
307990792Sgshapiro**  SMTP_DATA -- implement the SMTP DATA command.
308090792Sgshapiro**
308190792Sgshapiro**	Parameters:
308290792Sgshapiro**		smtp -- status of SMTP connection.
308390792Sgshapiro**		e -- envelope.
308490792Sgshapiro**
308590792Sgshapiro**	Returns:
3086132943Sgshapiro**		true iff SMTP session can continue.
308790792Sgshapiro**
308890792Sgshapiro**	Side Effects:
308990792Sgshapiro**		possibly sends message.
309090792Sgshapiro*/
309164562Sgshapiro
3092132943Sgshapirostatic bool
309390792Sgshapirosmtp_data(smtp, e)
309490792Sgshapiro	SMTP_T *smtp;
309590792Sgshapiro	ENVELOPE *e;
309690792Sgshapiro{
309790792Sgshapiro#if MILTER
309890792Sgshapiro	bool milteraccept;
309990792Sgshapiro#endif /* MILTER */
310090792Sgshapiro	bool aborting;
310190792Sgshapiro	bool doublequeue;
310290792Sgshapiro	ADDRESS *a;
310390792Sgshapiro	ENVELOPE *ee;
310490792Sgshapiro	char *id;
310598121Sgshapiro	char *oldid;
310690792Sgshapiro	char buf[32];
310790792Sgshapiro
310890792Sgshapiro	SmtpPhase = "server DATA";
310990792Sgshapiro	if (!smtp->sm_gotmail)
311090792Sgshapiro	{
311190792Sgshapiro		usrerr("503 5.0.0 Need MAIL command");
3112132943Sgshapiro		return true;
311390792Sgshapiro	}
311490792Sgshapiro	else if (smtp->sm_nrcpts <= 0)
311590792Sgshapiro	{
311690792Sgshapiro		usrerr("503 5.0.0 Need RCPT (recipient)");
3117132943Sgshapiro		return true;
311890792Sgshapiro	}
311990792Sgshapiro	(void) sm_snprintf(buf, sizeof buf, "%u", smtp->sm_nrcpts);
312090792Sgshapiro	if (rscheck("check_data", buf, NULL, e,
3121102528Sgshapiro		    RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
3122102528Sgshapiro		    e->e_id) != EX_OK)
3123132943Sgshapiro		return true;
312490792Sgshapiro
3125132943Sgshapiro#if MILTER && SMFI_VERSION > 3
3126132943Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize &&
3127132943Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
3128132943Sgshapiro	{
3129132943Sgshapiro		char state;
3130132943Sgshapiro		char *response;
3131132943Sgshapiro		int savelogusrerrs = LogUsrErrs;
3132132943Sgshapiro
3133132943Sgshapiro		response = milter_data_cmd(e, &state);
3134132943Sgshapiro		switch (state)
3135132943Sgshapiro		{
3136132943Sgshapiro		  case SMFIR_REPLYCODE:
3137132943Sgshapiro			if (MilterLogLevel > 3)
3138132943Sgshapiro			{
3139132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3140132943Sgshapiro					  "Milter: cmd=data, reject=%s",
3141132943Sgshapiro					  response);
3142132943Sgshapiro				LogUsrErrs = false;
3143132943Sgshapiro			}
3144132943Sgshapiro			usrerr(response);
3145132943Sgshapiro			if (strncmp(response, "421 ", 4) == 0)
3146132943Sgshapiro			{
3147132943Sgshapiro				e->e_sendqueue = NULL;
3148132943Sgshapiro				return false;
3149132943Sgshapiro			}
3150132943Sgshapiro			return true;
3151132943Sgshapiro
3152132943Sgshapiro		  case SMFIR_REJECT:
3153132943Sgshapiro			if (MilterLogLevel > 3)
3154132943Sgshapiro			{
3155132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3156132943Sgshapiro					  "Milter: cmd=data, reject=550 5.7.1 Command rejected");
3157132943Sgshapiro				LogUsrErrs = false;
3158132943Sgshapiro			}
3159132943Sgshapiro			usrerr("550 5.7.1 Command rejected");
3160132943Sgshapiro			return true;
3161132943Sgshapiro
3162132943Sgshapiro		  case SMFIR_DISCARD:
3163132943Sgshapiro			if (MilterLogLevel > 3)
3164132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3165132943Sgshapiro					  "Milter: cmd=data, discard");
3166132943Sgshapiro			e->e_flags |= EF_DISCARD;
3167132943Sgshapiro			break;
3168132943Sgshapiro
3169132943Sgshapiro		  case SMFIR_TEMPFAIL:
3170132943Sgshapiro			if (MilterLogLevel > 3)
3171132943Sgshapiro			{
3172132943Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
3173132943Sgshapiro					  "Milter: cmd=data, reject=%s",
3174132943Sgshapiro					  MSG_TEMPFAIL);
3175132943Sgshapiro				LogUsrErrs = false;
3176132943Sgshapiro			}
3177132943Sgshapiro			usrerr(MSG_TEMPFAIL);
3178132943Sgshapiro			return true;
3179132943Sgshapiro		}
3180132943Sgshapiro		LogUsrErrs = savelogusrerrs;
3181132943Sgshapiro		if (response != NULL)
3182132943Sgshapiro			sm_free(response); /* XXX */
3183132943Sgshapiro	}
3184132943Sgshapiro#endif /* MILTER && SMFI_VERSION > 3 */
3185132943Sgshapiro
318690792Sgshapiro	/* put back discard bit */
318790792Sgshapiro	if (smtp->sm_discard)
318890792Sgshapiro		e->e_flags |= EF_DISCARD;
318990792Sgshapiro
319090792Sgshapiro	/* check to see if we need to re-expand aliases */
319190792Sgshapiro	/* also reset QS_BADADDR on already-diagnosted addrs */
319290792Sgshapiro	doublequeue = false;
319390792Sgshapiro	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
319490792Sgshapiro	{
319590792Sgshapiro		if (QS_IS_VERIFIED(a->q_state) &&
319690792Sgshapiro		    !bitset(EF_DISCARD, e->e_flags))
319790792Sgshapiro		{
319890792Sgshapiro			/* need to re-expand aliases */
319990792Sgshapiro			doublequeue = true;
320090792Sgshapiro		}
320190792Sgshapiro		if (QS_IS_BADADDR(a->q_state))
320290792Sgshapiro		{
320390792Sgshapiro			/* make this "go away" */
320490792Sgshapiro			a->q_state = QS_DONTSEND;
320590792Sgshapiro		}
320690792Sgshapiro	}
320790792Sgshapiro
320890792Sgshapiro	/* collect the text of the message */
320990792Sgshapiro	SmtpPhase = "collect";
321090792Sgshapiro	buffer_errors();
321190792Sgshapiro
3212120256Sgshapiro	collect(InChannel, true, NULL, e, true);
321390792Sgshapiro
321490792Sgshapiro	/* redefine message size */
321590792Sgshapiro	(void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
321690792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
321790792Sgshapiro
321890792Sgshapiro#if _FFR_CHECK_EOM
321990792Sgshapiro	/* rscheck() will set Errors or EF_DISCARD if it trips */
3220102528Sgshapiro	(void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT,
3221102528Sgshapiro		       3, NULL, e->e_id);
322290792Sgshapiro#endif /* _FFR_CHECK_EOM */
322390792Sgshapiro
322490792Sgshapiro#if MILTER
322590792Sgshapiro	milteraccept = true;
322690792Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize &&
322790792Sgshapiro	    Errors <= 0 &&
322890792Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
322990792Sgshapiro	{
323090792Sgshapiro		char state;
323190792Sgshapiro		char *response;
323290792Sgshapiro
323390792Sgshapiro		response = milter_data(e, &state);
323490792Sgshapiro		switch (state)
323590792Sgshapiro		{
323690792Sgshapiro		  case SMFIR_REPLYCODE:
323790792Sgshapiro			if (MilterLogLevel > 3)
323890792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
323990792Sgshapiro					  "Milter: data, reject=%s",
324090792Sgshapiro					  response);
324190792Sgshapiro			milteraccept = false;
324290792Sgshapiro			usrerr(response);
324390792Sgshapiro			break;
324490792Sgshapiro
324590792Sgshapiro		  case SMFIR_REJECT:
324690792Sgshapiro			milteraccept = false;
324790792Sgshapiro			if (MilterLogLevel > 3)
324890792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
324990792Sgshapiro					  "Milter: data, reject=554 5.7.1 Command rejected");
325090792Sgshapiro			usrerr("554 5.7.1 Command rejected");
325190792Sgshapiro			break;
325290792Sgshapiro
325390792Sgshapiro		  case SMFIR_DISCARD:
325490792Sgshapiro			if (MilterLogLevel > 3)
325590792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
325690792Sgshapiro					  "Milter: data, discard");
325790792Sgshapiro			milteraccept = false;
325890792Sgshapiro			e->e_flags |= EF_DISCARD;
325990792Sgshapiro			break;
326090792Sgshapiro
326190792Sgshapiro		  case SMFIR_TEMPFAIL:
326290792Sgshapiro			if (MilterLogLevel > 3)
326390792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
326490792Sgshapiro					  "Milter: data, reject=%s",
326590792Sgshapiro					  MSG_TEMPFAIL);
326690792Sgshapiro			milteraccept = false;
326790792Sgshapiro			usrerr(MSG_TEMPFAIL);
326890792Sgshapiro			break;
326990792Sgshapiro		}
327090792Sgshapiro		if (response != NULL)
327190792Sgshapiro			sm_free(response);
327290792Sgshapiro	}
327390792Sgshapiro
327490792Sgshapiro	/* Milter may have changed message size */
327590792Sgshapiro	(void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
327690792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
327790792Sgshapiro
327890792Sgshapiro	/* abort message filters that didn't get the body & log msg is OK */
327990792Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize)
328090792Sgshapiro	{
328190792Sgshapiro		milter_abort(e);
328290792Sgshapiro		if (milteraccept && MilterLogLevel > 9)
328390792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
328490792Sgshapiro	}
3285132943Sgshapiro
3286132943Sgshapiro	/*
3287132943Sgshapiro	**  If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or
3288132943Sgshapiro	**  milter accepted message, sync it now
3289132943Sgshapiro	**
3290132943Sgshapiro	**  XXX This is almost a copy of the code in collect(): put it into
3291132943Sgshapiro	**	a function that is called from both places?
3292132943Sgshapiro	*/
3293132943Sgshapiro
3294132943Sgshapiro	if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER)
3295132943Sgshapiro	{
3296132943Sgshapiro		int afd;
3297132943Sgshapiro		SM_FILE_T *volatile df;
3298132943Sgshapiro		char *dfname;
3299132943Sgshapiro
3300132943Sgshapiro		df = e->e_dfp;
3301132943Sgshapiro		dfname = queuename(e, DATAFL_LETTER);
3302132943Sgshapiro		if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0
3303132943Sgshapiro		    && errno != EINVAL)
3304132943Sgshapiro		{
3305132943Sgshapiro			int save_errno;
3306132943Sgshapiro
3307132943Sgshapiro			save_errno = errno;
3308132943Sgshapiro			if (save_errno == EEXIST)
3309132943Sgshapiro			{
3310132943Sgshapiro				struct stat st;
3311132943Sgshapiro				int dfd;
3312132943Sgshapiro
3313132943Sgshapiro				if (stat(dfname, &st) < 0)
3314132943Sgshapiro					st.st_size = -1;
3315132943Sgshapiro				errno = EEXIST;
3316132943Sgshapiro				syserr("@collect: bfcommit(%s): already on disk, size=%ld",
3317132943Sgshapiro				       dfname, (long) st.st_size);
3318132943Sgshapiro				dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
3319132943Sgshapiro				if (dfd >= 0)
3320132943Sgshapiro					dumpfd(dfd, true, true);
3321132943Sgshapiro			}
3322132943Sgshapiro			errno = save_errno;
3323132943Sgshapiro			dferror(df, "bfcommit", e);
3324132943Sgshapiro			flush_errors(true);
3325132943Sgshapiro			finis(save_errno != EEXIST, true, ExitStat);
3326132943Sgshapiro		}
3327132943Sgshapiro		else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
3328132943Sgshapiro		{
3329132943Sgshapiro			dferror(df, "sm_io_getinfo", e);
3330132943Sgshapiro			flush_errors(true);
3331132943Sgshapiro			finis(true, true, ExitStat);
3332132943Sgshapiro			/* NOTREACHED */
3333132943Sgshapiro		}
3334132943Sgshapiro		else if (fsync(afd) < 0)
3335132943Sgshapiro		{
3336132943Sgshapiro			dferror(df, "fsync", e);
3337132943Sgshapiro			flush_errors(true);
3338132943Sgshapiro			finis(true, true, ExitStat);
3339132943Sgshapiro			/* NOTREACHED */
3340132943Sgshapiro		}
3341132943Sgshapiro		else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
3342132943Sgshapiro		{
3343132943Sgshapiro			dferror(df, "sm_io_close", e);
3344132943Sgshapiro			flush_errors(true);
3345132943Sgshapiro			finis(true, true, ExitStat);
3346132943Sgshapiro			/* NOTREACHED */
3347132943Sgshapiro		}
3348132943Sgshapiro
3349132943Sgshapiro		/* Now reopen the df file */
3350132943Sgshapiro		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
3351132943Sgshapiro					SM_IO_RDONLY, NULL);
3352132943Sgshapiro		if (e->e_dfp == NULL)
3353132943Sgshapiro		{
3354132943Sgshapiro			/* we haven't acked receipt yet, so just chuck this */
3355132943Sgshapiro			syserr("@Cannot reopen %s", dfname);
3356132943Sgshapiro			finis(true, true, ExitStat);
3357132943Sgshapiro			/* NOTREACHED */
3358132943Sgshapiro		}
3359132943Sgshapiro	}
336090792Sgshapiro#endif /* MILTER */
336190792Sgshapiro
336290792Sgshapiro	/* Check if quarantining stats should be updated */
336390792Sgshapiro	if (e->e_quarmsg != NULL)
336490792Sgshapiro		markstats(e, NULL, STATS_QUARANTINE);
336590792Sgshapiro
336690792Sgshapiro	/*
336790792Sgshapiro	**  If a header/body check (header checks or milter)
336890792Sgshapiro	**  set EF_DISCARD, don't queueup the message --
336990792Sgshapiro	**  that would lose the EF_DISCARD bit and deliver
337090792Sgshapiro	**  the message.
337190792Sgshapiro	*/
337290792Sgshapiro
337390792Sgshapiro	if (bitset(EF_DISCARD, e->e_flags))
337490792Sgshapiro		doublequeue = false;
337590792Sgshapiro
337690792Sgshapiro	aborting = Errors > 0;
3377125820Sgshapiro	if (!(aborting || bitset(EF_DISCARD, e->e_flags)) &&
337890792Sgshapiro	    (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
337990792Sgshapiro	    !split_by_recipient(e))
338090792Sgshapiro		aborting = bitset(EF_FATALERRS, e->e_flags);
338190792Sgshapiro
338290792Sgshapiro	if (aborting)
338390792Sgshapiro	{
338490792Sgshapiro		/* Log who the mail would have gone to */
338590792Sgshapiro		logundelrcpts(e, e->e_message, 8, false);
338690792Sgshapiro		flush_errors(true);
338790792Sgshapiro		buffer_errors();
338890792Sgshapiro		goto abortmessage;
338990792Sgshapiro	}
339090792Sgshapiro
339190792Sgshapiro	/* from now on, we have to operate silently */
339290792Sgshapiro	buffer_errors();
339390792Sgshapiro
339490792Sgshapiro#if 0
339590792Sgshapiro	/*
339690792Sgshapiro	**  Clear message, it may contain an error from the SMTP dialogue.
339790792Sgshapiro	**  This error must not show up in the queue.
339890792Sgshapiro	**	Some error message should show up, e.g., alias database
339990792Sgshapiro	**	not available, but others shouldn't, e.g., from check_rcpt.
340090792Sgshapiro	*/
340190792Sgshapiro
340290792Sgshapiro	e->e_message = NULL;
340390792Sgshapiro#endif /* 0 */
340490792Sgshapiro
340590792Sgshapiro	/*
340690792Sgshapiro	**  Arrange to send to everyone.
340790792Sgshapiro	**	If sending to multiple people, mail back
340890792Sgshapiro	**		errors rather than reporting directly.
340990792Sgshapiro	**	In any case, don't mail back errors for
341090792Sgshapiro	**		anything that has happened up to
341190792Sgshapiro	**		now (the other end will do this).
341290792Sgshapiro	**	Truncate our transcript -- the mail has gotten
341390792Sgshapiro	**		to us successfully, and if we have
341490792Sgshapiro	**		to mail this back, it will be easier
341590792Sgshapiro	**		on the reader.
341690792Sgshapiro	**	Then send to everyone.
341790792Sgshapiro	**	Finally give a reply code.  If an error has
341890792Sgshapiro	**		already been given, don't mail a
341990792Sgshapiro	**		message back.
342090792Sgshapiro	**	We goose error returns by clearing error bit.
342190792Sgshapiro	*/
342290792Sgshapiro
342390792Sgshapiro	SmtpPhase = "delivery";
342490792Sgshapiro	(void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
342590792Sgshapiro	id = e->e_id;
342690792Sgshapiro
342790792Sgshapiro#if NAMED_BIND
342890792Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
342990792Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
343090792Sgshapiro#endif /* NAMED_BIND */
343190792Sgshapiro
343290792Sgshapiro	for (ee = e; ee != NULL; ee = ee->e_sibling)
343390792Sgshapiro	{
343490792Sgshapiro		/* make sure we actually do delivery */
343590792Sgshapiro		ee->e_flags &= ~EF_CLRQUEUE;
343690792Sgshapiro
343790792Sgshapiro		/* from now on, operate silently */
343890792Sgshapiro		ee->e_errormode = EM_MAIL;
343990792Sgshapiro
344090792Sgshapiro		if (doublequeue)
344190792Sgshapiro		{
344290792Sgshapiro			/* make sure it is in the queue */
344390792Sgshapiro			queueup(ee, false, true);
344490792Sgshapiro		}
344590792Sgshapiro		else
344690792Sgshapiro		{
344790792Sgshapiro			/* send to all recipients */
344890792Sgshapiro			sendall(ee, SM_DEFAULT);
344990792Sgshapiro		}
345090792Sgshapiro		ee->e_to = NULL;
345190792Sgshapiro	}
345290792Sgshapiro
345398121Sgshapiro	/* put back id for SMTP logging in putoutmsg() */
345498121Sgshapiro	oldid = CurEnv->e_id;
345598121Sgshapiro	CurEnv->e_id = id;
345698121Sgshapiro
345790792Sgshapiro	/* issue success message */
345890792Sgshapiro	message("250 2.0.0 %s Message accepted for delivery", id);
345998121Sgshapiro	CurEnv->e_id = oldid;
346090792Sgshapiro
346190792Sgshapiro	/* if we just queued, poke it */
346290792Sgshapiro	if (doublequeue)
346390792Sgshapiro	{
346490792Sgshapiro		bool anything_to_send = false;
346590792Sgshapiro
346690792Sgshapiro		sm_getla();
346790792Sgshapiro		for (ee = e; ee != NULL; ee = ee->e_sibling)
346890792Sgshapiro		{
346990792Sgshapiro			if (WILL_BE_QUEUED(ee->e_sendmode))
347090792Sgshapiro				continue;
347190792Sgshapiro			if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
347290792Sgshapiro			{
347390792Sgshapiro				ee->e_sendmode = SM_QUEUE;
347490792Sgshapiro				continue;
347590792Sgshapiro			}
347690792Sgshapiro			else if (QueueMode != QM_QUARANTINE &&
347790792Sgshapiro				 ee->e_quarmsg != NULL)
347890792Sgshapiro			{
347990792Sgshapiro				ee->e_sendmode = SM_QUEUE;
348090792Sgshapiro				continue;
348190792Sgshapiro			}
348290792Sgshapiro			anything_to_send = true;
348390792Sgshapiro
348490792Sgshapiro			/* close all the queue files */
348590792Sgshapiro			closexscript(ee);
348690792Sgshapiro			if (ee->e_dfp != NULL)
348790792Sgshapiro			{
348890792Sgshapiro				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
348990792Sgshapiro				ee->e_dfp = NULL;
349090792Sgshapiro			}
349190792Sgshapiro			unlockqueue(ee);
349290792Sgshapiro		}
349390792Sgshapiro		if (anything_to_send)
349490792Sgshapiro		{
349590792Sgshapiro#if PIPELINING
349690792Sgshapiro			/*
349790792Sgshapiro			**  XXX if we don't do this, we get 250 twice
349890792Sgshapiro			**	because it is also flushed in the child.
349990792Sgshapiro			*/
350090792Sgshapiro
350190792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
350290792Sgshapiro#endif /* PIPELINING */
350390792Sgshapiro			(void) doworklist(e, true, true);
350490792Sgshapiro		}
350590792Sgshapiro	}
350690792Sgshapiro
350790792Sgshapiro  abortmessage:
350890792Sgshapiro	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
350990792Sgshapiro		logsender(e, NULL);
351090792Sgshapiro	e->e_flags &= ~EF_LOGSENDER;
351190792Sgshapiro
351290792Sgshapiro	/* clean up a bit */
351390792Sgshapiro	smtp->sm_gotmail = false;
351490792Sgshapiro
351590792Sgshapiro	/*
351690792Sgshapiro	**  Call dropenvelope if and only if the envelope is *not*
351790792Sgshapiro	**  being processed by the child process forked by doworklist().
351890792Sgshapiro	*/
351990792Sgshapiro
352090792Sgshapiro	if (aborting || bitset(EF_DISCARD, e->e_flags))
352190792Sgshapiro		dropenvelope(e, true, false);
352290792Sgshapiro	else
352390792Sgshapiro	{
352490792Sgshapiro		for (ee = e; ee != NULL; ee = ee->e_sibling)
352590792Sgshapiro		{
352690792Sgshapiro			if (!doublequeue &&
352790792Sgshapiro			    QueueMode != QM_QUARANTINE &&
352890792Sgshapiro			    ee->e_quarmsg != NULL)
352990792Sgshapiro			{
353090792Sgshapiro				dropenvelope(ee, true, false);
353190792Sgshapiro				continue;
353290792Sgshapiro			}
353390792Sgshapiro			if (WILL_BE_QUEUED(ee->e_sendmode))
353490792Sgshapiro				dropenvelope(ee, true, false);
353590792Sgshapiro		}
353690792Sgshapiro	}
353790792Sgshapiro	sm_rpool_free(e->e_rpool);
353890792Sgshapiro
353990792Sgshapiro	/*
354090792Sgshapiro	**  At this point, e == &MainEnvelope, but if we did splitting,
354190792Sgshapiro	**  then CurEnv may point to an envelope structure that was just
354290792Sgshapiro	**  freed with the rpool.  So reset CurEnv *before* calling
354390792Sgshapiro	**  newenvelope.
354490792Sgshapiro	*/
354590792Sgshapiro
354690792Sgshapiro	CurEnv = e;
354790792Sgshapiro	newenvelope(e, e, sm_rpool_new_x(NULL));
354890792Sgshapiro	e->e_flags = BlankEnvelope.e_flags;
354990792Sgshapiro
355090792Sgshapiro	/* restore connection quarantining */
355190792Sgshapiro	if (smtp->sm_quarmsg == NULL)
355290792Sgshapiro	{
355390792Sgshapiro		e->e_quarmsg = NULL;
355490792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
355590792Sgshapiro	}
355690792Sgshapiro	else
355790792Sgshapiro	{
355890792Sgshapiro		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
355990792Sgshapiro		macdefine(&e->e_macro, A_PERM,
356090792Sgshapiro			  macid("{quarantine}"), e->e_quarmsg);
356190792Sgshapiro	}
3562132943Sgshapiro	return true;
356338032Speter}
356490792Sgshapiro/*
356590792Sgshapiro**  LOGUNDELRCPTS -- log undelivered (or all) recipients.
356690792Sgshapiro**
356790792Sgshapiro**	Parameters:
356890792Sgshapiro**		e -- envelope.
356990792Sgshapiro**		msg -- message for Stat=
357090792Sgshapiro**		level -- log level.
357190792Sgshapiro**		all -- log all recipients.
357290792Sgshapiro**
357390792Sgshapiro**	Returns:
357490792Sgshapiro**		none.
357590792Sgshapiro**
357690792Sgshapiro**	Side Effects:
357790792Sgshapiro**		logs undelivered (or all) recipients
357890792Sgshapiro*/
357990792Sgshapiro
358090792Sgshapirovoid
358190792Sgshapirologundelrcpts(e, msg, level, all)
358290792Sgshapiro	ENVELOPE *e;
358390792Sgshapiro	char *msg;
358490792Sgshapiro	int level;
358590792Sgshapiro	bool all;
358690792Sgshapiro{
358790792Sgshapiro	ADDRESS *a;
358890792Sgshapiro
358990792Sgshapiro	if (LogLevel <= level || msg == NULL || *msg == '\0')
359090792Sgshapiro		return;
359190792Sgshapiro
359290792Sgshapiro	/* Clear $h so relay= doesn't get mislogged by logdelivery() */
359390792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', NULL);
359490792Sgshapiro
359590792Sgshapiro	/* Log who the mail would have gone to */
359690792Sgshapiro	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
359790792Sgshapiro	{
359890792Sgshapiro		if (!QS_IS_UNDELIVERED(a->q_state) && !all)
359990792Sgshapiro			continue;
360090792Sgshapiro		e->e_to = a->q_paddr;
360190792Sgshapiro		logdelivery(NULL, NULL, a->q_status, msg, NULL,
360290792Sgshapiro			    (time_t) 0, e);
360390792Sgshapiro	}
360490792Sgshapiro	e->e_to = NULL;
360590792Sgshapiro}
360690792Sgshapiro/*
360738032Speter**  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
360838032Speter**
360938032Speter**	Parameters:
361038032Speter**		pcounter -- pointer to a counter for this command.
361138032Speter**		maxcount -- maximum value for this counter before we
361238032Speter**			slow down.
361364562Sgshapiro**		waitnow -- sleep now (in this routine)?
361438032Speter**		cname -- command name for logging.
361538032Speter**		e -- the current envelope.
361638032Speter**
361738032Speter**	Returns:
3618132943Sgshapiro**		time to wait,
3619132943Sgshapiro**		STOP_ATTACK if twice as many commands as allowed and
3620132943Sgshapiro**			MaxChildren > 0.
362138032Speter**
362238032Speter**	Side Effects:
362338032Speter**		Slows down if we seem to be under attack.
362438032Speter*/
362538032Speter
362664562Sgshapirostatic time_t
362764562Sgshapirochecksmtpattack(pcounter, maxcount, waitnow, cname, e)
362890792Sgshapiro	volatile unsigned int *pcounter;
3629132943Sgshapiro	unsigned int maxcount;
363064562Sgshapiro	bool waitnow;
363138032Speter	char *cname;
363238032Speter	ENVELOPE *e;
363338032Speter{
363490792Sgshapiro	if (maxcount <= 0)	/* no limit */
363590792Sgshapiro		return (time_t) 0;
363690792Sgshapiro
363738032Speter	if (++(*pcounter) >= maxcount)
363838032Speter	{
3639132943Sgshapiro		unsigned int shift;
364064562Sgshapiro		time_t s;
364164562Sgshapiro
364238032Speter		if (*pcounter == maxcount && LogLevel > 5)
364338032Speter		{
364438032Speter			sm_syslog(LOG_INFO, e->e_id,
3645110560Sgshapiro				  "%s: possible SMTP attack: command=%.40s, count=%u",
364677349Sgshapiro				  CurSmtpClient, cname, *pcounter);
364738032Speter		}
3648132943Sgshapiro		shift = *pcounter - maxcount;
3649132943Sgshapiro		s = 1 << shift;
3650132943Sgshapiro		if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0)
365164562Sgshapiro			s = MAXTIMEOUT;
365290792Sgshapiro
3653132943Sgshapiro#define IS_ATTACK(s)	((MaxChildren > 0 && *pcounter >= maxcount * 2)	\
3654132943Sgshapiro				? STOP_ATTACK : (time_t) s)
3655132943Sgshapiro
365664562Sgshapiro		/* sleep at least 1 second before returning */
365764562Sgshapiro		(void) sleep(*pcounter / maxcount);
365864562Sgshapiro		s -= *pcounter / maxcount;
3659132943Sgshapiro		if (s >= MAXTIMEOUT || s < 0)
3660132943Sgshapiro			s = MAXTIMEOUT;
3661132943Sgshapiro		if (waitnow && s > 0)
366264562Sgshapiro		{
366364562Sgshapiro			(void) sleep(s);
3664132943Sgshapiro			return IS_ATTACK(0);
366564562Sgshapiro		}
3666132943Sgshapiro		return IS_ATTACK(s);
366738032Speter	}
366890792Sgshapiro	return (time_t) 0;
366938032Speter}
367090792Sgshapiro/*
367190792Sgshapiro**  SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
367290792Sgshapiro**
367390792Sgshapiro**	Parameters:
367490792Sgshapiro**		none.
367590792Sgshapiro**
367690792Sgshapiro**	Returns:
367790792Sgshapiro**		nothing.
367890792Sgshapiro**
367990792Sgshapiro**	Side Effects:
368090792Sgshapiro**		may change I/O fd.
368190792Sgshapiro*/
368290792Sgshapiro
368390792Sgshapirostatic void
368490792Sgshapirosetup_smtpd_io()
368590792Sgshapiro{
368690792Sgshapiro	int inchfd, outchfd, outfd;
368790792Sgshapiro
368890792Sgshapiro	inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
368990792Sgshapiro	outchfd  = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
369090792Sgshapiro	outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
369190792Sgshapiro	if (outchfd != outfd)
369290792Sgshapiro	{
369390792Sgshapiro		/* arrange for debugging output to go to remote host */
369490792Sgshapiro		(void) dup2(outchfd, outfd);
369590792Sgshapiro	}
369690792Sgshapiro
369790792Sgshapiro	/*
369890792Sgshapiro	**  if InChannel and OutChannel are stdin/stdout
369990792Sgshapiro	**  and connected to ttys
370090792Sgshapiro	**  and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
370190792Sgshapiro	**  then "chain" them together.
370290792Sgshapiro	*/
370390792Sgshapiro
370490792Sgshapiro	if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
370590792Sgshapiro	    isatty(inchfd) && isatty(outchfd))
370690792Sgshapiro	{
370790792Sgshapiro		int inmode, outmode;
370890792Sgshapiro
370990792Sgshapiro		inmode = fcntl(inchfd, F_GETFL, 0);
371090792Sgshapiro		if (inmode == -1)
371190792Sgshapiro		{
371290792Sgshapiro			if (LogLevel > 11)
371390792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
371490792Sgshapiro					"fcntl(inchfd, F_GETFL) failed: %s",
371590792Sgshapiro					sm_errstring(errno));
371690792Sgshapiro			return;
371790792Sgshapiro		}
371890792Sgshapiro		outmode = fcntl(outchfd, F_GETFL, 0);
371990792Sgshapiro		if (outmode == -1)
372090792Sgshapiro		{
372190792Sgshapiro			if (LogLevel > 11)
372290792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
372390792Sgshapiro					"fcntl(outchfd, F_GETFL) failed: %s",
372490792Sgshapiro					sm_errstring(errno));
372590792Sgshapiro			return;
372690792Sgshapiro		}
372790792Sgshapiro		if (bitset(O_NONBLOCK, inmode) ||
372890792Sgshapiro		    bitset(O_NONBLOCK, outmode) ||
372990792Sgshapiro		    fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
373090792Sgshapiro			return;
373190792Sgshapiro		outmode = fcntl(outchfd, F_GETFL, 0);
373290792Sgshapiro		if (outmode != -1 && bitset(O_NONBLOCK, outmode))
373390792Sgshapiro		{
373490792Sgshapiro			/* changing InChannel also changes OutChannel */
373590792Sgshapiro			sm_io_automode(OutChannel, InChannel);
373690792Sgshapiro			if (tTd(97, 4) && LogLevel > 9)
373790792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
373890792Sgshapiro					  "set automode for I (%d)/O (%d) in SMTP server",
373990792Sgshapiro					  inchfd, outchfd);
374090792Sgshapiro		}
374190792Sgshapiro
374290792Sgshapiro		/* undo change of inchfd */
374390792Sgshapiro		(void) fcntl(inchfd, F_SETFL, inmode);
374490792Sgshapiro	}
374590792Sgshapiro}
374690792Sgshapiro/*
374738032Speter**  SKIPWORD -- skip a fixed word.
374838032Speter**
374938032Speter**	Parameters:
375038032Speter**		p -- place to start looking.
375138032Speter**		w -- word to skip.
375238032Speter**
375338032Speter**	Returns:
375438032Speter**		p following w.
375538032Speter**		NULL on error.
375638032Speter**
375738032Speter**	Side Effects:
375838032Speter**		clobbers the p data area.
375938032Speter*/
376038032Speter
376138032Speterstatic char *
376238032Speterskipword(p, w)
376338032Speter	register char *volatile p;
376438032Speter	char *w;
376538032Speter{
376638032Speter	register char *q;
376738032Speter	char *firstp = p;
376838032Speter
376938032Speter	/* find beginning of word */
377090792Sgshapiro	SKIP_SPACE(p);
377138032Speter	q = p;
377238032Speter
377338032Speter	/* find end of word */
377438032Speter	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
377538032Speter		p++;
377638032Speter	while (isascii(*p) && isspace(*p))
377738032Speter		*p++ = '\0';
377838032Speter	if (*p != ':')
377938032Speter	{
378038032Speter	  syntax:
378164562Sgshapiro		usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
378238032Speter			shortenstring(firstp, MAXSHORTSTR));
378364562Sgshapiro		return NULL;
378438032Speter	}
378538032Speter	*p++ = '\0';
378690792Sgshapiro	SKIP_SPACE(p);
378738032Speter
378838032Speter	if (*p == '\0')
378938032Speter		goto syntax;
379038032Speter
379138032Speter	/* see if the input word matches desired word */
379290792Sgshapiro	if (sm_strcasecmp(q, w))
379338032Speter		goto syntax;
379438032Speter
379564562Sgshapiro	return p;
379638032Speter}
379790792Sgshapiro/*
379838032Speter**  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
379938032Speter**
380038032Speter**	Parameters:
380138032Speter**		kp -- the parameter key.
380238032Speter**		vp -- the value of that parameter.
380338032Speter**		e -- the envelope.
380438032Speter**
380538032Speter**	Returns:
380638032Speter**		none.
380738032Speter*/
380838032Speter
380964562Sgshapirostatic void
381038032Spetermail_esmtp_args(kp, vp, e)
381138032Speter	char *kp;
381238032Speter	char *vp;
381338032Speter	ENVELOPE *e;
381438032Speter{
381590792Sgshapiro	if (sm_strcasecmp(kp, "size") == 0)
381638032Speter	{
381738032Speter		if (vp == NULL)
381838032Speter		{
381964562Sgshapiro			usrerr("501 5.5.2 SIZE requires a value");
382038032Speter			/* NOTREACHED */
382138032Speter		}
382290792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
382390792Sgshapiro		errno = 0;
382471345Sgshapiro		e->e_msgsize = strtol(vp, (char **) NULL, 10);
382566494Sgshapiro		if (e->e_msgsize == LONG_MAX && errno == ERANGE)
382666494Sgshapiro		{
382766494Sgshapiro			usrerr("552 5.2.3 Message size exceeds maximum value");
382866494Sgshapiro			/* NOTREACHED */
382966494Sgshapiro		}
383090792Sgshapiro		if (e->e_msgsize < 0)
383190792Sgshapiro		{
383290792Sgshapiro			usrerr("552 5.2.3 Message size invalid");
383390792Sgshapiro			/* NOTREACHED */
383490792Sgshapiro		}
383538032Speter	}
383690792Sgshapiro	else if (sm_strcasecmp(kp, "body") == 0)
383738032Speter	{
383838032Speter		if (vp == NULL)
383938032Speter		{
384064562Sgshapiro			usrerr("501 5.5.2 BODY requires a value");
384138032Speter			/* NOTREACHED */
384238032Speter		}
384390792Sgshapiro		else if (sm_strcasecmp(vp, "8bitmime") == 0)
384438032Speter		{
384590792Sgshapiro			SevenBitInput = false;
384638032Speter		}
384790792Sgshapiro		else if (sm_strcasecmp(vp, "7bit") == 0)
384838032Speter		{
384990792Sgshapiro			SevenBitInput = true;
385038032Speter		}
385138032Speter		else
385238032Speter		{
385390792Sgshapiro			usrerr("501 5.5.4 Unknown BODY type %s", vp);
385438032Speter			/* NOTREACHED */
385538032Speter		}
385690792Sgshapiro		e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
385738032Speter	}
385890792Sgshapiro	else if (sm_strcasecmp(kp, "envid") == 0)
385938032Speter	{
386064562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
386164562Sgshapiro		{
386264562Sgshapiro			usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
386364562Sgshapiro			/* NOTREACHED */
386464562Sgshapiro		}
386538032Speter		if (vp == NULL)
386638032Speter		{
386764562Sgshapiro			usrerr("501 5.5.2 ENVID requires a value");
386838032Speter			/* NOTREACHED */
386938032Speter		}
387038032Speter		if (!xtextok(vp))
387138032Speter		{
387264562Sgshapiro			usrerr("501 5.5.4 Syntax error in ENVID parameter value");
387338032Speter			/* NOTREACHED */
387438032Speter		}
387538032Speter		if (e->e_envid != NULL)
387638032Speter		{
387764562Sgshapiro			usrerr("501 5.5.0 Duplicate ENVID parameter");
387838032Speter			/* NOTREACHED */
387938032Speter		}
388090792Sgshapiro		e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
388190792Sgshapiro		macdefine(&e->e_macro, A_PERM,
388290792Sgshapiro			macid("{dsn_envid}"), e->e_envid);
388338032Speter	}
388490792Sgshapiro	else if (sm_strcasecmp(kp, "ret") == 0)
388538032Speter	{
388664562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
388764562Sgshapiro		{
388864562Sgshapiro			usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
388964562Sgshapiro			/* NOTREACHED */
389064562Sgshapiro		}
389138032Speter		if (vp == NULL)
389238032Speter		{
389364562Sgshapiro			usrerr("501 5.5.2 RET requires a value");
389438032Speter			/* NOTREACHED */
389538032Speter		}
389638032Speter		if (bitset(EF_RET_PARAM, e->e_flags))
389738032Speter		{
389864562Sgshapiro			usrerr("501 5.5.0 Duplicate RET parameter");
389938032Speter			/* NOTREACHED */
390038032Speter		}
390138032Speter		e->e_flags |= EF_RET_PARAM;
390290792Sgshapiro		if (sm_strcasecmp(vp, "hdrs") == 0)
390338032Speter			e->e_flags |= EF_NO_BODY_RETN;
390490792Sgshapiro		else if (sm_strcasecmp(vp, "full") != 0)
390538032Speter		{
390664562Sgshapiro			usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
390738032Speter			/* NOTREACHED */
390838032Speter		}
390990792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
391038032Speter	}
391190792Sgshapiro#if SASL
391290792Sgshapiro	else if (sm_strcasecmp(kp, "auth") == 0)
391364562Sgshapiro	{
391464562Sgshapiro		int len;
391564562Sgshapiro		char *q;
391664562Sgshapiro		char *auth_param;	/* the value of the AUTH=x */
391764562Sgshapiro		bool saveQuickAbort = QuickAbort;
391864562Sgshapiro		bool saveSuprErrs = SuprErrs;
391990792Sgshapiro		bool saveExitStat = ExitStat;
392064562Sgshapiro
392164562Sgshapiro		if (vp == NULL)
392264562Sgshapiro		{
392364562Sgshapiro			usrerr("501 5.5.2 AUTH= requires a value");
392464562Sgshapiro			/* NOTREACHED */
392564562Sgshapiro		}
392664562Sgshapiro		if (e->e_auth_param != NULL)
392764562Sgshapiro		{
392864562Sgshapiro			usrerr("501 5.5.0 Duplicate AUTH parameter");
392964562Sgshapiro			/* NOTREACHED */
393064562Sgshapiro		}
393164562Sgshapiro		if ((q = strchr(vp, ' ')) != NULL)
393264562Sgshapiro			len = q - vp + 1;
393364562Sgshapiro		else
393464562Sgshapiro			len = strlen(vp) + 1;
393564562Sgshapiro		auth_param = xalloc(len);
393690792Sgshapiro		(void) sm_strlcpy(auth_param, vp, len);
393764562Sgshapiro		if (!xtextok(auth_param))
393864562Sgshapiro		{
393964562Sgshapiro			usrerr("501 5.5.4 Syntax error in AUTH parameter value");
394064562Sgshapiro			/* just a warning? */
394164562Sgshapiro			/* NOTREACHED */
394264562Sgshapiro		}
394364562Sgshapiro
394464562Sgshapiro		/* XXX define this always or only if trusted? */
3945132943Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"),
3946132943Sgshapiro			  auth_param);
394764562Sgshapiro
394864562Sgshapiro		/*
394964562Sgshapiro		**  call Strust_auth to find out whether
395064562Sgshapiro		**  auth_param is acceptable (trusted)
395164562Sgshapiro		**  we shouldn't trust it if not authenticated
395264562Sgshapiro		**  (required by RFC, leave it to ruleset?)
395364562Sgshapiro		*/
395464562Sgshapiro
395590792Sgshapiro		SuprErrs = true;
395690792Sgshapiro		QuickAbort = false;
395764562Sgshapiro		if (strcmp(auth_param, "<>") != 0 &&
3958132943Sgshapiro		     (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM,
3959102528Sgshapiro			      9, NULL, NOQID) != EX_OK || Errors > 0))
396064562Sgshapiro		{
396164562Sgshapiro			if (tTd(95, 8))
396264562Sgshapiro			{
396364562Sgshapiro				q = e->e_auth_param;
396490792Sgshapiro				sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
3965132943Sgshapiro					auth_param, (q == NULL) ? "" : q);
396664562Sgshapiro			}
396790792Sgshapiro
396864562Sgshapiro			/* not trusted */
396990792Sgshapiro			e->e_auth_param = "<>";
397090792Sgshapiro# if _FFR_AUTH_PASSING
397190792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
397290792Sgshapiro				  macid("{auth_author}"), NULL);
397390792Sgshapiro# endif /* _FFR_AUTH_PASSING */
397464562Sgshapiro		}
397564562Sgshapiro		else
397664562Sgshapiro		{
397764562Sgshapiro			if (tTd(95, 8))
3978132943Sgshapiro				sm_dprintf("auth=\"%.100s\" trusted\n", auth_param);
397990792Sgshapiro			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
398090792Sgshapiro							    auth_param);
398164562Sgshapiro		}
398290792Sgshapiro		sm_free(auth_param); /* XXX */
398377349Sgshapiro
398464562Sgshapiro		/* reset values */
398564562Sgshapiro		Errors = 0;
398664562Sgshapiro		QuickAbort = saveQuickAbort;
398764562Sgshapiro		SuprErrs = saveSuprErrs;
398890792Sgshapiro		ExitStat = saveExitStat;
398964562Sgshapiro	}
399090792Sgshapiro#endif /* SASL */
399190792Sgshapiro#define PRTCHAR(c)	((isascii(c) && isprint(c)) ? (c) : '?')
399290792Sgshapiro
399390792Sgshapiro	/*
399490792Sgshapiro	**  "by" is only accepted if DeliverByMin >= 0.
399590792Sgshapiro	**  We maybe could add this to the list of server_features.
399690792Sgshapiro	*/
399790792Sgshapiro
399890792Sgshapiro	else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
399990792Sgshapiro	{
400090792Sgshapiro		char *s;
400190792Sgshapiro
400290792Sgshapiro		if (vp == NULL)
400390792Sgshapiro		{
400490792Sgshapiro			usrerr("501 5.5.2 BY= requires a value");
400590792Sgshapiro			/* NOTREACHED */
400690792Sgshapiro		}
400790792Sgshapiro		errno = 0;
400890792Sgshapiro		e->e_deliver_by = strtol(vp, &s, 10);
400990792Sgshapiro		if (e->e_deliver_by == LONG_MIN ||
401090792Sgshapiro		    e->e_deliver_by == LONG_MAX ||
401190792Sgshapiro		    e->e_deliver_by > 999999999l ||
401290792Sgshapiro		    e->e_deliver_by < -999999999l)
401390792Sgshapiro		{
401490792Sgshapiro			usrerr("501 5.5.2 BY=%s out of range", vp);
401590792Sgshapiro			/* NOTREACHED */
401690792Sgshapiro		}
401790792Sgshapiro		if (s == NULL || *s != ';')
401890792Sgshapiro		{
401990792Sgshapiro			usrerr("501 5.5.2 BY= missing ';'");
402090792Sgshapiro			/* NOTREACHED */
402190792Sgshapiro		}
402290792Sgshapiro		e->e_dlvr_flag = 0;
402390792Sgshapiro		++s;	/* XXX: spaces allowed? */
402490792Sgshapiro		SKIP_SPACE(s);
402590792Sgshapiro		switch (tolower(*s))
402690792Sgshapiro		{
402790792Sgshapiro		  case 'n':
402890792Sgshapiro			e->e_dlvr_flag = DLVR_NOTIFY;
402990792Sgshapiro			break;
403090792Sgshapiro		  case 'r':
403190792Sgshapiro			e->e_dlvr_flag = DLVR_RETURN;
403290792Sgshapiro			if (e->e_deliver_by <= 0)
403390792Sgshapiro			{
403490792Sgshapiro				usrerr("501 5.5.4 mode R requires BY time > 0");
403590792Sgshapiro				/* NOTREACHED */
403690792Sgshapiro			}
403790792Sgshapiro			if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
403890792Sgshapiro			    e->e_deliver_by < DeliverByMin)
403990792Sgshapiro			{
404090792Sgshapiro				usrerr("555 5.5.2 time %ld less than %ld",
404190792Sgshapiro					e->e_deliver_by, (long) DeliverByMin);
404290792Sgshapiro				/* NOTREACHED */
404390792Sgshapiro			}
404490792Sgshapiro			break;
404590792Sgshapiro		  default:
404690792Sgshapiro			usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
404790792Sgshapiro			/* NOTREACHED */
404890792Sgshapiro		}
404990792Sgshapiro		++s;	/* XXX: spaces allowed? */
405090792Sgshapiro		SKIP_SPACE(s);
405190792Sgshapiro		switch (tolower(*s))
405290792Sgshapiro		{
405390792Sgshapiro		  case 't':
405490792Sgshapiro			e->e_dlvr_flag |= DLVR_TRACE;
405590792Sgshapiro			break;
405690792Sgshapiro		  case '\0':
405790792Sgshapiro			break;
405890792Sgshapiro		  default:
405990792Sgshapiro			usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
406090792Sgshapiro			/* NOTREACHED */
406190792Sgshapiro		}
406290792Sgshapiro
406390792Sgshapiro		/* XXX: check whether more characters follow? */
406490792Sgshapiro	}
406538032Speter	else
406638032Speter	{
406766494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
406838032Speter		/* NOTREACHED */
406938032Speter	}
407038032Speter}
407190792Sgshapiro/*
407238032Speter**  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
407338032Speter**
407438032Speter**	Parameters:
407538032Speter**		a -- the address corresponding to the To: parameter.
407638032Speter**		kp -- the parameter key.
407738032Speter**		vp -- the value of that parameter.
407838032Speter**		e -- the envelope.
407938032Speter**
408038032Speter**	Returns:
408138032Speter**		none.
408238032Speter*/
408338032Speter
408464562Sgshapirostatic void
408538032Speterrcpt_esmtp_args(a, kp, vp, e)
408638032Speter	ADDRESS *a;
408738032Speter	char *kp;
408838032Speter	char *vp;
408938032Speter	ENVELOPE *e;
409038032Speter{
409190792Sgshapiro	if (sm_strcasecmp(kp, "notify") == 0)
409238032Speter	{
409338032Speter		char *p;
409438032Speter
409564562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
409664562Sgshapiro		{
409764562Sgshapiro			usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
409864562Sgshapiro			/* NOTREACHED */
409964562Sgshapiro		}
410038032Speter		if (vp == NULL)
410138032Speter		{
410264562Sgshapiro			usrerr("501 5.5.2 NOTIFY requires a value");
410338032Speter			/* NOTREACHED */
410438032Speter		}
410538032Speter		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
410638032Speter		a->q_flags |= QHASNOTIFY;
410790792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
410864562Sgshapiro
410990792Sgshapiro		if (sm_strcasecmp(vp, "never") == 0)
411038032Speter			return;
411138032Speter		for (p = vp; p != NULL; vp = p)
411238032Speter		{
411338032Speter			p = strchr(p, ',');
411438032Speter			if (p != NULL)
411538032Speter				*p++ = '\0';
411690792Sgshapiro			if (sm_strcasecmp(vp, "success") == 0)
411738032Speter				a->q_flags |= QPINGONSUCCESS;
411890792Sgshapiro			else if (sm_strcasecmp(vp, "failure") == 0)
411938032Speter				a->q_flags |= QPINGONFAILURE;
412090792Sgshapiro			else if (sm_strcasecmp(vp, "delay") == 0)
412138032Speter				a->q_flags |= QPINGONDELAY;
412238032Speter			else
412338032Speter			{
412464562Sgshapiro				usrerr("501 5.5.4 Bad argument \"%s\"  to NOTIFY",
412538032Speter					vp);
412638032Speter				/* NOTREACHED */
412738032Speter			}
412838032Speter		}
412938032Speter	}
413090792Sgshapiro	else if (sm_strcasecmp(kp, "orcpt") == 0)
413138032Speter	{
413264562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
413364562Sgshapiro		{
413464562Sgshapiro			usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
413564562Sgshapiro			/* NOTREACHED */
413664562Sgshapiro		}
413738032Speter		if (vp == NULL)
413838032Speter		{
413964562Sgshapiro			usrerr("501 5.5.2 ORCPT requires a value");
414038032Speter			/* NOTREACHED */
414138032Speter		}
414238032Speter		if (strchr(vp, ';') == NULL || !xtextok(vp))
414338032Speter		{
414464562Sgshapiro			usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
414538032Speter			/* NOTREACHED */
414638032Speter		}
414738032Speter		if (a->q_orcpt != NULL)
414838032Speter		{
414964562Sgshapiro			usrerr("501 5.5.0 Duplicate ORCPT parameter");
415038032Speter			/* NOTREACHED */
415138032Speter		}
415290792Sgshapiro		a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
415338032Speter	}
415438032Speter	else
415538032Speter	{
415666494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
415738032Speter		/* NOTREACHED */
415838032Speter	}
415938032Speter}
416090792Sgshapiro/*
416138032Speter**  PRINTVRFYADDR -- print an entry in the verify queue
416238032Speter**
416338032Speter**	Parameters:
416490792Sgshapiro**		a -- the address to print.
416538032Speter**		last -- set if this is the last one.
416638032Speter**		vrfy -- set if this is a VRFY command.
416738032Speter**
416838032Speter**	Returns:
416938032Speter**		none.
417038032Speter**
417138032Speter**	Side Effects:
417238032Speter**		Prints the appropriate 250 codes.
417338032Speter*/
417464562Sgshapiro#define OFFF	(3 + 1 + 5 + 1)	/* offset in fmt: SMTP reply + enh. code */
417538032Speter
417664562Sgshapirostatic void
417738032Speterprintvrfyaddr(a, last, vrfy)
417838032Speter	register ADDRESS *a;
417938032Speter	bool last;
418038032Speter	bool vrfy;
418138032Speter{
418264562Sgshapiro	char fmtbuf[30];
418338032Speter
418438032Speter	if (vrfy && a->q_mailer != NULL &&
418538032Speter	    !bitnset(M_VRFY250, a->q_mailer->m_flags))
418690792Sgshapiro		(void) sm_strlcpy(fmtbuf, "252", sizeof fmtbuf);
418738032Speter	else
418890792Sgshapiro		(void) sm_strlcpy(fmtbuf, "250", sizeof fmtbuf);
418938032Speter	fmtbuf[3] = last ? ' ' : '-';
419090792Sgshapiro	(void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4);
419138032Speter	if (a->q_fullname == NULL)
419238032Speter	{
419364562Sgshapiro		if ((a->q_mailer == NULL ||
419464562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
419590792Sgshapiro		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
419664562Sgshapiro		    strchr(a->q_user, '@') == NULL)
419790792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
419864562Sgshapiro				       sizeof fmtbuf - OFFF);
419938032Speter		else
420090792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
420164562Sgshapiro				       sizeof fmtbuf - OFFF);
420238032Speter		message(fmtbuf, a->q_user, MyHostName);
420338032Speter	}
420438032Speter	else
420538032Speter	{
420664562Sgshapiro		if ((a->q_mailer == NULL ||
420764562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
420890792Sgshapiro		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
420964562Sgshapiro		    strchr(a->q_user, '@') == NULL)
421090792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
421164562Sgshapiro				       sizeof fmtbuf - OFFF);
421238032Speter		else
421390792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
421464562Sgshapiro				       sizeof fmtbuf - OFFF);
421538032Speter		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
421638032Speter	}
421738032Speter}
421838032Speter
421990792Sgshapiro#if SASL
422090792Sgshapiro/*
422164562Sgshapiro**  SASLMECHS -- get list of possible AUTH mechanisms
422264562Sgshapiro**
422364562Sgshapiro**	Parameters:
422490792Sgshapiro**		conn -- SASL connection info.
422590792Sgshapiro**		mechlist -- output parameter for list of mechanisms.
422664562Sgshapiro**
422764562Sgshapiro**	Returns:
422890792Sgshapiro**		number of mechs.
422964562Sgshapiro*/
423064562Sgshapiro
423164562Sgshapirostatic int
423264562Sgshapirosaslmechs(conn, mechlist)
423364562Sgshapiro	sasl_conn_t *conn;
423464562Sgshapiro	char **mechlist;
423564562Sgshapiro{
423664562Sgshapiro	int len, num, result;
423764562Sgshapiro
423864562Sgshapiro	/* "user" is currently unused */
423998121Sgshapiro# if SASL >= 20000
424098121Sgshapiro	result = sasl_listmech(conn, NULL,
424198121Sgshapiro			       "", " ", "", (const char **) mechlist,
4242120256Sgshapiro			       (unsigned int *)&len, &num);
424398121Sgshapiro# else /* SASL >= 20000 */
424464562Sgshapiro	result = sasl_listmech(conn, "user", /* XXX */
424564562Sgshapiro			       "", " ", "", mechlist,
424690792Sgshapiro			       (unsigned int *)&len, (unsigned int *)&num);
424798121Sgshapiro# endif /* SASL >= 20000 */
424890792Sgshapiro	if (result != SASL_OK)
424964562Sgshapiro	{
425090792Sgshapiro		if (LogLevel > 9)
425190792Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
425290792Sgshapiro				  "AUTH error: listmech=%d, num=%d",
425390792Sgshapiro				  result, num);
425490792Sgshapiro		num = 0;
425590792Sgshapiro	}
425690792Sgshapiro	if (num > 0)
425790792Sgshapiro	{
425864562Sgshapiro		if (LogLevel > 11)
425964562Sgshapiro			sm_syslog(LOG_INFO, NOQID,
426090792Sgshapiro				  "AUTH: available mech=%s, allowed mech=%s",
426164562Sgshapiro				  *mechlist, AuthMechanisms);
426290792Sgshapiro		*mechlist = intersect(AuthMechanisms, *mechlist, NULL);
426364562Sgshapiro	}
426464562Sgshapiro	else
426564562Sgshapiro	{
426690792Sgshapiro		*mechlist = NULL;	/* be paranoid... */
426790792Sgshapiro		if (result == SASL_OK && LogLevel > 9)
426864562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
426990792Sgshapiro				  "AUTH warning: no mechanisms");
427064562Sgshapiro	}
427164562Sgshapiro	return num;
427264562Sgshapiro}
427398121Sgshapiro
427498121Sgshapiro# if SASL >= 20000
427590792Sgshapiro/*
427664562Sgshapiro**  PROXY_POLICY -- define proxy policy for AUTH
427764562Sgshapiro**
427864562Sgshapiro**	Parameters:
427998121Sgshapiro**		conn -- unused.
428090792Sgshapiro**		context -- unused.
428198121Sgshapiro**		requested_user -- authorization identity.
428298121Sgshapiro**		rlen -- authorization identity length.
428390792Sgshapiro**		auth_identity -- authentication identity.
428498121Sgshapiro**		alen -- authentication identity length.
428598121Sgshapiro**		def_realm -- default user realm.
428698121Sgshapiro**		urlen -- user realm length.
428798121Sgshapiro**		propctx -- unused.
428898121Sgshapiro**
428998121Sgshapiro**	Returns:
429098121Sgshapiro**		ok?
429198121Sgshapiro**
429298121Sgshapiro**	Side Effects:
429398121Sgshapiro**		sets {auth_authen} macro.
429498121Sgshapiro*/
429598121Sgshapiro
429698121Sgshapiroint
429798121Sgshapiroproxy_policy(conn, context, requested_user, rlen, auth_identity, alen,
429898121Sgshapiro	     def_realm, urlen, propctx)
429998121Sgshapiro	sasl_conn_t *conn;
430098121Sgshapiro	void *context;
430198121Sgshapiro	const char *requested_user;
430298121Sgshapiro	unsigned rlen;
430398121Sgshapiro	const char *auth_identity;
430498121Sgshapiro	unsigned alen;
430598121Sgshapiro	const char *def_realm;
430698121Sgshapiro	unsigned urlen;
430798121Sgshapiro	struct propctx *propctx;
430898121Sgshapiro{
430998121Sgshapiro	if (auth_identity == NULL)
431098121Sgshapiro		return SASL_FAIL;
431198121Sgshapiro
431298121Sgshapiro	macdefine(&BlankEnvelope.e_macro, A_TEMP,
431398121Sgshapiro		  macid("{auth_authen}"), (char *) auth_identity);
431498121Sgshapiro
431598121Sgshapiro	return SASL_OK;
431698121Sgshapiro}
431798121Sgshapiro# else /* SASL >= 20000 */
431898121Sgshapiro
431998121Sgshapiro/*
432098121Sgshapiro**  PROXY_POLICY -- define proxy policy for AUTH
432198121Sgshapiro**
432298121Sgshapiro**	Parameters:
432398121Sgshapiro**		context -- unused.
432498121Sgshapiro**		auth_identity -- authentication identity.
432590792Sgshapiro**		requested_user -- authorization identity.
432690792Sgshapiro**		user -- allowed user (output).
432790792Sgshapiro**		errstr -- possible error string (output).
432864562Sgshapiro**
432964562Sgshapiro**	Returns:
433064562Sgshapiro**		ok?
433164562Sgshapiro*/
433264562Sgshapiro
433364562Sgshapiroint
433464562Sgshapiroproxy_policy(context, auth_identity, requested_user, user, errstr)
433564562Sgshapiro	void *context;
433664562Sgshapiro	const char *auth_identity;
433764562Sgshapiro	const char *requested_user;
433864562Sgshapiro	const char **user;
433964562Sgshapiro	const char **errstr;
434064562Sgshapiro{
434164562Sgshapiro	if (user == NULL || auth_identity == NULL)
434264562Sgshapiro		return SASL_FAIL;
434364562Sgshapiro	*user = newstr(auth_identity);
434464562Sgshapiro	return SASL_OK;
434564562Sgshapiro}
434698121Sgshapiro# endif /* SASL >= 20000 */
434790792Sgshapiro#endif /* SASL */
434864562Sgshapiro
434990792Sgshapiro#if STARTTLS
435090792Sgshapiro/*
435190792Sgshapiro**  INITSRVTLS -- initialize server side TLS
435264562Sgshapiro**
435364562Sgshapiro**	Parameters:
435490792Sgshapiro**		tls_ok -- should tls initialization be done?
435564562Sgshapiro**
435664562Sgshapiro**	Returns:
435790792Sgshapiro**		succeeded?
435864562Sgshapiro**
435964562Sgshapiro**	Side Effects:
436090792Sgshapiro**		sets tls_ok_srv which is a static variable in this module.
436190792Sgshapiro**		Do NOT remove assignments to it!
436264562Sgshapiro*/
436364562Sgshapiro
436466494Sgshapirobool
436590792Sgshapiroinitsrvtls(tls_ok)
436690792Sgshapiro	bool tls_ok;
436764562Sgshapiro{
436890792Sgshapiro	if (!tls_ok)
436990792Sgshapiro		return false;
437064562Sgshapiro
437190792Sgshapiro	/* do NOT remove assignment */
4372110560Sgshapiro	tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCertFile,
4373110560Sgshapiro			     SrvKeyFile, CACertPath, CACertFile, DHParams);
437490792Sgshapiro	return tls_ok_srv;
437564562Sgshapiro}
437690792Sgshapiro#endif /* STARTTLS */
437764562Sgshapiro/*
437890792Sgshapiro**  SRVFEATURES -- get features for SMTP server
437964562Sgshapiro**
438064562Sgshapiro**	Parameters:
438190792Sgshapiro**		e -- envelope (should be session context).
438290792Sgshapiro**		clientname -- name of client.
438390792Sgshapiro**		features -- default features for this invocation.
438464562Sgshapiro**
438564562Sgshapiro**	Returns:
438690792Sgshapiro**		server features.
438764562Sgshapiro*/
438864562Sgshapiro
438990792Sgshapiro/* table with options: it uses just one character, how about strings? */
439090792Sgshapirostatic struct
439164562Sgshapiro{
439290792Sgshapiro	char		srvf_opt;
439390792Sgshapiro	unsigned int	srvf_flag;
439490792Sgshapiro} srv_feat_table[] =
439564562Sgshapiro{
439690792Sgshapiro	{ 'A',	SRV_OFFER_AUTH	},
4397132943Sgshapiro	{ 'B',	SRV_OFFER_VERB	},
4398132943Sgshapiro	{ 'C',	SRV_REQ_SEC	},
4399132943Sgshapiro	{ 'D',	SRV_OFFER_DSN	},
4400132943Sgshapiro	{ 'E',	SRV_OFFER_ETRN	},
4401132943Sgshapiro	{ 'L',	SRV_REQ_AUTH	},
440290792Sgshapiro#if PIPELINING
440390792Sgshapiro# if _FFR_NO_PIPE
440490792Sgshapiro	{ 'N',	SRV_NO_PIPE	},
440590792Sgshapiro# endif /* _FFR_NO_PIPE */
440690792Sgshapiro	{ 'P',	SRV_OFFER_PIPE	},
440790792Sgshapiro#endif /* PIPELINING */
4408132943Sgshapiro	{ 'R',	SRV_VRFY_CLT	},	/* same as V; not documented */
440990792Sgshapiro	{ 'S',	SRV_OFFER_TLS	},
441090792Sgshapiro/*	{ 'T',	SRV_TMP_FAIL	},	*/
441190792Sgshapiro	{ 'V',	SRV_VRFY_CLT	},
4412132943Sgshapiro	{ 'X',	SRV_OFFER_EXPN	},
441390792Sgshapiro/*	{ 'Y',	SRV_OFFER_VRFY	},	*/
441490792Sgshapiro	{ '\0',	SRV_NONE	}
441590792Sgshapiro};
441664562Sgshapiro
441790792Sgshapirostatic unsigned int
441890792Sgshapirosrvfeatures(e, clientname, features)
441990792Sgshapiro	ENVELOPE *e;
442090792Sgshapiro	char *clientname;
442190792Sgshapiro	unsigned int features;
442277349Sgshapiro{
442390792Sgshapiro	int r, i, j;
442490792Sgshapiro	char **pvp, c, opt;
442590792Sgshapiro	char pvpbuf[PSBUFSIZE];
442677349Sgshapiro
442790792Sgshapiro	pvp = NULL;
442890792Sgshapiro	r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
442990792Sgshapiro		  sizeof(pvpbuf));
443090792Sgshapiro	if (r != EX_OK)
443190792Sgshapiro		return features;
443290792Sgshapiro	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
443390792Sgshapiro		return features;
443490792Sgshapiro	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
443590792Sgshapiro		return SRV_TMP_FAIL;
443677349Sgshapiro
443764562Sgshapiro	/*
443890792Sgshapiro	**  General rule (see sendmail.h, d_flags):
443990792Sgshapiro	**  lower case: required/offered, upper case: Not required/available
444090792Sgshapiro	**
444190792Sgshapiro	**  Since we can change some features per daemon, we have both
444290792Sgshapiro	**  cases here: turn on/off a feature.
444364562Sgshapiro	*/
444464562Sgshapiro
444590792Sgshapiro	for (i = 1; pvp[i] != NULL; i++)
444664562Sgshapiro	{
444790792Sgshapiro		c = pvp[i][0];
444890792Sgshapiro		j = 0;
444990792Sgshapiro		for (;;)
445064562Sgshapiro		{
445190792Sgshapiro			if ((opt = srv_feat_table[j].srvf_opt) == '\0')
445264562Sgshapiro			{
445390792Sgshapiro				if (LogLevel > 9)
445490792Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
445590792Sgshapiro						  "srvfeatures: unknown feature %s",
445690792Sgshapiro						  pvp[i]);
445790792Sgshapiro				break;
445864562Sgshapiro			}
445990792Sgshapiro			if (c == opt)
446064562Sgshapiro			{
446190792Sgshapiro				features &= ~(srv_feat_table[j].srvf_flag);
446290792Sgshapiro				break;
446364562Sgshapiro			}
446490792Sgshapiro			if (c == tolower(opt))
446564562Sgshapiro			{
446690792Sgshapiro				features |= srv_feat_table[j].srvf_flag;
446790792Sgshapiro				break;
446864562Sgshapiro			}
446990792Sgshapiro			++j;
447064562Sgshapiro		}
447164562Sgshapiro	}
447290792Sgshapiro	return features;
447364562Sgshapiro}
447464562Sgshapiro
447590792Sgshapiro/*
447638032Speter**  HELP -- implement the HELP command.
447738032Speter**
447838032Speter**	Parameters:
447938032Speter**		topic -- the topic we want help for.
448090792Sgshapiro**		e -- envelope.
448138032Speter**
448238032Speter**	Returns:
448338032Speter**		none.
448438032Speter**
448538032Speter**	Side Effects:
448638032Speter**		outputs the help file to message output.
448738032Speter*/
448864562Sgshapiro#define HELPVSTR	"#vers	"
448964562Sgshapiro#define HELPVERSION	2
449038032Speter
449138032Spetervoid
449264562Sgshapirohelp(topic, e)
449338032Speter	char *topic;
449464562Sgshapiro	ENVELOPE *e;
449538032Speter{
449690792Sgshapiro	register SM_FILE_T *hf;
449764562Sgshapiro	register char *p;
449838032Speter	int len;
449938032Speter	bool noinfo;
450090792Sgshapiro	bool first = true;
450164562Sgshapiro	long sff = SFF_OPENASROOT|SFF_REGONLY;
450238032Speter	char buf[MAXLINE];
450364562Sgshapiro	char inp[MAXLINE];
450464562Sgshapiro	static int foundvers = -1;
450538032Speter	extern char Version[];
450638032Speter
450738032Speter	if (DontLockReadFiles)
450838032Speter		sff |= SFF_NOLOCK;
450964562Sgshapiro	if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
451038032Speter		sff |= SFF_SAFEDIRPATH;
451138032Speter
451238032Speter	if (HelpFile == NULL ||
451338032Speter	    (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
451438032Speter	{
451538032Speter		/* no help */
451638032Speter		errno = 0;
451764562Sgshapiro		message("502 5.3.0 Sendmail %s -- HELP not implemented",
451864562Sgshapiro			Version);
451938032Speter		return;
452038032Speter	}
452138032Speter
452238032Speter	if (topic == NULL || *topic == '\0')
452338032Speter	{
452438032Speter		topic = "smtp";
452590792Sgshapiro		noinfo = false;
452638032Speter	}
452738032Speter	else
452838032Speter	{
452938032Speter		makelower(topic);
453090792Sgshapiro		noinfo = true;
453138032Speter	}
453238032Speter
453338032Speter	len = strlen(topic);
453438032Speter
453590792Sgshapiro	while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
453638032Speter	{
453764562Sgshapiro		if (buf[0] == '#')
453864562Sgshapiro		{
453964562Sgshapiro			if (foundvers < 0 &&
454064562Sgshapiro			    strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
454164562Sgshapiro			{
454264562Sgshapiro				int h;
454364562Sgshapiro
454490792Sgshapiro				if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
454590792Sgshapiro						 &h) == 1)
454664562Sgshapiro					foundvers = h;
454764562Sgshapiro			}
454864562Sgshapiro			continue;
454964562Sgshapiro		}
455038032Speter		if (strncmp(buf, topic, len) == 0)
455138032Speter		{
455264562Sgshapiro			if (first)
455364562Sgshapiro			{
455490792Sgshapiro				first = false;
455538032Speter
455664562Sgshapiro				/* print version if no/old vers# in file */
455764562Sgshapiro				if (foundvers < 2 && !noinfo)
455864562Sgshapiro					message("214-2.0.0 This is Sendmail version %s", Version);
455964562Sgshapiro			}
456064562Sgshapiro			p = strpbrk(buf, " \t");
456138032Speter			if (p == NULL)
456264562Sgshapiro				p = buf + strlen(buf) - 1;
456338032Speter			else
456438032Speter				p++;
456590792Sgshapiro			fixcrlf(p, true);
456664562Sgshapiro			if (foundvers >= 2)
456764562Sgshapiro			{
456864562Sgshapiro				translate_dollars(p);
456964562Sgshapiro				expand(p, inp, sizeof inp, e);
457064562Sgshapiro				p = inp;
457164562Sgshapiro			}
457264562Sgshapiro			message("214-2.0.0 %s", p);
457390792Sgshapiro			noinfo = false;
457438032Speter		}
457538032Speter	}
457638032Speter
457738032Speter	if (noinfo)
457864562Sgshapiro		message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
457938032Speter	else
458064562Sgshapiro		message("214 2.0.0 End of HELP info");
458164562Sgshapiro
458264562Sgshapiro	if (foundvers != 0 && foundvers < HELPVERSION)
458364562Sgshapiro	{
458464562Sgshapiro		if (LogLevel > 1)
458564562Sgshapiro			sm_syslog(LOG_WARNING, e->e_id,
458664562Sgshapiro				  "%s too old (require version %d)",
458764562Sgshapiro				  HelpFile, HELPVERSION);
458864562Sgshapiro
458964562Sgshapiro		/* avoid log next time */
459064562Sgshapiro		foundvers = 0;
459164562Sgshapiro	}
459264562Sgshapiro
459390792Sgshapiro	(void) sm_io_close(hf, SM_TIME_DEFAULT);
459438032Speter}
4595120256Sgshapiro
4596120256Sgshapiro#if SASL
4597120256Sgshapiro/*
4598120256Sgshapiro**  RESET_SASLCONN -- reset SASL connection data
4599120256Sgshapiro**
4600120256Sgshapiro**	Parameters:
4601120256Sgshapiro**		conn -- SASL connection context
4602120256Sgshapiro**		hostname -- host name
4603120256Sgshapiro**		various connection data
4604120256Sgshapiro**
4605120256Sgshapiro**	Returns:
4606120256Sgshapiro**		SASL result
4607120256Sgshapiro*/
4608120256Sgshapiro
4609120256Sgshapirostatic int
4610120256Sgshapiroreset_saslconn(sasl_conn_t ** conn, char *hostname,
4611120256Sgshapiro# if SASL >= 20000
4612120256Sgshapiro	       char *remoteip, char *localip,
4613120256Sgshapiro	       char *auth_id, sasl_ssf_t * ext_ssf)
4614120256Sgshapiro# else /* SASL >= 20000 */
4615120256Sgshapiro	       struct sockaddr_in * saddr_r, struct sockaddr_in * saddr_l,
4616120256Sgshapiro	       sasl_external_properties_t * ext_ssf)
4617120256Sgshapiro# endif /* SASL >= 20000 */
4618120256Sgshapiro{
4619120256Sgshapiro	int result;
4620120256Sgshapiro
4621120256Sgshapiro	sasl_dispose(conn);
4622120256Sgshapiro# if SASL >= 20000
4623120256Sgshapiro	result = sasl_server_new("smtp", hostname, NULL, NULL, NULL,
4624120256Sgshapiro				 NULL, 0, conn);
4625120256Sgshapiro# elif SASL > 10505
4626120256Sgshapiro	/* use empty realm: only works in SASL > 1.5.5 */
4627120256Sgshapiro	result = sasl_server_new("smtp", hostname, "", NULL, 0, conn);
4628120256Sgshapiro# else /* SASL >= 20000 */
4629120256Sgshapiro	/* use no realm -> realm is set to hostname by SASL lib */
4630120256Sgshapiro	result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
4631120256Sgshapiro				 conn);
4632120256Sgshapiro# endif /* SASL >= 20000 */
4633120256Sgshapiro	if (result != SASL_OK)
4634120256Sgshapiro		return result;
4635120256Sgshapiro
4636120256Sgshapiro# if SASL >= 20000
4637120256Sgshapiro#  if NETINET || NETINET6
4638120256Sgshapiro	if (remoteip != NULL)
4639120256Sgshapiro		result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip);
4640120256Sgshapiro	if (result != SASL_OK)
4641120256Sgshapiro		return result;
4642120256Sgshapiro
4643120256Sgshapiro	if (localip != NULL)
4644120256Sgshapiro		result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip);
4645120256Sgshapiro	if (result != SASL_OK)
4646120256Sgshapiro		return result;
4647120256Sgshapiro#  endif /* NETINET || NETINET6 */
4648120256Sgshapiro
4649120256Sgshapiro	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
4650120256Sgshapiro	if (result != SASL_OK)
4651120256Sgshapiro		return result;
4652120256Sgshapiro
4653120256Sgshapiro	result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id);
4654120256Sgshapiro	if (result != SASL_OK)
4655120256Sgshapiro		return result;
4656120256Sgshapiro# else /* SASL >= 20000 */
4657120256Sgshapiro#  if NETINET
4658120256Sgshapiro	if (saddr_r != NULL)
4659120256Sgshapiro		result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r);
4660120256Sgshapiro	if (result != SASL_OK)
4661120256Sgshapiro		return result;
4662120256Sgshapiro
4663120256Sgshapiro	if (saddr_l != NULL)
4664120256Sgshapiro		result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l);
4665120256Sgshapiro	if (result != SASL_OK)
4666120256Sgshapiro		return result;
4667120256Sgshapiro#  endif /* NETINET */
4668120256Sgshapiro
4669120256Sgshapiro	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
4670120256Sgshapiro	if (result != SASL_OK)
4671120256Sgshapiro		return result;
4672120256Sgshapiro# endif /* SASL >= 20000 */
4673120256Sgshapiro	return SASL_OK;
4674120256Sgshapiro}
4675120256Sgshapiro#endif /* SASL */
4676