srvrsmtp.c revision 94334
138032Speter/*
290792Sgshapiro * Copyright (c) 1998-2002 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
1690792Sgshapiro# include <libmilter/mfdef.h>
1790792Sgshapiro#endif /* MILTER */
1864562Sgshapiro
1994334SgshapiroSM_RCSID("@(#)$Id: srvrsmtp.c,v 8.819 2002/04/02 03:51:02 ca Exp $")
2064562Sgshapiro
2190792Sgshapiro#if SASL || STARTTLS
2290792Sgshapiro# include <sys/time.h>
2390792Sgshapiro# include "sfsasl.h"
2490792Sgshapiro#endif /* SASL || STARTTLS */
2590792Sgshapiro#if SASL
2690792Sgshapiro# define ENC64LEN(l)	(((l) + 2) * 4 / 3 + 1)
2764562Sgshapirostatic int saslmechs __P((sasl_conn_t *, char **));
2890792Sgshapiro#endif /* SASL */
2990792Sgshapiro#if STARTTLS
3090792Sgshapiro# include <sysexits.h>
3138032Speter
3290792Sgshapirostatic SSL_CTX	*srv_ctx = NULL;	/* TLS server context */
3390792Sgshapirostatic SSL	*srv_ssl = NULL;	/* per connection context */
3438032Speter
3590792Sgshapirostatic bool	tls_ok_srv = false;
3690792Sgshapiro
3790792Sgshapiroextern void	tls_set_verify __P((SSL_CTX *, SSL *, bool));
3890792Sgshapiro# define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
3990792Sgshapiro				bitset(SRV_VRFY_CLT, features))
4090792Sgshapiro#endif /* STARTTLS */
4190792Sgshapiro
4290792Sgshapiro/* server features */
4390792Sgshapiro#define SRV_NONE	0x0000	/* none... */
4490792Sgshapiro#define SRV_OFFER_TLS	0x0001	/* offer STARTTLS */
4590792Sgshapiro#define SRV_VRFY_CLT	0x0002	/* request a cert */
4690792Sgshapiro#define SRV_OFFER_AUTH	0x0004	/* offer AUTH */
4790792Sgshapiro#define SRV_OFFER_ETRN	0x0008	/* offer ETRN */
4890792Sgshapiro#define SRV_OFFER_VRFY	0x0010	/* offer VRFY (not yet used) */
4990792Sgshapiro#define SRV_OFFER_EXPN	0x0020	/* offer EXPN */
5090792Sgshapiro#define SRV_OFFER_VERB	0x0040	/* offer VERB */
5190792Sgshapiro#define SRV_OFFER_DSN	0x0080	/* offer DSN */
5290792Sgshapiro#if PIPELINING
5390792Sgshapiro# define SRV_OFFER_PIPE	0x0100	/* offer PIPELINING */
5490792Sgshapiro# if _FFR_NO_PIPE
5590792Sgshapiro#  define SRV_NO_PIPE	0x0200	/* disable PIPELINING, sleep if used */
5690792Sgshapiro# endif /* _FFR_NO_PIPE */
5790792Sgshapiro#endif /* PIPELINING */
5890792Sgshapiro#define SRV_REQ_AUTH	0x0400	/* require AUTH */
5990792Sgshapiro#define SRV_TMP_FAIL	0x1000	/* ruleset caused a temporary failure */
6090792Sgshapiro
6190792Sgshapirostatic unsigned int	srvfeatures __P((ENVELOPE *, char *, unsigned int));
6290792Sgshapiro
6390792Sgshapirostatic time_t	checksmtpattack __P((volatile unsigned int *, int, bool,
6464562Sgshapiro				     char *, ENVELOPE *));
6564562Sgshapirostatic void	mail_esmtp_args __P((char *, char *, ENVELOPE *));
6664562Sgshapirostatic void	printvrfyaddr __P((ADDRESS *, bool, bool));
6764562Sgshapirostatic void	rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
6864562Sgshapirostatic char	*skipword __P((char *volatile, char *));
6990792Sgshapirostatic void	setup_smtpd_io __P((void));
7064562Sgshapiroextern ENVELOPE	BlankEnvelope;
7138032Speter
7290792Sgshapiro#define SKIP_SPACE(s)	while (isascii(*s) && isspace(*s))	\
7390792Sgshapiro				(s)++
7490792Sgshapiro
7538032Speter/*
7638032Speter**  SMTP -- run the SMTP protocol.
7738032Speter**
7838032Speter**	Parameters:
7938032Speter**		nullserver -- if non-NULL, rejection message for
8090792Sgshapiro**			(almost) all SMTP commands.
8190792Sgshapiro**		d_flags -- daemon flags
8238032Speter**		e -- the envelope.
8338032Speter**
8438032Speter**	Returns:
8538032Speter**		never.
8638032Speter**
8738032Speter**	Side Effects:
8890792Sgshapiro**		Reads commands from the input channel and processes them.
8938032Speter*/
9038032Speter
9190792Sgshapiro/*
9290792Sgshapiro**  Notice: The smtp server doesn't have a session context like the client
9390792Sgshapiro**	side has (mci). Therefore some data (session oriented) is allocated
9490792Sgshapiro**	or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
9590792Sgshapiro**	This should be fixed in a successor version.
9690792Sgshapiro*/
9790792Sgshapiro
9838032Speterstruct cmd
9938032Speter{
10064562Sgshapiro	char	*cmd_name;	/* command name */
10164562Sgshapiro	int	cmd_code;	/* internal code, see below */
10238032Speter};
10338032Speter
10464562Sgshapiro/* values for cmd_code */
10590792Sgshapiro#define CMDERROR	0	/* bad command */
10690792Sgshapiro#define CMDMAIL	1	/* mail -- designate sender */
10790792Sgshapiro#define CMDRCPT	2	/* rcpt -- designate recipient */
10890792Sgshapiro#define CMDDATA	3	/* data -- send message text */
10990792Sgshapiro#define CMDRSET	4	/* rset -- reset state */
11090792Sgshapiro#define CMDVRFY	5	/* vrfy -- verify address */
11190792Sgshapiro#define CMDEXPN	6	/* expn -- expand address */
11290792Sgshapiro#define CMDNOOP	7	/* noop -- do nothing */
11390792Sgshapiro#define CMDQUIT	8	/* quit -- close connection and die */
11490792Sgshapiro#define CMDHELO	9	/* helo -- be polite */
11590792Sgshapiro#define CMDHELP	10	/* help -- give usage info */
11690792Sgshapiro#define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
11790792Sgshapiro#define CMDETRN	12	/* etrn -- flush queue */
11890792Sgshapiro#if SASL
11990792Sgshapiro# define CMDAUTH	13	/* auth -- SASL authenticate */
12090792Sgshapiro#endif /* SASL */
12190792Sgshapiro#if STARTTLS
12290792Sgshapiro# define CMDSTLS	14	/* STARTTLS -- start TLS session */
12390792Sgshapiro#endif /* STARTTLS */
12438032Speter/* non-standard commands */
12590792Sgshapiro#define CMDVERB	17	/* verb -- go into verbose mode */
12664562Sgshapiro/* unimplemented commands from RFC 821 */
12790792Sgshapiro#define CMDUNIMPL	19	/* unimplemented rfc821 commands */
12838032Speter/* use this to catch and log "door handle" attempts on your system */
12990792Sgshapiro#define CMDLOGBOGUS	23	/* bogus command that should be logged */
13038032Speter/* debugging-only commands, only enabled if SMTPDEBUG is defined */
13190792Sgshapiro#define CMDDBGQSHOW	24	/* showq -- show send queue */
13290792Sgshapiro#define CMDDBGDEBUG	25	/* debug -- set debug mode */
13338032Speter
13466494Sgshapiro/*
13590792Sgshapiro**  Note: If you change this list, remember to update 'helpfile'
13666494Sgshapiro*/
13766494Sgshapiro
13838032Speterstatic struct cmd	CmdTab[] =
13938032Speter{
14038032Speter	{ "mail",	CMDMAIL		},
14138032Speter	{ "rcpt",	CMDRCPT		},
14238032Speter	{ "data",	CMDDATA		},
14338032Speter	{ "rset",	CMDRSET		},
14438032Speter	{ "vrfy",	CMDVRFY		},
14538032Speter	{ "expn",	CMDEXPN		},
14638032Speter	{ "help",	CMDHELP		},
14738032Speter	{ "noop",	CMDNOOP		},
14838032Speter	{ "quit",	CMDQUIT		},
14938032Speter	{ "helo",	CMDHELO		},
15038032Speter	{ "ehlo",	CMDEHLO		},
15138032Speter	{ "etrn",	CMDETRN		},
15238032Speter	{ "verb",	CMDVERB		},
15364562Sgshapiro	{ "send",	CMDUNIMPL	},
15464562Sgshapiro	{ "saml",	CMDUNIMPL	},
15564562Sgshapiro	{ "soml",	CMDUNIMPL	},
15664562Sgshapiro	{ "turn",	CMDUNIMPL	},
15790792Sgshapiro#if SASL
15864562Sgshapiro	{ "auth",	CMDAUTH,	},
15990792Sgshapiro#endif /* SASL */
16090792Sgshapiro#if STARTTLS
16164562Sgshapiro	{ "starttls",	CMDSTLS,	},
16290792Sgshapiro#endif /* STARTTLS */
16338032Speter    /* remaining commands are here only to trap and log attempts to use them */
16438032Speter	{ "showq",	CMDDBGQSHOW	},
16538032Speter	{ "debug",	CMDDBGDEBUG	},
16638032Speter	{ "wiz",	CMDLOGBOGUS	},
16738032Speter
16838032Speter	{ NULL,		CMDERROR	}
16938032Speter};
17038032Speter
17164562Sgshapirostatic char	*CurSmtpClient;		/* who's at the other end of channel */
17238032Speter
17390792Sgshapiro#ifndef MAXBADCOMMANDS
17490792Sgshapiro# define MAXBADCOMMANDS 25	/* maximum number of bad commands */
17594334Sgshapiro#endif /* ! MAXBADCOMMANDS */
17690792Sgshapiro#ifndef MAXNOOPCOMMANDS
17790792Sgshapiro# define MAXNOOPCOMMANDS 20	/* max "noise" commands before slowdown */
17894334Sgshapiro#endif /* ! MAXNOOPCOMMANDS */
17990792Sgshapiro#ifndef MAXHELOCOMMANDS
18090792Sgshapiro# define MAXHELOCOMMANDS 3	/* max HELO/EHLO commands before slowdown */
18194334Sgshapiro#endif /* ! MAXHELOCOMMANDS */
18290792Sgshapiro#ifndef MAXVRFYCOMMANDS
18390792Sgshapiro# define MAXVRFYCOMMANDS 6	/* max VRFY/EXPN commands before slowdown */
18494334Sgshapiro#endif /* ! MAXVRFYCOMMANDS */
18590792Sgshapiro#ifndef MAXETRNCOMMANDS
18690792Sgshapiro# define MAXETRNCOMMANDS 8	/* max ETRN commands before slowdown */
18794334Sgshapiro#endif /* ! MAXETRNCOMMANDS */
18890792Sgshapiro#ifndef MAXTIMEOUT
18990792Sgshapiro# define MAXTIMEOUT (4 * 60)	/* max timeout for bad commands */
19094334Sgshapiro#endif /* ! MAXTIMEOUT */
19138032Speter
19290792Sgshapiro#if SM_HEAP_CHECK
19390792Sgshapirostatic SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
19490792Sgshapiro	"@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
19590792Sgshapiro#endif /* SM_HEAP_CHECK */
19638032Speter
19790792Sgshapirotypedef struct
19890792Sgshapiro{
19990792Sgshapiro	bool	sm_gotmail;	/* mail command received */
20090792Sgshapiro	unsigned int sm_nrcpts;	/* number of successful RCPT commands */
20190792Sgshapiro#if _FFR_ADAPTIVE_EOL
20290792SgshapiroWARNING: do NOT use this FFR, it is most likely broken
20390792Sgshapiro	bool	sm_crlf;	/* input in CRLF form? */
20490792Sgshapiro#endif /* _FFR_ADAPTIVE_EOL */
20590792Sgshapiro	bool	sm_discard;
20690792Sgshapiro#if MILTER
20790792Sgshapiro	bool	sm_milterize;
20890792Sgshapiro	bool	sm_milterlist;	/* any filters in the list? */
20990792Sgshapiro#endif /* MILTER */
21090792Sgshapiro#if _FFR_QUARANTINE
21190792Sgshapiro	char	*sm_quarmsg;	/* carry quarantining across messages */
21290792Sgshapiro#endif /* _FFR_QUARANTINE */
21390792Sgshapiro} SMTP_T;
21490792Sgshapiro
21590792Sgshapirostatic void	smtp_data __P((SMTP_T *, ENVELOPE *));
21690792Sgshapiro
21790792Sgshapiro#define MSG_TEMPFAIL "451 4.7.1 Please try again later"
21890792Sgshapiro
21990792Sgshapiro#if MILTER
22090792Sgshapiro# define MILTER_ABORT(e)	milter_abort((e))
22190792Sgshapiro# define MILTER_REPLY(str)						\
22290792Sgshapiro	{								\
22390792Sgshapiro		int savelogusrerrs = LogUsrErrs;			\
22490792Sgshapiro									\
22590792Sgshapiro		switch (state)						\
22690792Sgshapiro		{							\
22790792Sgshapiro		  case SMFIR_REPLYCODE:					\
22890792Sgshapiro			if (MilterLogLevel > 3)				\
22990792Sgshapiro			{						\
23090792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
23190792Sgshapiro					  "Milter: %s=%s, reject=%s",	\
23290792Sgshapiro					  str, addr, response);		\
23390792Sgshapiro				LogUsrErrs = false;			\
23490792Sgshapiro			}						\
23590792Sgshapiro			usrerr(response);				\
23690792Sgshapiro			break;						\
23790792Sgshapiro									\
23890792Sgshapiro		  case SMFIR_REJECT:					\
23990792Sgshapiro			if (MilterLogLevel > 3)				\
24090792Sgshapiro			{						\
24190792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
24290792Sgshapiro					  "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
24390792Sgshapiro					  str, addr);			\
24490792Sgshapiro				LogUsrErrs = false;			\
24590792Sgshapiro			}						\
24690792Sgshapiro			usrerr("550 5.7.1 Command rejected");		\
24790792Sgshapiro			break;						\
24890792Sgshapiro									\
24990792Sgshapiro		  case SMFIR_DISCARD:					\
25090792Sgshapiro			if (MilterLogLevel > 3)				\
25190792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
25290792Sgshapiro					  "Milter: %s=%s, discard",	\
25390792Sgshapiro					  str, addr);			\
25490792Sgshapiro			e->e_flags |= EF_DISCARD;			\
25590792Sgshapiro			break;						\
25690792Sgshapiro									\
25790792Sgshapiro		  case SMFIR_TEMPFAIL:					\
25890792Sgshapiro			if (MilterLogLevel > 3)				\
25990792Sgshapiro			{						\
26090792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,		\
26190792Sgshapiro					  "Milter: %s=%s, reject=%s",	\
26290792Sgshapiro					  str, addr, MSG_TEMPFAIL);	\
26390792Sgshapiro				LogUsrErrs = false;			\
26490792Sgshapiro			}						\
26590792Sgshapiro			usrerr(MSG_TEMPFAIL);				\
26690792Sgshapiro			break;						\
26790792Sgshapiro		}							\
26890792Sgshapiro		LogUsrErrs = savelogusrerrs;				\
26990792Sgshapiro		if (response != NULL)					\
27090792Sgshapiro			sm_free(response); /* XXX */			\
27190792Sgshapiro	}
27290792Sgshapiro
27390792Sgshapiro#else /* MILTER */
27490792Sgshapiro# define MILTER_ABORT(e)
27590792Sgshapiro#endif /* MILTER */
27690792Sgshapiro
27790792Sgshapiro/* clear all SMTP state (for HELO/EHLO/RSET) */
27890792Sgshapiro#define CLEAR_STATE(cmd)					\
27990792Sgshapiro{								\
28090792Sgshapiro	/* abort milter filters */				\
28190792Sgshapiro	MILTER_ABORT(e);					\
28290792Sgshapiro								\
28390792Sgshapiro	if (smtp.sm_nrcpts > 0)					\
28490792Sgshapiro	{							\
28590792Sgshapiro		logundelrcpts(e, cmd, 10, false);		\
28690792Sgshapiro		smtp.sm_nrcpts = 0;				\
28790792Sgshapiro		macdefine(&e->e_macro, A_PERM,			\
28890792Sgshapiro			  macid("{nrcpts}"), "0");		\
28990792Sgshapiro	}							\
29090792Sgshapiro								\
29190792Sgshapiro	e->e_sendqueue = NULL;					\
29290792Sgshapiro	e->e_flags |= EF_CLRQUEUE;				\
29390792Sgshapiro								\
29490792Sgshapiro	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))	\
29590792Sgshapiro		logsender(e, NULL);				\
29690792Sgshapiro	e->e_flags &= ~EF_LOGSENDER;				\
29790792Sgshapiro								\
29890792Sgshapiro	/* clean up a bit */					\
29990792Sgshapiro	smtp.sm_gotmail = false;				\
30090792Sgshapiro	SuprErrs = true;					\
30190792Sgshapiro	dropenvelope(e, true, false);				\
30290792Sgshapiro	sm_rpool_free(e->e_rpool);				\
30390792Sgshapiro	e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL));	\
30490792Sgshapiro	CurEnv = e;						\
30590792Sgshapiro}
30690792Sgshapiro
30790792Sgshapiro/* sleep to flatten out connection load */
30890792Sgshapiro#define MIN_DELAY_LOG	15	/* wait before logging this again */
30990792Sgshapiro
31090792Sgshapiro/* is it worth setting the process title for 1s? */
31190792Sgshapiro#define DELAY_CONN(cmd)						\
31290792Sgshapiro	if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA)	\
31390792Sgshapiro	{							\
31490792Sgshapiro		time_t dnow;					\
31590792Sgshapiro								\
31690792Sgshapiro		sm_setproctitle(true, e,			\
31790792Sgshapiro				"%s: %s: delaying %s: load average: %d", \
31890792Sgshapiro				qid_printname(e), CurSmtpClient,	\
31990792Sgshapiro				cmd, DelayLA);	\
32090792Sgshapiro		if (LogLevel > 8 && (dnow = curtime()) > log_delay)	\
32190792Sgshapiro		{						\
32290792Sgshapiro			sm_syslog(LOG_INFO, e->e_id,		\
32390792Sgshapiro				  "delaying=%s, load average=%d >= %d",	\
32490792Sgshapiro				  cmd, CurrentLA, DelayLA);		\
32590792Sgshapiro			log_delay = dnow + MIN_DELAY_LOG;	\
32690792Sgshapiro		}						\
32790792Sgshapiro		(void) sleep(1);				\
32890792Sgshapiro		sm_setproctitle(true, e, "%s %s: %.80s",	\
32990792Sgshapiro				qid_printname(e), CurSmtpClient, inp);	\
33090792Sgshapiro	}
33190792Sgshapiro
33290792Sgshapiro
33338032Spetervoid
33464562Sgshapirosmtp(nullserver, d_flags, e)
33564562Sgshapiro	char *volatile nullserver;
33664562Sgshapiro	BITMAP256 d_flags;
33738032Speter	register ENVELOPE *volatile e;
33838032Speter{
33938032Speter	register char *volatile p;
34064562Sgshapiro	register struct cmd *volatile c = NULL;
34138032Speter	char *cmd;
34238032Speter	auto ADDRESS *vrfyqueue;
34338032Speter	ADDRESS *a;
34438032Speter	volatile bool gothello;		/* helo command received */
34538032Speter	bool vrfy;			/* set if this is a vrfy command */
34638032Speter	char *volatile protocol;	/* sending protocol */
34738032Speter	char *volatile sendinghost;	/* sending hostname */
34838032Speter	char *volatile peerhostname;	/* name of SMTP peer or "localhost" */
34938032Speter	auto char *delimptr;
35038032Speter	char *id;
35190792Sgshapiro	volatile unsigned int n_badcmds = 0;	/* count of bad commands */
35290792Sgshapiro	volatile unsigned int n_badrcpts = 0;	/* number of rejected RCPT */
35390792Sgshapiro	volatile unsigned int n_verifies = 0;	/* count of VRFY/EXPN */
35490792Sgshapiro	volatile unsigned int n_etrn = 0;	/* count of ETRN */
35590792Sgshapiro	volatile unsigned int n_noop = 0;	/* count of NOOP/VERB/etc */
35690792Sgshapiro	volatile unsigned int n_helo = 0;	/* count of HELO/EHLO */
35738032Speter	bool ok;
35890792Sgshapiro#if _FFR_ADAPTIVE_EOL
35990792Sgshapiro	volatile bool first;
36090792Sgshapiro#endif /* _FFR_ADAPTIVE_EOL */
36190792Sgshapiro	volatile bool tempfail = false;
36264562Sgshapiro	volatile time_t wt;		/* timeout after too many commands */
36364562Sgshapiro	volatile time_t previous;	/* time after checksmtpattack() */
36490792Sgshapiro	volatile bool lognullconnection = true;
36538032Speter	register char *q;
36690792Sgshapiro	SMTP_T smtp;
36764562Sgshapiro	char *addr;
36864562Sgshapiro	char *greetcode = "220";
36990792Sgshapiro	char *hostname;			/* my hostname ($j) */
37038032Speter	QUEUE_CHAR *new;
37164562Sgshapiro	int argno;
37264562Sgshapiro	char *args[MAXSMTPARGS];
37338032Speter	char inp[MAXLINE];
37438032Speter	char cmdbuf[MAXLINE];
37590792Sgshapiro#if SASL
37664562Sgshapiro	sasl_conn_t *conn;
37764562Sgshapiro	volatile bool sasl_ok;
37890792Sgshapiro	volatile unsigned int n_auth = 0;	/* count of AUTH commands */
37964562Sgshapiro	bool ismore;
38064562Sgshapiro	int result;
38164562Sgshapiro	volatile int authenticating;
38264562Sgshapiro	char *user;
38364562Sgshapiro	char *in, *out, *out2;
38464562Sgshapiro	const char *errstr;
38590792Sgshapiro	unsigned int inlen, out2len;
38664562Sgshapiro	unsigned int outlen;
38764562Sgshapiro	char *volatile auth_type;
38864562Sgshapiro	char *mechlist;
38990792Sgshapiro	volatile unsigned int n_mechs;
39090792Sgshapiro	unsigned int len;
39164562Sgshapiro	sasl_security_properties_t ssp;
39264562Sgshapiro	sasl_external_properties_t ext_ssf;
39364562Sgshapiro	sasl_ssf_t *ssf;
39490792Sgshapiro#endif /* SASL */
39590792Sgshapiro#if STARTTLS
39664562Sgshapiro	int r;
39766494Sgshapiro	int rfd, wfd;
39890792Sgshapiro	volatile bool tls_active = false;
39990792Sgshapiro# if _FFR_SMTP_SSL
40090792Sgshapiro	volatile bool smtps = false;
40190792Sgshapiro# endif /* _FFR_SMTP_SSL */
40264562Sgshapiro	bool saveQuickAbort;
40364562Sgshapiro	bool saveSuprErrs;
40490792Sgshapiro	time_t tlsstart;
40590792Sgshapiro#endif /* STARTTLS */
40690792Sgshapiro	volatile unsigned int features;
40790792Sgshapiro#if PIPELINING
40890792Sgshapiro# if _FFR_NO_PIPE
40990792Sgshapiro	int np_log = 0;
41090792Sgshapiro# endif /* _FFR_NO_PIPE */
41190792Sgshapiro#endif /* PIPELINING */
41290792Sgshapiro	volatile time_t log_delay = (time_t) 0;
41338032Speter
41490792Sgshapiro	smtp.sm_nrcpts = 0;
41590792Sgshapiro#if MILTER
41690792Sgshapiro	smtp.sm_milterize = (nullserver == NULL);
41790792Sgshapiro	smtp.sm_milterlist = false;
41890792Sgshapiro#endif /* MILTER */
41990792Sgshapiro
42090792Sgshapiro	/* setup I/O fd correctly for the SMTP server */
42190792Sgshapiro	setup_smtpd_io();
42290792Sgshapiro
42390792Sgshapiro#if SM_HEAP_CHECK
42490792Sgshapiro	if (sm_debug_active(&DebugLeakSmtp, 1))
42538032Speter	{
42690792Sgshapiro		sm_heap_newgroup();
42790792Sgshapiro		sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
42838032Speter	}
42990792Sgshapiro#endif /* SM_HEAP_CHECK */
43064562Sgshapiro
43190792Sgshapiro	/* XXX the rpool should be set when e is initialized in main() */
43290792Sgshapiro	e->e_rpool = sm_rpool_new_x(NULL);
43390792Sgshapiro	e->e_macro.mac_rpool = e->e_rpool;
43490792Sgshapiro
43538032Speter	settime(e);
43690792Sgshapiro	sm_getla();
43738032Speter	peerhostname = RealHostName;
43838032Speter	if (peerhostname == NULL)
43938032Speter		peerhostname = "localhost";
44038032Speter	CurHostName = peerhostname;
44138032Speter	CurSmtpClient = macvalue('_', e);
44238032Speter	if (CurSmtpClient == NULL)
44338032Speter		CurSmtpClient = CurHostName;
44438032Speter
44538032Speter	/* check_relay may have set discard bit, save for later */
44690792Sgshapiro	smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
44738032Speter
44890792Sgshapiro#if PIPELINING
44990792Sgshapiro	/* auto-flush output when reading input */
45090792Sgshapiro	(void) sm_io_autoflush(InChannel, OutChannel);
45190792Sgshapiro#endif /* PIPELINING */
45264562Sgshapiro
45390792Sgshapiro	sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
45490792Sgshapiro
45590792Sgshapiro	/* Set default features for server. */
45690792Sgshapiro	features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
45790792Sgshapiro		     bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
45890792Sgshapiro		| (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
45990792Sgshapiro		| (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
46090792Sgshapiro			: (SRV_OFFER_EXPN
46190792Sgshapiro			  | (bitset(PRIV_NOVERB, PrivacyFlags)
46290792Sgshapiro			     ? SRV_NONE : SRV_OFFER_VERB)))
46390792Sgshapiro		| (bitset(PRIV_NORECEIPTS, PrivacyFlags) ? SRV_NONE
46490792Sgshapiro							 : SRV_OFFER_DSN)
46590792Sgshapiro#if SASL
46690792Sgshapiro		| (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
46790792Sgshapiro#endif /* SASL */
46890792Sgshapiro#if PIPELINING
46990792Sgshapiro		| SRV_OFFER_PIPE
47090792Sgshapiro#endif /* PIPELINING */
47190792Sgshapiro#if STARTTLS
47290792Sgshapiro		| (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
47390792Sgshapiro		| (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
47490792Sgshapiro						       : SRV_VRFY_CLT)
47590792Sgshapiro#endif /* STARTTLS */
47690792Sgshapiro		;
47790792Sgshapiro	if (nullserver == NULL)
47890792Sgshapiro	{
47990792Sgshapiro		features = srvfeatures(e, CurSmtpClient, features);
48090792Sgshapiro		if (bitset(SRV_TMP_FAIL, features))
48190792Sgshapiro		{
48290792Sgshapiro			if (LogLevel > 4)
48390792Sgshapiro				sm_syslog(LOG_ERR, NOQID,
48490792Sgshapiro					  "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
48590792Sgshapiro					  CurSmtpClient);
48690792Sgshapiro			nullserver = "450 4.3.0 Please try again later.";
48790792Sgshapiro		}
48890792Sgshapiro#if PIPELINING
48990792Sgshapiro# if _FFR_NO_PIPE
49090792Sgshapiro		else if (bitset(SRV_NO_PIPE, features))
49190792Sgshapiro		{
49290792Sgshapiro			/* for consistency */
49390792Sgshapiro			features &= ~SRV_OFFER_PIPE;
49490792Sgshapiro		}
49590792Sgshapiro# endif /* _FFR_NO_PIPE */
49690792Sgshapiro#endif /* PIPELINING */
49790792Sgshapiro	}
49890792Sgshapiro
49990792Sgshapiro	hostname = macvalue('j', e);
50090792Sgshapiro
50190792Sgshapiro
50290792Sgshapiro#if SASL
50390792Sgshapiro	sasl_ok = bitset(SRV_OFFER_AUTH, features);
50464562Sgshapiro	n_mechs = 0;
50590792Sgshapiro	authenticating = SASL_NOT_AUTH;
50664562Sgshapiro
50764562Sgshapiro	/* SASL server new connection */
50890792Sgshapiro	if (sasl_ok)
50938032Speter	{
51090792Sgshapiro# if SASL > 10505
51190792Sgshapiro		/* use empty realm: only works in SASL > 1.5.5 */
51290792Sgshapiro		result = sasl_server_new("smtp", hostname, "", NULL, 0, &conn);
51390792Sgshapiro# else /* SASL > 10505 */
51490792Sgshapiro		/* use no realm -> realm is set to hostname by SASL lib */
51590792Sgshapiro		result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
51690792Sgshapiro					 &conn);
51790792Sgshapiro# endif /* SASL > 10505 */
51890792Sgshapiro		sasl_ok = result == SASL_OK;
51990792Sgshapiro		if (!sasl_ok)
52090792Sgshapiro		{
52190792Sgshapiro			if (LogLevel > 9)
52290792Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
52390792Sgshapiro					  "AUTH error: sasl_server_new failed=%d",
52490792Sgshapiro					  result);
52590792Sgshapiro		}
52690792Sgshapiro	}
52790792Sgshapiro	if (sasl_ok)
52890792Sgshapiro	{
52964562Sgshapiro		/*
53064562Sgshapiro		**  SASL set properties for sasl
53164562Sgshapiro		**  set local/remote IP
53264562Sgshapiro		**  XXX only IPv4: Cyrus SASL doesn't support anything else
53364562Sgshapiro		**
53464562Sgshapiro		**  XXX where exactly are these used/required?
53564562Sgshapiro		**  Kerberos_v4
53664562Sgshapiro		*/
53764562Sgshapiro
53894334Sgshapiro# if NETINET
53990792Sgshapiro		in = macvalue(macid("{daemon_family}"), e);
54064562Sgshapiro		if (in != NULL && strcmp(in, "inet") == 0)
54164562Sgshapiro		{
54264562Sgshapiro			SOCKADDR_LEN_T addrsize;
54364562Sgshapiro			struct sockaddr_in saddr_l;
54464562Sgshapiro			struct sockaddr_in saddr_r;
54564562Sgshapiro
54664562Sgshapiro			addrsize = sizeof(struct sockaddr_in);
54790792Sgshapiro			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
54890792Sgshapiro						      NULL),
54964562Sgshapiro					(struct sockaddr *)&saddr_r,
55064562Sgshapiro					&addrsize) == 0)
55164562Sgshapiro			{
55264562Sgshapiro				sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
55364562Sgshapiro				addrsize = sizeof(struct sockaddr_in);
55490792Sgshapiro				if (getsockname(sm_io_getinfo(InChannel,
55590792Sgshapiro							      SM_IO_WHAT_FD,
55690792Sgshapiro							      NULL),
55764562Sgshapiro						(struct sockaddr *)&saddr_l,
55864562Sgshapiro						&addrsize) == 0)
55964562Sgshapiro					sasl_setprop(conn, SASL_IP_LOCAL,
56064562Sgshapiro						     &saddr_l);
56164562Sgshapiro			}
56264562Sgshapiro		}
56394334Sgshapiro# endif /* NETINET */
56464562Sgshapiro
56564562Sgshapiro		auth_type = NULL;
56664562Sgshapiro		mechlist = NULL;
56764562Sgshapiro		user = NULL;
56890792Sgshapiro# if 0
56990792Sgshapiro		macdefine(&BlankEnvelope.e_macro, A_PERM,
57090792Sgshapiro			macid("{auth_author}"), NULL);
57190792Sgshapiro# endif /* 0 */
57264562Sgshapiro
57364562Sgshapiro		/* set properties */
57464562Sgshapiro		(void) memset(&ssp, '\0', sizeof ssp);
57590792Sgshapiro
57664562Sgshapiro		/* XXX should these be options settable via .cf ? */
57764562Sgshapiro		/* ssp.min_ssf = 0; is default due to memset() */
57894334Sgshapiro# if STARTTLS
57994334Sgshapiro# endif /* STARTTLS */
58064562Sgshapiro		{
58190792Sgshapiro			ssp.max_ssf = MaxSLBits;
58264562Sgshapiro			ssp.maxbufsize = MAXOUTLEN;
58364562Sgshapiro		}
58464562Sgshapiro		ssp.security_flags = SASLOpts & SASL_SEC_MASK;
58564562Sgshapiro		sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
58664562Sgshapiro
58764562Sgshapiro		if (sasl_ok)
58864562Sgshapiro		{
58964562Sgshapiro			/*
59064562Sgshapiro			**  external security strength factor;
59190792Sgshapiro			**	currently we have none so zero
59264562Sgshapiro			*/
59390792Sgshapiro
59464562Sgshapiro			ext_ssf.ssf = 0;
59564562Sgshapiro			ext_ssf.auth_id = NULL;
59664562Sgshapiro			sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
59764562Sgshapiro					       &ext_ssf) == SASL_OK;
59864562Sgshapiro		}
59964562Sgshapiro		if (sasl_ok)
60064562Sgshapiro			n_mechs = saslmechs(conn, &mechlist);
60138032Speter	}
60290792Sgshapiro#endif /* SASL */
60338032Speter
60490792Sgshapiro#if MILTER
60590792Sgshapiro	if (smtp.sm_milterize)
60664562Sgshapiro	{
60764562Sgshapiro		char state;
60864562Sgshapiro
60964562Sgshapiro		/* initialize mail filter connection */
61090792Sgshapiro		smtp.sm_milterlist = milter_init(e, &state);
61164562Sgshapiro		switch (state)
61264562Sgshapiro		{
61364562Sgshapiro		  case SMFIR_REJECT:
61490792Sgshapiro			if (MilterLogLevel > 3)
61590792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
61694334Sgshapiro					  "Milter: initialization failed, rejecting commands");
61764562Sgshapiro			greetcode = "554";
61864562Sgshapiro			nullserver = "Command rejected";
61990792Sgshapiro			smtp.sm_milterize = false;
62064562Sgshapiro			break;
62164562Sgshapiro
62264562Sgshapiro		  case SMFIR_TEMPFAIL:
62390792Sgshapiro			if (MilterLogLevel > 3)
62490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
62594334Sgshapiro					  "Milter: initialization failed, temp failing commands");
62690792Sgshapiro			tempfail = true;
62790792Sgshapiro			smtp.sm_milterize = false;
62864562Sgshapiro			break;
62964562Sgshapiro		}
63064562Sgshapiro	}
63164562Sgshapiro
63290792Sgshapiro	if (smtp.sm_milterlist && smtp.sm_milterize &&
63390792Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
63464562Sgshapiro	{
63564562Sgshapiro		char state;
63690792Sgshapiro		char *response;
63764562Sgshapiro
63890792Sgshapiro		response = milter_connect(peerhostname, RealHostAddr,
63990792Sgshapiro					  e, &state);
64064562Sgshapiro		switch (state)
64164562Sgshapiro		{
64264562Sgshapiro		  case SMFIR_REPLYCODE:	/* REPLYCODE shouldn't happen */
64364562Sgshapiro		  case SMFIR_REJECT:
64490792Sgshapiro			if (MilterLogLevel > 3)
64590792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
64690792Sgshapiro					  "Milter: connect: host=%s, addr=%s, rejecting commands",
64790792Sgshapiro					  peerhostname,
64890792Sgshapiro					  anynet_ntoa(&RealHostAddr));
64964562Sgshapiro			greetcode = "554";
65064562Sgshapiro			nullserver = "Command rejected";
65190792Sgshapiro			smtp.sm_milterize = false;
65264562Sgshapiro			break;
65364562Sgshapiro
65464562Sgshapiro		  case SMFIR_TEMPFAIL:
65590792Sgshapiro			if (MilterLogLevel > 3)
65690792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
65790792Sgshapiro					  "Milter: connect: host=%s, addr=%s, temp failing commands",
65890792Sgshapiro					  peerhostname,
65990792Sgshapiro					  anynet_ntoa(&RealHostAddr));
66090792Sgshapiro			tempfail = true;
66190792Sgshapiro			smtp.sm_milterize = false;
66264562Sgshapiro			break;
66364562Sgshapiro		}
66490792Sgshapiro		if (response != NULL)
66590792Sgshapiro
66690792Sgshapiro			sm_free(response); /* XXX */
66764562Sgshapiro	}
66890792Sgshapiro#endif /* MILTER */
66964562Sgshapiro
67090792Sgshapiro#if STARTTLS
67190792Sgshapiro# if _FFR_SMTP_SSL
67290792Sgshapiro	/* If this an smtps connection, start TLS now */
67390792Sgshapiro	smtps = bitnset(D_SMTPS, d_flags);
67490792Sgshapiro	if (smtps)
67590792Sgshapiro		goto starttls;
67690792Sgshapiro
67790792Sgshapiro  greeting:
67890792Sgshapiro
67990792Sgshapiro# endif /* _FFR_SMTP_SSL */
68090792Sgshapiro#endif /* STARTTLS */
68190792Sgshapiro
68238032Speter	/* output the first line, inserting "ESMTP" as second word */
68390792Sgshapiro	if (*greetcode == '5')
68490792Sgshapiro		(void) sm_snprintf(inp, sizeof inp, "%s not accepting messages",
68590792Sgshapiro				   hostname);
68690792Sgshapiro	else
68790792Sgshapiro		expand(SmtpGreeting, inp, sizeof inp, e);
68890792Sgshapiro
68938032Speter	p = strchr(inp, '\n');
69038032Speter	if (p != NULL)
69138032Speter		*p++ = '\0';
69238032Speter	id = strchr(inp, ' ');
69338032Speter	if (id == NULL)
69438032Speter		id = &inp[strlen(inp)];
69564562Sgshapiro	if (p == NULL)
69690792Sgshapiro		(void) sm_snprintf(cmdbuf, sizeof cmdbuf,
69764562Sgshapiro			 "%s %%.*s ESMTP%%s", greetcode);
69864562Sgshapiro	else
69990792Sgshapiro		(void) sm_snprintf(cmdbuf, sizeof cmdbuf,
70064562Sgshapiro			 "%s-%%.*s ESMTP%%s", greetcode);
70166494Sgshapiro	message(cmdbuf, (int) (id - inp), inp, id);
70238032Speter
70338032Speter	/* output remaining lines */
70438032Speter	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
70538032Speter	{
70638032Speter		*p++ = '\0';
70738032Speter		if (isascii(*id) && isspace(*id))
70838032Speter			id++;
70990792Sgshapiro		(void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, "-%s");
71064562Sgshapiro		message(cmdbuf, id);
71138032Speter	}
71238032Speter	if (id != NULL)
71338032Speter	{
71438032Speter		if (isascii(*id) && isspace(*id))
71538032Speter			id++;
71690792Sgshapiro		(void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, " %s");
71764562Sgshapiro		message(cmdbuf, id);
71838032Speter	}
71938032Speter
72038032Speter	protocol = NULL;
72138032Speter	sendinghost = macvalue('s', e);
72290792Sgshapiro
72390792Sgshapiro#if _FFR_QUARANTINE
72490792Sgshapiro	/* If quarantining by a connect/ehlo action, save between messages */
72590792Sgshapiro	if (e->e_quarmsg == NULL)
72690792Sgshapiro		smtp.sm_quarmsg = NULL;
72790792Sgshapiro	else
72890792Sgshapiro		smtp.sm_quarmsg = newstr(e->e_quarmsg);
72990792Sgshapiro#endif /* _FFR_QUARANTINE */
73090792Sgshapiro
73190792Sgshapiro	/* sendinghost's storage must outlive the current envelope */
73290792Sgshapiro	if (sendinghost != NULL)
73390792Sgshapiro		sendinghost = sm_strdup_x(sendinghost);
73490792Sgshapiro#if _FFR_ADAPTIVE_EOL
73590792Sgshapiro	first = true;
73690792Sgshapiro#endif /* _FFR_ADAPTIVE_EOL */
73790792Sgshapiro	gothello = false;
73890792Sgshapiro	smtp.sm_gotmail = false;
73938032Speter	for (;;)
74038032Speter	{
74190792Sgshapiro	    SM_TRY
74290792Sgshapiro	    {
74390792Sgshapiro		QuickAbort = false;
74490792Sgshapiro		HoldErrs = false;
74590792Sgshapiro		SuprErrs = false;
74690792Sgshapiro		LogUsrErrs = false;
74790792Sgshapiro		OnlyOneError = true;
74838032Speter		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
74938032Speter
75038032Speter		/* setup for the read */
75138032Speter		e->e_to = NULL;
75238032Speter		Errors = 0;
75364562Sgshapiro		FileName = NULL;
75490792Sgshapiro		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
75538032Speter
75638032Speter		/* read the input line */
75738032Speter		SmtpPhase = "server cmd read";
75890792Sgshapiro		sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
75990792Sgshapiro#if SASL
76064562Sgshapiro		/*
76190792Sgshapiro		**  XXX SMTP AUTH requires accepting any length,
76290792Sgshapiro		**	at least for challenge/response
76364562Sgshapiro		*/
76490792Sgshapiro#endif /* SASL */
76538032Speter
76638032Speter		/* handle errors */
76790792Sgshapiro		if (sm_io_error(OutChannel) ||
76864562Sgshapiro		    (p = sfgets(inp, sizeof inp, InChannel,
76964562Sgshapiro				TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
77038032Speter		{
77164562Sgshapiro			char *d;
77264562Sgshapiro
77390792Sgshapiro			d = macvalue(macid("{daemon_name}"), e);
77464562Sgshapiro			if (d == NULL)
77564562Sgshapiro				d = "stdin";
77638032Speter			/* end of file, just die */
77738032Speter			disconnect(1, e);
77864562Sgshapiro
77990792Sgshapiro#if MILTER
78064562Sgshapiro			/* close out milter filters */
78164562Sgshapiro			milter_quit(e);
78290792Sgshapiro#endif /* MILTER */
78364562Sgshapiro
78464562Sgshapiro			message("421 4.4.1 %s Lost input channel from %s",
78538032Speter				MyHostName, CurSmtpClient);
78690792Sgshapiro			if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
78738032Speter				sm_syslog(LOG_NOTICE, e->e_id,
78864562Sgshapiro					  "lost input channel from %.100s to %s after %s",
78964562Sgshapiro					  CurSmtpClient, d,
79064562Sgshapiro					  (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
79138032Speter			/*
79242575Speter			**  If have not accepted mail (DATA), do not bounce
79342575Speter			**  bad addresses back to sender.
79438032Speter			*/
79564562Sgshapiro
79638032Speter			if (bitset(EF_CLRQUEUE, e->e_flags))
79738032Speter				e->e_sendqueue = NULL;
79864562Sgshapiro			goto doquit;
79938032Speter		}
80038032Speter
80190792Sgshapiro#if _FFR_ADAPTIVE_EOL
80290792Sgshapiro		if (first)
80390792Sgshapiro		{
80490792Sgshapiro			char *p;
80590792Sgshapiro
80690792Sgshapiro			smtp.sm_crlf = true;
80790792Sgshapiro			p = strchr(inp, '\n');
80890792Sgshapiro			if (p == NULL || p <= inp || p[-1] != '\r')
80990792Sgshapiro			{
81090792Sgshapiro				smtp.sm_crlf = false;
81190792Sgshapiro				if (tTd(66, 1) && LogLevel > 8)
81290792Sgshapiro				{
81390792Sgshapiro					/* how many bad guys are there? */
81490792Sgshapiro					sm_syslog(LOG_INFO, NOQID,
81590792Sgshapiro						  "%.100s did not use CRLF",
81690792Sgshapiro						  CurSmtpClient);
81790792Sgshapiro				}
81890792Sgshapiro			}
81990792Sgshapiro			first = false;
82090792Sgshapiro		}
82190792Sgshapiro#endif /* _FFR_ADAPTIVE_EOL */
82290792Sgshapiro
82338032Speter		/* clean up end of line */
82490792Sgshapiro		fixcrlf(inp, true);
82538032Speter
82690792Sgshapiro#if PIPELINING
82790792Sgshapiro# if _FFR_NO_PIPE
82890792Sgshapiro		/*
82990792Sgshapiro		**  if there is more input and pipelining is disabled:
83090792Sgshapiro		**	delay ... (and maybe discard the input?)
83190792Sgshapiro		**  XXX this doesn't really work, at least in tests using
83290792Sgshapiro		**  telnet SM_IO_IS_READABLE only returns 1 if there were
83390792Sgshapiro		**  more than 2 input lines available.
83490792Sgshapiro		*/
83590792Sgshapiro
83690792Sgshapiro		if (bitset(SRV_NO_PIPE, features) &&
83790792Sgshapiro		    sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL))
83890792Sgshapiro		{
83990792Sgshapiro			if (++np_log < 3)
84090792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
84190792Sgshapiro					  "unauthorized PIPELINING, sleeping");
84290792Sgshapiro			sleep(1);
84390792Sgshapiro		}
84490792Sgshapiro
84590792Sgshapiro# endif /* _FFR_NO_PIPE */
84690792Sgshapiro#endif /* PIPELINING */
84790792Sgshapiro
84890792Sgshapiro#if SASL
84964562Sgshapiro		if (authenticating == SASL_PROC_AUTH)
85064562Sgshapiro		{
85190792Sgshapiro# if 0
85264562Sgshapiro			if (*inp == '\0')
85364562Sgshapiro			{
85464562Sgshapiro				authenticating = SASL_NOT_AUTH;
85564562Sgshapiro				message("501 5.5.2 missing input");
85664562Sgshapiro				continue;
85764562Sgshapiro			}
85890792Sgshapiro# endif /* 0 */
85964562Sgshapiro			if (*inp == '*' && *(inp + 1) == '\0')
86064562Sgshapiro			{
86164562Sgshapiro				authenticating = SASL_NOT_AUTH;
86264562Sgshapiro
86364562Sgshapiro				/* rfc 2254 4. */
86464562Sgshapiro				message("501 5.0.0 AUTH aborted");
86564562Sgshapiro				continue;
86664562Sgshapiro			}
86764562Sgshapiro
86864562Sgshapiro			/* could this be shorter? XXX */
86964562Sgshapiro			out = xalloc(strlen(inp));
87064562Sgshapiro			result = sasl_decode64(inp, strlen(inp), out, &outlen);
87164562Sgshapiro			if (result != SASL_OK)
87264562Sgshapiro			{
87364562Sgshapiro				authenticating = SASL_NOT_AUTH;
87464562Sgshapiro
87564562Sgshapiro				/* rfc 2254 4. */
87664562Sgshapiro				message("501 5.5.4 cannot decode AUTH parameter %s",
87764562Sgshapiro					inp);
87864562Sgshapiro				continue;
87964562Sgshapiro			}
88064562Sgshapiro
88164562Sgshapiro			result = sasl_server_step(conn,	out, outlen,
88264562Sgshapiro						  &out, &outlen, &errstr);
88364562Sgshapiro
88464562Sgshapiro			/* get an OK if we're done */
88564562Sgshapiro			if (result == SASL_OK)
88664562Sgshapiro			{
88764562Sgshapiro  authenticated:
88864562Sgshapiro				message("235 2.0.0 OK Authenticated");
88964562Sgshapiro				authenticating = SASL_IS_AUTH;
89090792Sgshapiro				macdefine(&BlankEnvelope.e_macro, A_TEMP,
89190792Sgshapiro					macid("{auth_type}"), auth_type);
89264562Sgshapiro
89364562Sgshapiro				result = sasl_getprop(conn, SASL_USERNAME,
89464562Sgshapiro						      (void **)&user);
89564562Sgshapiro				if (result != SASL_OK)
89664562Sgshapiro				{
89764562Sgshapiro					user = "";
89890792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
89990792Sgshapiro						  A_PERM,
90090792Sgshapiro						  macid("{auth_authen}"), NULL);
90164562Sgshapiro				}
90264562Sgshapiro				else
90364562Sgshapiro				{
90490792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
90590792Sgshapiro						  A_TEMP,
90690792Sgshapiro						  macid("{auth_authen}"), user);
90764562Sgshapiro				}
90864562Sgshapiro
90990792Sgshapiro# if 0
91064562Sgshapiro				/* get realm? */
91164562Sgshapiro				sasl_getprop(conn, SASL_REALM, (void **) &data);
91290792Sgshapiro# endif /* 0 */
91364562Sgshapiro
91464562Sgshapiro				/* get security strength (features) */
91564562Sgshapiro				result = sasl_getprop(conn, SASL_SSF,
91664562Sgshapiro						      (void **) &ssf);
91764562Sgshapiro				if (result != SASL_OK)
91864562Sgshapiro				{
91990792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
92090792Sgshapiro						  A_PERM,
92190792Sgshapiro						  macid("{auth_ssf}"), "0");
92264562Sgshapiro					ssf = NULL;
92364562Sgshapiro				}
92464562Sgshapiro				else
92564562Sgshapiro				{
92664562Sgshapiro					char pbuf[8];
92764562Sgshapiro
92890792Sgshapiro					(void) sm_snprintf(pbuf, sizeof pbuf,
92990792Sgshapiro							   "%u", *ssf);
93090792Sgshapiro					macdefine(&BlankEnvelope.e_macro,
93190792Sgshapiro						  A_TEMP,
93290792Sgshapiro						  macid("{auth_ssf}"), pbuf);
93364562Sgshapiro					if (tTd(95, 8))
93490792Sgshapiro						sm_dprintf("AUTH auth_ssf: %u\n",
93590792Sgshapiro							   *ssf);
93664562Sgshapiro				}
93790792Sgshapiro
93864562Sgshapiro				/*
93990792Sgshapiro				**  Only switch to encrypted connection
94064562Sgshapiro				**  if a security layer has been negotiated
94164562Sgshapiro				*/
94290792Sgshapiro
94364562Sgshapiro				if (ssf != NULL && *ssf > 0)
94464562Sgshapiro				{
94564562Sgshapiro					/*
94690792Sgshapiro					**  Convert I/O layer to use SASL.
94790792Sgshapiro					**  If the call fails, the connection
94890792Sgshapiro					**  is aborted.
94964562Sgshapiro					*/
95090792Sgshapiro
95190792Sgshapiro					if (sfdcsasl(&InChannel, &OutChannel,
95290792Sgshapiro						     conn) == 0)
95364562Sgshapiro					{
95464562Sgshapiro						/* restart dialogue */
95564562Sgshapiro						n_helo = 0;
95694334Sgshapiro# if PIPELINING
95790792Sgshapiro						(void) sm_io_autoflush(InChannel,
95890792Sgshapiro								       OutChannel);
95994334Sgshapiro# endif /* PIPELINING */
96064562Sgshapiro					}
96164562Sgshapiro					else
96264562Sgshapiro						syserr("503 5.3.3 SASL TLS failed");
96364562Sgshapiro				}
96490792Sgshapiro
96590792Sgshapiro				/* NULL pointer ok since it's our function */
96690792Sgshapiro				if (LogLevel > 8)
96764562Sgshapiro					sm_syslog(LOG_INFO, NOQID,
96890792Sgshapiro						  "AUTH=server, relay=%.100s, authid=%.128s, mech=%.16s, bits=%d",
96990792Sgshapiro						  CurSmtpClient,
97090792Sgshapiro						  shortenstring(user, 128),
97190792Sgshapiro						  auth_type, *ssf);
97264562Sgshapiro			}
97364562Sgshapiro			else if (result == SASL_CONTINUE)
97464562Sgshapiro			{
97564562Sgshapiro				len = ENC64LEN(outlen);
97664562Sgshapiro				out2 = xalloc(len);
97764562Sgshapiro				result = sasl_encode64(out, outlen, out2, len,
97890792Sgshapiro						       &out2len);
97964562Sgshapiro				if (result != SASL_OK)
98064562Sgshapiro				{
98164562Sgshapiro					/* correct code? XXX */
98264562Sgshapiro					/* 454 Temp. authentication failure */
98364562Sgshapiro					message("454 4.5.4 Internal error: unable to encode64");
98464562Sgshapiro					if (LogLevel > 5)
98564562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
98690792Sgshapiro							  "AUTH encode64 error [%d for \"%s\"]",
98764562Sgshapiro							  result, out);
98864562Sgshapiro					/* start over? */
98964562Sgshapiro					authenticating = SASL_NOT_AUTH;
99064562Sgshapiro				}
99164562Sgshapiro				else
99264562Sgshapiro				{
99364562Sgshapiro					message("334 %s", out2);
99464562Sgshapiro					if (tTd(95, 2))
99590792Sgshapiro						sm_dprintf("AUTH continue: msg='%s' len=%u\n",
99690792Sgshapiro							   out2, out2len);
99764562Sgshapiro				}
99864562Sgshapiro			}
99964562Sgshapiro			else
100064562Sgshapiro			{
100164562Sgshapiro				/* not SASL_OK or SASL_CONT */
100264562Sgshapiro				message("500 5.7.0 authentication failed");
100364562Sgshapiro				if (LogLevel > 9)
100464562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
100590792Sgshapiro						  "AUTH failure (%s): %s (%d) %s",
100664562Sgshapiro						  auth_type,
100764562Sgshapiro						  sasl_errstring(result, NULL,
100864562Sgshapiro								 NULL),
100990792Sgshapiro						  result,
101090792Sgshapiro						  errstr == NULL ? "" : errstr);
101164562Sgshapiro				authenticating = SASL_NOT_AUTH;
101264562Sgshapiro			}
101364562Sgshapiro		}
101464562Sgshapiro		else
101564562Sgshapiro		{
101664562Sgshapiro			/* don't want to do any of this if authenticating */
101790792Sgshapiro#endif /* SASL */
101864562Sgshapiro
101938032Speter		/* echo command to transcript */
102038032Speter		if (e->e_xfp != NULL)
102190792Sgshapiro			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
102290792Sgshapiro					     "<<< %s\n", inp);
102338032Speter
102490792Sgshapiro		if (LogLevel > 14)
102590792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
102638032Speter
102738032Speter		/* break off command */
102838032Speter		for (p = inp; isascii(*p) && isspace(*p); p++)
102938032Speter			continue;
103038032Speter		cmd = cmdbuf;
103138032Speter		while (*p != '\0' &&
103238032Speter		       !(isascii(*p) && isspace(*p)) &&
103338032Speter		       cmd < &cmdbuf[sizeof cmdbuf - 2])
103438032Speter			*cmd++ = *p++;
103538032Speter		*cmd = '\0';
103638032Speter
103738032Speter		/* throw away leading whitespace */
103890792Sgshapiro		SKIP_SPACE(p);
103938032Speter
104038032Speter		/* decode command */
104164562Sgshapiro		for (c = CmdTab; c->cmd_name != NULL; c++)
104238032Speter		{
104390792Sgshapiro			if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
104438032Speter				break;
104538032Speter		}
104638032Speter
104738032Speter		/* reset errors */
104838032Speter		errno = 0;
104938032Speter
105090792Sgshapiro		/* check whether a "non-null" command has been used */
105190792Sgshapiro		switch (c->cmd_code)
105290792Sgshapiro		{
105390792Sgshapiro#if SASL
105490792Sgshapiro		  case CMDAUTH:
105590792Sgshapiro			/* avoid information leak; take first two words? */
105690792Sgshapiro			q = "AUTH";
105790792Sgshapiro			break;
105890792Sgshapiro#endif /* SASL */
105990792Sgshapiro
106090792Sgshapiro		  case CMDMAIL:
106190792Sgshapiro		  case CMDEXPN:
106290792Sgshapiro		  case CMDVRFY:
106390792Sgshapiro		  case CMDETRN:
106490792Sgshapiro			lognullconnection = false;
106590792Sgshapiro			/* FALLTHROUGH */
106690792Sgshapiro		  default:
106790792Sgshapiro			q = inp;
106890792Sgshapiro			break;
106990792Sgshapiro		}
107090792Sgshapiro
107190792Sgshapiro		if (e->e_id == NULL)
107290792Sgshapiro			sm_setproctitle(true, e, "%s: %.80s",
107390792Sgshapiro					CurSmtpClient, q);
107490792Sgshapiro		else
107590792Sgshapiro			sm_setproctitle(true, e, "%s %s: %.80s",
107690792Sgshapiro					qid_printname(e),
107790792Sgshapiro					CurSmtpClient, q);
107890792Sgshapiro
107938032Speter		/*
108038032Speter		**  Process command.
108138032Speter		**
108238032Speter		**	If we are running as a null server, return 550
108390792Sgshapiro		**	to almost everything.
108438032Speter		*/
108538032Speter
108664562Sgshapiro		if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
108738032Speter		{
108864562Sgshapiro			switch (c->cmd_code)
108938032Speter			{
109038032Speter			  case CMDQUIT:
109138032Speter			  case CMDHELO:
109238032Speter			  case CMDEHLO:
109338032Speter			  case CMDNOOP:
109464562Sgshapiro			  case CMDRSET:
109538032Speter				/* process normally */
109638032Speter				break;
109738032Speter
109864562Sgshapiro			  case CMDETRN:
109964562Sgshapiro				if (bitnset(D_ETRNONLY, d_flags) &&
110064562Sgshapiro				    nullserver == NULL)
110164562Sgshapiro					break;
110290792Sgshapiro				DELAY_CONN("ETRN");
110380785Sgshapiro				/* FALLTHROUGH */
110464562Sgshapiro
110538032Speter			  default:
110690792Sgshapiro#if MAXBADCOMMANDS > 0
110790792Sgshapiro				/* theoretically this could overflow */
110890792Sgshapiro				if (nullserver != NULL &&
110990792Sgshapiro				    ++n_badcmds > MAXBADCOMMANDS)
111064562Sgshapiro				{
111190792Sgshapiro					message("421 4.7.0 %s Too many bad commands; closing connection",
111290792Sgshapiro						MyHostName);
111390792Sgshapiro
111490792Sgshapiro					/* arrange to ignore send list */
111590792Sgshapiro					e->e_sendqueue = NULL;
111690792Sgshapiro					goto doquit;
111764562Sgshapiro				}
111890792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
111964562Sgshapiro				if (nullserver != NULL)
112064562Sgshapiro				{
112164562Sgshapiro					if (ISSMTPREPLY(nullserver))
112264562Sgshapiro						usrerr(nullserver);
112364562Sgshapiro					else
112490792Sgshapiro						usrerr("550 5.0.0 %s",
112590792Sgshapiro						       nullserver);
112664562Sgshapiro				}
112764562Sgshapiro				else
112864562Sgshapiro					usrerr("452 4.4.5 Insufficient disk space; try again later");
112938032Speter				continue;
113038032Speter			}
113138032Speter		}
113238032Speter
113364562Sgshapiro		switch (c->cmd_code)
113438032Speter		{
113590792Sgshapiro#if SASL
113664562Sgshapiro		  case CMDAUTH: /* sasl */
113790792Sgshapiro			DELAY_CONN("AUTH");
113890792Sgshapiro			if (!sasl_ok || n_mechs <= 0)
113964562Sgshapiro			{
114064562Sgshapiro				message("503 5.3.3 AUTH not available");
114164562Sgshapiro				break;
114264562Sgshapiro			}
114364562Sgshapiro			if (authenticating == SASL_IS_AUTH)
114464562Sgshapiro			{
114564562Sgshapiro				message("503 5.5.0 Already Authenticated");
114664562Sgshapiro				break;
114764562Sgshapiro			}
114890792Sgshapiro			if (smtp.sm_gotmail)
114964562Sgshapiro			{
115064562Sgshapiro				message("503 5.5.0 AUTH not permitted during a mail transaction");
115164562Sgshapiro				break;
115264562Sgshapiro			}
115364562Sgshapiro			if (tempfail)
115464562Sgshapiro			{
115564562Sgshapiro				if (LogLevel > 9)
115664562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
115764562Sgshapiro						  "SMTP AUTH command (%.100s) from %.100s tempfailed (due to previous checks)",
115864562Sgshapiro						  p, CurSmtpClient);
115964562Sgshapiro				usrerr("454 4.7.1 Please try again later");
116064562Sgshapiro				break;
116164562Sgshapiro			}
116264562Sgshapiro
116390792Sgshapiro			ismore = false;
116464562Sgshapiro
116564562Sgshapiro			/* crude way to avoid crack attempts */
116690792Sgshapiro			(void) checksmtpattack(&n_auth, n_mechs + 1, true,
116764562Sgshapiro					       "AUTH", e);
116864562Sgshapiro
116990792Sgshapiro			/* make sure mechanism (p) is a valid string */
117064562Sgshapiro			for (q = p; *q != '\0' && isascii(*q); q++)
117164562Sgshapiro			{
117264562Sgshapiro				if (isspace(*q))
117364562Sgshapiro				{
117464562Sgshapiro					*q = '\0';
117564562Sgshapiro					while (*++q != '\0' &&
117664562Sgshapiro					       isascii(*q) && isspace(*q))
117764562Sgshapiro						continue;
117864562Sgshapiro					*(q - 1) = '\0';
117964562Sgshapiro					ismore = (*q != '\0');
118064562Sgshapiro					break;
118164562Sgshapiro				}
118264562Sgshapiro			}
118364562Sgshapiro
118464562Sgshapiro			/* check whether mechanism is available */
118564562Sgshapiro			if (iteminlist(p, mechlist, " ") == NULL)
118664562Sgshapiro			{
118790792Sgshapiro				message("503 5.3.3 AUTH mechanism %.32s not available",
118864562Sgshapiro					p);
118964562Sgshapiro				break;
119064562Sgshapiro			}
119164562Sgshapiro
119264562Sgshapiro			if (ismore)
119364562Sgshapiro			{
119464562Sgshapiro				/* could this be shorter? XXX */
119590792Sgshapiro				in = sm_rpool_malloc(e->e_rpool, strlen(q));
119664562Sgshapiro				result = sasl_decode64(q, strlen(q), in,
119790792Sgshapiro						       &inlen);
119864562Sgshapiro				if (result != SASL_OK)
119964562Sgshapiro				{
120064562Sgshapiro					message("501 5.5.4 cannot BASE64 decode '%s'",
120164562Sgshapiro						q);
120264562Sgshapiro					if (LogLevel > 5)
120364562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
120490792Sgshapiro							  "AUTH decode64 error [%d for \"%s\"]",
120564562Sgshapiro							  result, q);
120664562Sgshapiro					/* start over? */
120764562Sgshapiro					authenticating = SASL_NOT_AUTH;
120864562Sgshapiro					in = NULL;
120964562Sgshapiro					inlen = 0;
121064562Sgshapiro					break;
121164562Sgshapiro				}
121264562Sgshapiro			}
121364562Sgshapiro			else
121464562Sgshapiro			{
121564562Sgshapiro				in = NULL;
121664562Sgshapiro				inlen = 0;
121764562Sgshapiro			}
121864562Sgshapiro
121964562Sgshapiro			/* see if that auth type exists */
122064562Sgshapiro			result = sasl_server_start(conn, p, in, inlen,
122164562Sgshapiro						   &out, &outlen, &errstr);
122264562Sgshapiro
122364562Sgshapiro			if (result != SASL_OK && result != SASL_CONTINUE)
122464562Sgshapiro			{
122564562Sgshapiro				message("500 5.7.0 authentication failed");
122664562Sgshapiro				if (LogLevel > 9)
122764562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
122890792Sgshapiro						  "AUTH failure (%s): %s (%d) %s",
122964562Sgshapiro						  p,
123064562Sgshapiro						  sasl_errstring(result, NULL,
123164562Sgshapiro								 NULL),
123290792Sgshapiro						  result,
123390792Sgshapiro						  errstr);
123464562Sgshapiro				break;
123564562Sgshapiro			}
123664562Sgshapiro			auth_type = newstr(p);
123764562Sgshapiro
123864562Sgshapiro			if (result == SASL_OK)
123964562Sgshapiro			{
124064562Sgshapiro				/* ugly, but same code */
124164562Sgshapiro				goto authenticated;
124264562Sgshapiro				/* authenticated by the initial response */
124364562Sgshapiro			}
124464562Sgshapiro
124564562Sgshapiro			/* len is at least 2 */
124664562Sgshapiro			len = ENC64LEN(outlen);
124764562Sgshapiro			out2 = xalloc(len);
124864562Sgshapiro			result = sasl_encode64(out, outlen, out2, len,
124990792Sgshapiro					       &out2len);
125064562Sgshapiro
125164562Sgshapiro			if (result != SASL_OK)
125264562Sgshapiro			{
125364562Sgshapiro				message("454 4.5.4 Temporary authentication failure");
125464562Sgshapiro				if (LogLevel > 5)
125564562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
125690792Sgshapiro						  "AUTH encode64 error [%d for \"%s\"]",
125764562Sgshapiro						  result, out);
125864562Sgshapiro
125964562Sgshapiro				/* start over? */
126064562Sgshapiro				authenticating = SASL_NOT_AUTH;
126164562Sgshapiro			}
126264562Sgshapiro			else
126364562Sgshapiro			{
126464562Sgshapiro				message("334 %s", out2);
126564562Sgshapiro				authenticating = SASL_PROC_AUTH;
126664562Sgshapiro			}
126764562Sgshapiro			break;
126890792Sgshapiro#endif /* SASL */
126964562Sgshapiro
127090792Sgshapiro#if STARTTLS
127164562Sgshapiro		  case CMDSTLS: /* starttls */
127290792Sgshapiro			DELAY_CONN("STARTTLS");
127364562Sgshapiro			if (*p != '\0')
127464562Sgshapiro			{
127564562Sgshapiro				message("501 5.5.2 Syntax error (no parameters allowed)");
127664562Sgshapiro				break;
127764562Sgshapiro			}
127890792Sgshapiro			if (!bitset(SRV_OFFER_TLS, features))
127964562Sgshapiro			{
128064562Sgshapiro				message("503 5.5.0 TLS not available");
128164562Sgshapiro				break;
128264562Sgshapiro			}
128377349Sgshapiro			if (!tls_ok_srv)
128464562Sgshapiro			{
128564562Sgshapiro				message("454 4.3.3 TLS not available after start");
128664562Sgshapiro				break;
128764562Sgshapiro			}
128890792Sgshapiro			if (smtp.sm_gotmail)
128964562Sgshapiro			{
129064562Sgshapiro				message("503 5.5.0 TLS not permitted during a mail transaction");
129164562Sgshapiro				break;
129264562Sgshapiro			}
129364562Sgshapiro			if (tempfail)
129464562Sgshapiro			{
129564562Sgshapiro				if (LogLevel > 9)
129664562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
129764562Sgshapiro						  "SMTP STARTTLS command (%.100s) from %.100s tempfailed (due to previous checks)",
129864562Sgshapiro						  p, CurSmtpClient);
129964562Sgshapiro				usrerr("454 4.7.1 Please try again later");
130064562Sgshapiro				break;
130164562Sgshapiro			}
130290792Sgshapiro# if _FFR_SMTP_SSL
130390792Sgshapiro  starttls:
130490792Sgshapiro# endif /* _FFR_SMTP_SSL */
130564562Sgshapiro# if TLS_NO_RSA
130664562Sgshapiro			/*
130764562Sgshapiro			**  XXX do we need a temp key ?
130864562Sgshapiro			*/
130964562Sgshapiro# else /* TLS_NO_RSA */
131064562Sgshapiro# endif /* TLS_NO_RSA */
131190792Sgshapiro
131290792Sgshapiro# if TLS_VRFY_PER_CTX
131390792Sgshapiro			/*
131490792Sgshapiro			**  Note: this sets the verification globally
131590792Sgshapiro			**  (per SSL_CTX)
131690792Sgshapiro			**  it's ok since it applies only to one transaction
131790792Sgshapiro			*/
131890792Sgshapiro
131990792Sgshapiro			TLS_VERIFY_CLIENT();
132090792Sgshapiro# endif /* TLS_VRFY_PER_CTX */
132190792Sgshapiro
132264562Sgshapiro			if (srv_ssl != NULL)
132364562Sgshapiro				SSL_clear(srv_ssl);
132464562Sgshapiro			else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
132564562Sgshapiro			{
132664562Sgshapiro				message("454 4.3.3 TLS not available: error generating SSL handle");
132790792Sgshapiro# if _FFR_SMTP_SSL
132890792Sgshapiro				goto tls_done;
132990792Sgshapiro# else /* _FFR_SMTP_SSL */
133064562Sgshapiro				break;
133190792Sgshapiro# endif /* _FFR_SMTP_SSL */
133264562Sgshapiro			}
133390792Sgshapiro
133490792Sgshapiro# if !TLS_VRFY_PER_CTX
133590792Sgshapiro			/*
133690792Sgshapiro			**  this could be used if it were possible to set
133790792Sgshapiro			**  verification per SSL (connection)
133890792Sgshapiro			**  not just per SSL_CTX (global)
133990792Sgshapiro			*/
134090792Sgshapiro
134190792Sgshapiro			TLS_VERIFY_CLIENT();
134290792Sgshapiro# endif /* !TLS_VRFY_PER_CTX */
134390792Sgshapiro
134490792Sgshapiro			rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
134590792Sgshapiro			wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
134690792Sgshapiro
134766494Sgshapiro			if (rfd < 0 || wfd < 0 ||
134866494Sgshapiro			    SSL_set_rfd(srv_ssl, rfd) <= 0 ||
134966494Sgshapiro			    SSL_set_wfd(srv_ssl, wfd) <= 0)
135064562Sgshapiro			{
135164562Sgshapiro				message("454 4.3.3 TLS not available: error set fd");
135264562Sgshapiro				SSL_free(srv_ssl);
135364562Sgshapiro				srv_ssl = NULL;
135490792Sgshapiro# if _FFR_SMTP_SSL
135590792Sgshapiro				goto tls_done;
135690792Sgshapiro# else /* _FFR_SMTP_SSL */
135764562Sgshapiro				break;
135890792Sgshapiro# endif /* _FFR_SMTP_SSL */
135964562Sgshapiro			}
136090792Sgshapiro# if _FFR_SMTP_SSL
136190792Sgshapiro			if (!smtps)
136290792Sgshapiro# endif /* _FFR_SMTP_SSL */
136390792Sgshapiro				message("220 2.0.0 Ready to start TLS");
136490792Sgshapiro# if PIPELINING
136590792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
136690792Sgshapiro# endif /* PIPELINING */
136790792Sgshapiro
136864562Sgshapiro			SSL_set_accept_state(srv_ssl);
136964562Sgshapiro
137064562Sgshapiro#  define SSL_ACC(s)	SSL_accept(s)
137190792Sgshapiro
137290792Sgshapiro			tlsstart = curtime();
137390792Sgshapiro  ssl_retry:
137464562Sgshapiro			if ((r = SSL_ACC(srv_ssl)) <= 0)
137564562Sgshapiro			{
137664562Sgshapiro				int i;
137790792Sgshapiro				bool timedout;
137890792Sgshapiro				time_t left;
137990792Sgshapiro				time_t now = curtime();
138090792Sgshapiro				struct timeval tv;
138164562Sgshapiro
138264562Sgshapiro				/* what to do in this case? */
138364562Sgshapiro				i = SSL_get_error(srv_ssl, r);
138490792Sgshapiro
138590792Sgshapiro				/*
138690792Sgshapiro				**  For SSL_ERROR_WANT_{READ,WRITE}:
138790792Sgshapiro				**  There is no SSL record available yet
138890792Sgshapiro				**  or there is only a partial SSL record
138990792Sgshapiro				**  removed from the network (socket) buffer
139090792Sgshapiro				**  into the SSL buffer. The SSL_accept will
139190792Sgshapiro				**  only succeed when a full SSL record is
139290792Sgshapiro				**  available (assuming a "real" error
139390792Sgshapiro				**  doesn't happen). To handle when a "real"
139490792Sgshapiro				**  error does happen the select is set for
139590792Sgshapiro				**  exceptions too.
139690792Sgshapiro				**  The connection may be re-negotiated
139790792Sgshapiro				**  during this time so both read and write
139890792Sgshapiro				**  "want errors" need to be handled.
139990792Sgshapiro				**  A select() exception loops back so that
140090792Sgshapiro				**  a proper SSL error message can be gotten.
140190792Sgshapiro				*/
140290792Sgshapiro
140390792Sgshapiro				left = TimeOuts.to_starttls - (now - tlsstart);
140490792Sgshapiro				timedout = left <= 0;
140590792Sgshapiro				if (!timedout)
140690792Sgshapiro				{
140790792Sgshapiro					tv.tv_sec = left;
140890792Sgshapiro					tv.tv_usec = 0;
140990792Sgshapiro				}
141090792Sgshapiro
141190792Sgshapiro				/* XXX what about SSL_pending() ? */
141290792Sgshapiro				if (!timedout && i == SSL_ERROR_WANT_READ)
141390792Sgshapiro				{
141490792Sgshapiro					fd_set ssl_maskr, ssl_maskx;
141590792Sgshapiro
141690792Sgshapiro					FD_ZERO(&ssl_maskr);
141790792Sgshapiro					FD_SET(rfd, &ssl_maskr);
141890792Sgshapiro					FD_ZERO(&ssl_maskx);
141990792Sgshapiro					FD_SET(rfd, &ssl_maskx);
142090792Sgshapiro					if (select(rfd + 1, &ssl_maskr, NULL,
142190792Sgshapiro						   &ssl_maskx, &tv) > 0)
142290792Sgshapiro						goto ssl_retry;
142390792Sgshapiro				}
142490792Sgshapiro				if (!timedout && i == SSL_ERROR_WANT_WRITE)
142590792Sgshapiro				{
142690792Sgshapiro					fd_set ssl_maskw, ssl_maskx;
142790792Sgshapiro
142890792Sgshapiro					FD_ZERO(&ssl_maskw);
142990792Sgshapiro					FD_SET(wfd, &ssl_maskw);
143090792Sgshapiro					FD_ZERO(&ssl_maskx);
143190792Sgshapiro					FD_SET(rfd, &ssl_maskx);
143290792Sgshapiro					if (select(wfd + 1, NULL, &ssl_maskw,
143390792Sgshapiro						   &ssl_maskx, &tv) > 0)
143490792Sgshapiro						goto ssl_retry;
143590792Sgshapiro				}
143664562Sgshapiro				if (LogLevel > 5)
143764562Sgshapiro				{
143890792Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
143990792Sgshapiro						  "STARTTLS=server, error: accept failed=%d, SSL_error=%d, timedout=%d",
144090792Sgshapiro						  r, i, (int) timedout);
144190792Sgshapiro					if (LogLevel > 8)
144290792Sgshapiro						tlslogerr("server");
144364562Sgshapiro				}
144490792Sgshapiro				tls_ok_srv = false;
144564562Sgshapiro				SSL_free(srv_ssl);
144664562Sgshapiro				srv_ssl = NULL;
144764562Sgshapiro
144864562Sgshapiro				/*
144964562Sgshapiro				**  according to the next draft of
145064562Sgshapiro				**  RFC 2487 the connection should be dropped
145164562Sgshapiro				*/
145264562Sgshapiro
145364562Sgshapiro				/* arrange to ignore any current send list */
145464562Sgshapiro				e->e_sendqueue = NULL;
145564562Sgshapiro				goto doquit;
145664562Sgshapiro			}
145764562Sgshapiro
145864562Sgshapiro			/* ignore return code for now, it's in {verify} */
145990792Sgshapiro			(void) tls_get_info(srv_ssl, true,
146090792Sgshapiro					    CurSmtpClient,
146190792Sgshapiro					    &BlankEnvelope.e_macro,
146290792Sgshapiro					    bitset(SRV_VRFY_CLT, features));
146364562Sgshapiro
146464562Sgshapiro			/*
146564562Sgshapiro			**  call Stls_client to find out whether
146664562Sgshapiro			**  to accept the connection from the client
146764562Sgshapiro			*/
146864562Sgshapiro
146964562Sgshapiro			saveQuickAbort = QuickAbort;
147064562Sgshapiro			saveSuprErrs = SuprErrs;
147190792Sgshapiro			SuprErrs = true;
147290792Sgshapiro			QuickAbort = false;
147364562Sgshapiro			if (rscheck("tls_client",
147490792Sgshapiro				     macvalue(macid("{verify}"), e),
147590792Sgshapiro				     "STARTTLS", e, true, true, 5,
147690792Sgshapiro				     NULL, NOQID) != EX_OK ||
147790792Sgshapiro			    Errors > 0)
147864562Sgshapiro			{
147964562Sgshapiro				extern char MsgBuf[];
148064562Sgshapiro
148164562Sgshapiro				if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
148264562Sgshapiro					nullserver = newstr(MsgBuf);
148364562Sgshapiro				else
148464562Sgshapiro					nullserver = "503 5.7.0 Authentication required.";
148564562Sgshapiro			}
148664562Sgshapiro			QuickAbort = saveQuickAbort;
148764562Sgshapiro			SuprErrs = saveSuprErrs;
148864562Sgshapiro
148990792Sgshapiro			tls_ok_srv = false;	/* don't offer STARTTLS again */
149064562Sgshapiro			n_helo = 0;
149190792Sgshapiro# if SASL
149264562Sgshapiro			if (sasl_ok)
149364562Sgshapiro			{
149464562Sgshapiro				char *s;
149564562Sgshapiro
149690792Sgshapiro				s = macvalue(macid("{cipher_bits}"), e);
149790792Sgshapiro				if (s != NULL && (ext_ssf.ssf = atoi(s)) > 0)
149864562Sgshapiro				{
149990792Sgshapiro					ext_ssf.auth_id = macvalue(macid("{cert_subject}"),
150064562Sgshapiro								   e);
150164562Sgshapiro					sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
150264562Sgshapiro							       &ext_ssf) == SASL_OK;
150364562Sgshapiro					mechlist = NULL;
150464562Sgshapiro					if (sasl_ok)
150564562Sgshapiro						n_mechs = saslmechs(conn,
150664562Sgshapiro								    &mechlist);
150764562Sgshapiro				}
150864562Sgshapiro			}
150990792Sgshapiro# endif /* SASL */
151064562Sgshapiro
151164562Sgshapiro			/* switch to secure connection */
151290792Sgshapiro			if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
151390792Sgshapiro			{
151490792Sgshapiro				tls_active = true;
151590792Sgshapiro# if PIPELINING
151690792Sgshapiro				(void) sm_io_autoflush(InChannel, OutChannel);
151790792Sgshapiro# endif /* PIPELINING */
151890792Sgshapiro			}
151964562Sgshapiro			else
152064562Sgshapiro			{
152164562Sgshapiro				/*
152264562Sgshapiro				**  XXX this is an internal error
152364562Sgshapiro				**  how to deal with it?
152464562Sgshapiro				**  we can't generate an error message
152564562Sgshapiro				**  since the other side switched to an
152664562Sgshapiro				**  encrypted layer, but we could not...
152764562Sgshapiro				**  just "hang up"?
152864562Sgshapiro				*/
152990792Sgshapiro
153064562Sgshapiro				nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
153190792Sgshapiro				syserr("STARTTLS: can't switch to encrypted layer");
153264562Sgshapiro			}
153390792Sgshapiro# if _FFR_SMTP_SSL
153490792Sgshapiro		  tls_done:
153590792Sgshapiro			if (smtps)
153690792Sgshapiro			{
153790792Sgshapiro				if (tls_active)
153890792Sgshapiro					goto greeting;
153990792Sgshapiro				else
154090792Sgshapiro					goto doquit;
154190792Sgshapiro			}
154290792Sgshapiro# endif /* _FFR_SMTP_SSL */
154364562Sgshapiro			break;
154490792Sgshapiro#endif /* STARTTLS */
154564562Sgshapiro
154638032Speter		  case CMDHELO:		/* hello -- introduce yourself */
154738032Speter		  case CMDEHLO:		/* extended hello */
154890792Sgshapiro			DELAY_CONN("EHLO");
154964562Sgshapiro			if (c->cmd_code == CMDEHLO)
155038032Speter			{
155138032Speter				protocol = "ESMTP";
155238032Speter				SmtpPhase = "server EHLO";
155338032Speter			}
155438032Speter			else
155538032Speter			{
155638032Speter				protocol = "SMTP";
155738032Speter				SmtpPhase = "server HELO";
155838032Speter			}
155938032Speter
156038032Speter			/* avoid denial-of-service */
156190792Sgshapiro			(void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, true,
156264562Sgshapiro					       "HELO/EHLO", e);
156338032Speter
156490792Sgshapiro#if 0
156590792Sgshapiro			/* RFC2821 4.1.4 allows duplicate HELO/EHLO */
156638032Speter			/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
156738032Speter			if (gothello)
156838032Speter			{
156938032Speter				usrerr("503 %s Duplicate HELO/EHLO",
157073188Sgshapiro				       MyHostName);
157138032Speter				break;
157238032Speter			}
157390792Sgshapiro#endif /* 0 */
157438032Speter
157538032Speter			/* check for valid domain name (re 1123 5.2.5) */
157638032Speter			if (*p == '\0' && !AllowBogusHELO)
157738032Speter			{
157838032Speter				usrerr("501 %s requires domain address",
157938032Speter					cmdbuf);
158038032Speter				break;
158138032Speter			}
158238032Speter
158338032Speter			/* check for long domain name (hides Received: info) */
158438032Speter			if (strlen(p) > MAXNAME)
158538032Speter			{
158638032Speter				usrerr("501 Invalid domain name");
158764562Sgshapiro				if (LogLevel > 9)
158864562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
158964562Sgshapiro						  "invalid domain name (too long) from %.100s",
159064562Sgshapiro						  CurSmtpClient);
159138032Speter				break;
159238032Speter			}
159338032Speter
159438032Speter			for (q = p; *q != '\0'; q++)
159538032Speter			{
159638032Speter				if (!isascii(*q))
159738032Speter					break;
159838032Speter				if (isalnum(*q))
159938032Speter					continue;
160038032Speter				if (isspace(*q))
160138032Speter				{
160238032Speter					*q = '\0';
160338032Speter					break;
160438032Speter				}
160538032Speter				if (strchr("[].-_#", *q) == NULL)
160638032Speter					break;
160738032Speter			}
160864562Sgshapiro
160938032Speter			if (*q == '\0')
161038032Speter			{
161138032Speter				q = "pleased to meet you";
161290792Sgshapiro				sendinghost = sm_strdup_x(p);
161338032Speter			}
161438032Speter			else if (!AllowBogusHELO)
161538032Speter			{
161638032Speter				usrerr("501 Invalid domain name");
161764562Sgshapiro				if (LogLevel > 9)
161864562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
161964562Sgshapiro						  "invalid domain name (%.100s) from %.100s",
162064562Sgshapiro						  p, CurSmtpClient);
162138032Speter				break;
162238032Speter			}
162338032Speter			else
162438032Speter			{
162538032Speter				q = "accepting invalid domain name";
162638032Speter			}
162738032Speter
162890792Sgshapiro			if (gothello)
162990792Sgshapiro			{
163090792Sgshapiro				CLEAR_STATE(cmdbuf);
163164562Sgshapiro
163290792Sgshapiro#if _FFR_QUARANTINE
163390792Sgshapiro				/* restore connection quarantining */
163490792Sgshapiro				if (smtp.sm_quarmsg == NULL)
163590792Sgshapiro				{
163690792Sgshapiro					e->e_quarmsg = NULL;
163790792Sgshapiro					macdefine(&e->e_macro, A_PERM,
163890792Sgshapiro						  macid("{quarantine}"), "");
163990792Sgshapiro				}
164090792Sgshapiro				else
164190792Sgshapiro				{
164290792Sgshapiro					e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
164390792Sgshapiro									 smtp.sm_quarmsg);
164490792Sgshapiro					macdefine(&e->e_macro, A_PERM,
164590792Sgshapiro						  macid("{quarantine}"),
164690792Sgshapiro						  e->e_quarmsg);
164790792Sgshapiro				}
164890792Sgshapiro#endif /* _FFR_QUARANTINE */
164990792Sgshapiro			}
165090792Sgshapiro
165190792Sgshapiro#if MILTER
165290792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
165390792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
165464562Sgshapiro			{
165564562Sgshapiro				char state;
165664562Sgshapiro				char *response;
165764562Sgshapiro
165864562Sgshapiro				response = milter_helo(p, e, &state);
165964562Sgshapiro				switch (state)
166064562Sgshapiro				{
166164562Sgshapiro				  case SMFIR_REPLYCODE:
166290792Sgshapiro					if (MilterLogLevel > 3)
166390792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
166490792Sgshapiro							  "Milter: helo=%s, reject=%s",
166590792Sgshapiro							  p, response);
166690792Sgshapiro					nullserver = newstr(response);
166790792Sgshapiro					smtp.sm_milterize = false;
166864562Sgshapiro					break;
166964562Sgshapiro
167064562Sgshapiro				  case SMFIR_REJECT:
167190792Sgshapiro					if (MilterLogLevel > 3)
167290792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
167390792Sgshapiro							  "Milter: helo=%s, reject=Command rejected",
167490792Sgshapiro							  p);
167564562Sgshapiro					nullserver = "Command rejected";
167690792Sgshapiro					smtp.sm_milterize = false;
167764562Sgshapiro					break;
167864562Sgshapiro
167964562Sgshapiro				  case SMFIR_TEMPFAIL:
168090792Sgshapiro					if (MilterLogLevel > 3)
168190792Sgshapiro						sm_syslog(LOG_INFO, e->e_id,
168290792Sgshapiro							  "Milter: helo=%s, reject=%s",
168390792Sgshapiro							  p, MSG_TEMPFAIL);
168490792Sgshapiro					tempfail = true;
168590792Sgshapiro					smtp.sm_milterize = false;
168664562Sgshapiro					break;
168764562Sgshapiro				}
168890792Sgshapiro				if (response != NULL)
168990792Sgshapiro					sm_free(response);
169090792Sgshapiro
169194334Sgshapiro# if _FFR_QUARANTINE
169290792Sgshapiro				/*
169390792Sgshapiro				**  If quarantining by a connect/ehlo action,
169490792Sgshapiro				**  save between messages
169590792Sgshapiro				*/
169690792Sgshapiro
169790792Sgshapiro				if (smtp.sm_quarmsg == NULL &&
169890792Sgshapiro				    e->e_quarmsg != NULL)
169990792Sgshapiro					smtp.sm_quarmsg = newstr(e->e_quarmsg);
170094334Sgshapiro# endif /* _FFR_QUARANTINE */
170164562Sgshapiro			}
170290792Sgshapiro#endif /* MILTER */
170390792Sgshapiro			gothello = true;
170464562Sgshapiro
170538032Speter			/* print HELO response message */
170664562Sgshapiro			if (c->cmd_code != CMDEHLO)
170738032Speter			{
170838032Speter				message("250 %s Hello %s, %s",
170938032Speter					MyHostName, CurSmtpClient, q);
171038032Speter				break;
171138032Speter			}
171238032Speter
171338032Speter			message("250-%s Hello %s, %s",
171438032Speter				MyHostName, CurSmtpClient, q);
171538032Speter
171664562Sgshapiro			/* offer ENHSC even for nullserver */
171764562Sgshapiro			if (nullserver != NULL)
171864562Sgshapiro			{
171964562Sgshapiro				message("250 ENHANCEDSTATUSCODES");
172064562Sgshapiro				break;
172164562Sgshapiro			}
172264562Sgshapiro
172366494Sgshapiro			/*
172466494Sgshapiro			**  print EHLO features list
172566494Sgshapiro			**
172666494Sgshapiro			**  Note: If you change this list,
172790792Sgshapiro			**	  remember to update 'helpfile'
172866494Sgshapiro			*/
172966494Sgshapiro
173064562Sgshapiro			message("250-ENHANCEDSTATUSCODES");
173190792Sgshapiro#if PIPELINING
173290792Sgshapiro			if (bitset(SRV_OFFER_PIPE, features))
173390792Sgshapiro				message("250-PIPELINING");
173490792Sgshapiro#endif /* PIPELINING */
173590792Sgshapiro			if (bitset(SRV_OFFER_EXPN, features))
173638032Speter			{
173738032Speter				message("250-EXPN");
173890792Sgshapiro				if (bitset(SRV_OFFER_VERB, features))
173938032Speter					message("250-VERB");
174038032Speter			}
174190792Sgshapiro#if MIME8TO7
174238032Speter			message("250-8BITMIME");
174390792Sgshapiro#endif /* MIME8TO7 */
174438032Speter			if (MaxMessageSize > 0)
174538032Speter				message("250-SIZE %ld", MaxMessageSize);
174638032Speter			else
174738032Speter				message("250-SIZE");
174890792Sgshapiro#if DSN
174990792Sgshapiro			if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
175038032Speter				message("250-DSN");
175190792Sgshapiro#endif /* DSN */
175290792Sgshapiro			if (bitset(SRV_OFFER_ETRN, features))
175338032Speter				message("250-ETRN");
175490792Sgshapiro#if SASL
175564562Sgshapiro			if (sasl_ok && mechlist != NULL && *mechlist != '\0')
175664562Sgshapiro				message("250-AUTH %s", mechlist);
175790792Sgshapiro#endif /* SASL */
175890792Sgshapiro#if STARTTLS
175990792Sgshapiro			if (tls_ok_srv && bitset(SRV_OFFER_TLS, features))
176064562Sgshapiro				message("250-STARTTLS");
176190792Sgshapiro#endif /* STARTTLS */
176290792Sgshapiro			if (DeliverByMin > 0)
176390792Sgshapiro				message("250-DELIVERBY %ld",
176490792Sgshapiro					(long) DeliverByMin);
176590792Sgshapiro			else if (DeliverByMin == 0)
176690792Sgshapiro				message("250-DELIVERBY");
176790792Sgshapiro
176890792Sgshapiro			/* < 0: no deliver-by */
176990792Sgshapiro
177038032Speter			message("250 HELP");
177138032Speter			break;
177238032Speter
177338032Speter		  case CMDMAIL:		/* mail -- designate sender */
177438032Speter			SmtpPhase = "server MAIL";
177590792Sgshapiro			DELAY_CONN("MAIL");
177638032Speter
177738032Speter			/* check for validity of this command */
177838032Speter			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
177938032Speter			{
178064562Sgshapiro				usrerr("503 5.0.0 Polite people say HELO first");
178138032Speter				break;
178238032Speter			}
178390792Sgshapiro			if (smtp.sm_gotmail)
178438032Speter			{
178564562Sgshapiro				usrerr("503 5.5.0 Sender already specified");
178638032Speter				break;
178738032Speter			}
178890792Sgshapiro#if SASL
178990792Sgshapiro			if (bitset(SRV_REQ_AUTH, features) &&
179064562Sgshapiro			    authenticating != SASL_IS_AUTH)
179164562Sgshapiro			{
179264562Sgshapiro				usrerr("530 5.7.0 Authentication required");
179364562Sgshapiro				break;
179464562Sgshapiro			}
179590792Sgshapiro#endif /* SASL */
179638032Speter
179764562Sgshapiro			p = skipword(p, "from");
179864562Sgshapiro			if (p == NULL)
179964562Sgshapiro				break;
180064562Sgshapiro			if (tempfail)
180164562Sgshapiro			{
180264562Sgshapiro				if (LogLevel > 9)
180364562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
180464562Sgshapiro						  "SMTP MAIL command (%.100s) from %.100s tempfailed (due to previous checks)",
180564562Sgshapiro						  p, CurSmtpClient);
180690792Sgshapiro				usrerr(MSG_TEMPFAIL);
180764562Sgshapiro				break;
180864562Sgshapiro			}
180964562Sgshapiro
181038032Speter			/* make sure we know who the sending host is */
181138032Speter			if (sendinghost == NULL)
181238032Speter				sendinghost = peerhostname;
181338032Speter
181438032Speter
181590792Sgshapiro#if SM_HEAP_CHECK
181690792Sgshapiro			if (sm_debug_active(&DebugLeakSmtp, 1))
181790792Sgshapiro			{
181890792Sgshapiro				sm_heap_newgroup();
181990792Sgshapiro				sm_dprintf("smtp() heap group #%d\n",
182090792Sgshapiro					sm_heap_group());
182190792Sgshapiro			}
182290792Sgshapiro#endif /* SM_HEAP_CHECK */
182364562Sgshapiro
182438032Speter			if (Errors > 0)
182590792Sgshapiro				goto undo_no_pm;
182638032Speter			if (!gothello)
182738032Speter			{
182890792Sgshapiro				auth_warning(e, "%s didn't use HELO protocol",
182990792Sgshapiro					     CurSmtpClient);
183038032Speter			}
183190792Sgshapiro#ifdef PICKY_HELO_CHECK
183290792Sgshapiro			if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
183390792Sgshapiro			    (sm_strcasecmp(peerhostname, "localhost") != 0 ||
183490792Sgshapiro			     sm_strcasecmp(sendinghost, MyHostName) != 0))
183538032Speter			{
183638032Speter				auth_warning(e, "Host %s claimed to be %s",
183790792Sgshapiro					     CurSmtpClient, sendinghost);
183838032Speter			}
183990792Sgshapiro#endif /* PICKY_HELO_CHECK */
184038032Speter
184138032Speter			if (protocol == NULL)
184238032Speter				protocol = "SMTP";
184390792Sgshapiro			macdefine(&e->e_macro, A_PERM, 'r', protocol);
184490792Sgshapiro			macdefine(&e->e_macro, A_PERM, 's', sendinghost);
184564562Sgshapiro
184638032Speter			if (Errors > 0)
184790792Sgshapiro				goto undo_no_pm;
184890792Sgshapiro			smtp.sm_nrcpts = 0;
184990792Sgshapiro			n_badrcpts = 0;
185090792Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
185190792Sgshapiro			macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
185264562Sgshapiro			e->e_flags |= EF_CLRQUEUE;
185390792Sgshapiro			sm_setproctitle(true, e, "%s %s: %.80s",
185464562Sgshapiro					qid_printname(e),
185564562Sgshapiro					CurSmtpClient, inp);
185638032Speter
185790792Sgshapiro			/* do the processing */
185890792Sgshapiro		    SM_TRY
185990792Sgshapiro		    {
186094334Sgshapiro			extern char *FullName;
186194334Sgshapiro
186290792Sgshapiro			QuickAbort = true;
186394334Sgshapiro			SM_FREE_CLR(FullName);
186464562Sgshapiro
186538032Speter			/* must parse sender first */
186638032Speter			delimptr = NULL;
186790792Sgshapiro			setsender(p, e, &delimptr, ' ', false);
186838032Speter			if (delimptr != NULL && *delimptr != '\0')
186938032Speter				*delimptr++ = '\0';
187038032Speter			if (Errors > 0)
187190792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
187238032Speter
187364562Sgshapiro			/* Successfully set e_from, allow logging */
187464562Sgshapiro			e->e_flags |= EF_LOGSENDER;
187538032Speter
187664562Sgshapiro			/* put resulting triple from parseaddr() into macros */
187764562Sgshapiro			if (e->e_from.q_mailer != NULL)
187890792Sgshapiro				 macdefine(&e->e_macro, A_PERM,
187990792Sgshapiro					macid("{mail_mailer}"),
188090792Sgshapiro					e->e_from.q_mailer->m_name);
188164562Sgshapiro			else
188290792Sgshapiro				 macdefine(&e->e_macro, A_PERM,
188390792Sgshapiro					macid("{mail_mailer}"), NULL);
188464562Sgshapiro			if (e->e_from.q_host != NULL)
188590792Sgshapiro				macdefine(&e->e_macro, A_PERM,
188690792Sgshapiro					macid("{mail_host}"),
188790792Sgshapiro					e->e_from.q_host);
188864562Sgshapiro			else
188990792Sgshapiro				macdefine(&e->e_macro, A_PERM,
189090792Sgshapiro					macid("{mail_host}"), "localhost");
189164562Sgshapiro			if (e->e_from.q_user != NULL)
189290792Sgshapiro				macdefine(&e->e_macro, A_PERM,
189390792Sgshapiro					macid("{mail_addr}"),
189490792Sgshapiro					e->e_from.q_user);
189564562Sgshapiro			else
189690792Sgshapiro				macdefine(&e->e_macro, A_PERM,
189790792Sgshapiro					macid("{mail_addr}"), NULL);
189864562Sgshapiro			if (Errors > 0)
189990792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
190064562Sgshapiro
190138032Speter			/* check for possible spoofing */
190238032Speter			if (RealUid != 0 && OpMode == MD_SMTP &&
190338032Speter			    !wordinclass(RealUserName, 't') &&
190464562Sgshapiro			    (!bitnset(M_LOCALMAILER,
190564562Sgshapiro				      e->e_from.q_mailer->m_flags) ||
190664562Sgshapiro			     strcmp(e->e_from.q_user, RealUserName) != 0))
190738032Speter			{
190838032Speter				auth_warning(e, "%s owned process doing -bs",
190938032Speter					RealUserName);
191038032Speter			}
191138032Speter
191238032Speter			/* now parse ESMTP arguments */
191338032Speter			e->e_msgsize = 0;
191464562Sgshapiro			addr = p;
191564562Sgshapiro			argno = 0;
191664562Sgshapiro			args[argno++] = p;
191738032Speter			p = delimptr;
191838032Speter			while (p != NULL && *p != '\0')
191938032Speter			{
192038032Speter				char *kp;
192138032Speter				char *vp = NULL;
192264562Sgshapiro				char *equal = NULL;
192338032Speter
192438032Speter				/* locate the beginning of the keyword */
192590792Sgshapiro				SKIP_SPACE(p);
192638032Speter				if (*p == '\0')
192738032Speter					break;
192838032Speter				kp = p;
192938032Speter
193038032Speter				/* skip to the value portion */
193138032Speter				while ((isascii(*p) && isalnum(*p)) || *p == '-')
193238032Speter					p++;
193338032Speter				if (*p == '=')
193438032Speter				{
193564562Sgshapiro					equal = p;
193638032Speter					*p++ = '\0';
193738032Speter					vp = p;
193838032Speter
193938032Speter					/* skip to the end of the value */
194038032Speter					while (*p != '\0' && *p != ' ' &&
194138032Speter					       !(isascii(*p) && iscntrl(*p)) &&
194238032Speter					       *p != '=')
194338032Speter						p++;
194438032Speter				}
194538032Speter
194638032Speter				if (*p != '\0')
194738032Speter					*p++ = '\0';
194838032Speter
194938032Speter				if (tTd(19, 1))
195090792Sgshapiro					sm_dprintf("MAIL: got arg %s=\"%s\"\n", kp,
195138032Speter						vp == NULL ? "<null>" : vp);
195238032Speter
195338032Speter				mail_esmtp_args(kp, vp, e);
195464562Sgshapiro				if (equal != NULL)
195564562Sgshapiro					*equal = '=';
195664562Sgshapiro				args[argno++] = kp;
195764562Sgshapiro				if (argno >= MAXSMTPARGS - 1)
195864562Sgshapiro					usrerr("501 5.5.4 Too many parameters");
195938032Speter				if (Errors > 0)
196090792Sgshapiro					sm_exc_raisenew_x(&EtypeQuickAbort, 1);
196138032Speter			}
196264562Sgshapiro			args[argno] = NULL;
196338032Speter			if (Errors > 0)
196490792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
196538032Speter
196690792Sgshapiro#if SASL
196790792Sgshapiro# if _FFR_AUTH_PASSING
196890792Sgshapiro			/* set the default AUTH= if the sender didn't */
196990792Sgshapiro			if (e->e_auth_param == NULL)
197090792Sgshapiro			{
197190792Sgshapiro				/* XXX only do this for an MSA? */
197290792Sgshapiro				e->e_auth_param = macvalue(macid("{auth_authen}"),
197390792Sgshapiro							   e);
197490792Sgshapiro				if (e->e_auth_param == NULL)
197590792Sgshapiro					e->e_auth_param = "<>";
197690792Sgshapiro
197790792Sgshapiro				/*
197890792Sgshapiro				**  XXX should we invoke Strust_auth now?
197990792Sgshapiro				**  authorizing as the client that just
198090792Sgshapiro				**  authenticated, so we'll trust implicitly
198190792Sgshapiro				*/
198290792Sgshapiro			}
198390792Sgshapiro# endif /* _FFR_AUTH_PASSING */
198490792Sgshapiro#endif /* SASL */
198590792Sgshapiro
198664562Sgshapiro			/* do config file checking of the sender */
198790792Sgshapiro			macdefine(&e->e_macro, A_PERM,
198890792Sgshapiro				macid("{addr_type}"), "e s");
198990792Sgshapiro#if _FFR_MAIL_MACRO
199090792Sgshapiro			/* make the "real" sender address available */
199190792Sgshapiro			macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
199290792Sgshapiro				  e->e_from.q_paddr);
199390792Sgshapiro#endif /* _FFR_MAIL_MACRO */
199464562Sgshapiro			if (rscheck("check_mail", addr,
199590792Sgshapiro				    NULL, e, true, true, 3, NULL,
199690792Sgshapiro				    e->e_id) != EX_OK ||
199764562Sgshapiro			    Errors > 0)
199890792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
199990792Sgshapiro			macdefine(&e->e_macro, A_PERM,
200090792Sgshapiro				  macid("{addr_type}"), NULL);
200164562Sgshapiro
200266494Sgshapiro			if (MaxMessageSize > 0 &&
200377349Sgshapiro			    (e->e_msgsize > MaxMessageSize ||
200477349Sgshapiro			     e->e_msgsize < 0))
200538032Speter			{
200664562Sgshapiro				usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
200738032Speter					MaxMessageSize);
200890792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
200938032Speter			}
201064562Sgshapiro
201190792Sgshapiro			/*
201290792Sgshapiro			**  XXX always check whether there is at least one fs
201390792Sgshapiro			**  with enough space?
201490792Sgshapiro			**  However, this may not help much: the queue group
201590792Sgshapiro			**  selection may later on select a FS that hasn't
201690792Sgshapiro			**  enough space.
201790792Sgshapiro			*/
201890792Sgshapiro
201990792Sgshapiro			if ((NumFileSys == 1 || NumQueue == 1) &&
202090792Sgshapiro			    !enoughdiskspace(e->e_msgsize, e)
202190792Sgshapiro#if _FFR_ANY_FREE_FS
202290792Sgshapiro			    && !filesys_free(e->e_msgsize)
202390792Sgshapiro#endif /* _FFR_ANY_FREE_FS */
202490792Sgshapiro			   )
202538032Speter			{
202690792Sgshapiro				/*
202790792Sgshapiro				**  We perform this test again when the
202890792Sgshapiro				**  queue directory is selected, in collect.
202990792Sgshapiro				*/
203090792Sgshapiro
203164562Sgshapiro				usrerr("452 4.4.5 Insufficient disk space; try again later");
203290792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
203338032Speter			}
203438032Speter			if (Errors > 0)
203590792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
203664562Sgshapiro
203790792Sgshapiro			LogUsrErrs = true;
203890792Sgshapiro#if MILTER
203990792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
204090792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
204164562Sgshapiro			{
204264562Sgshapiro				char state;
204364562Sgshapiro				char *response;
204464562Sgshapiro
204564562Sgshapiro				response = milter_envfrom(args, e, &state);
204690792Sgshapiro				MILTER_REPLY("from");
204764562Sgshapiro			}
204890792Sgshapiro#endif /* MILTER */
204964562Sgshapiro			if (Errors > 0)
205090792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
205164562Sgshapiro
205264562Sgshapiro			message("250 2.1.0 Sender ok");
205390792Sgshapiro			smtp.sm_gotmail = true;
205490792Sgshapiro		    }
205590792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
205690792Sgshapiro		    {
205790792Sgshapiro			/*
205890792Sgshapiro			**  An error occurred while processing a MAIL command.
205990792Sgshapiro			**  Jump to the common error handling code.
206090792Sgshapiro			*/
206190792Sgshapiro
206290792Sgshapiro			sm_exc_free(exc);
206390792Sgshapiro			goto undo_no_pm;
206490792Sgshapiro		    }
206590792Sgshapiro		    SM_END_TRY
206638032Speter			break;
206738032Speter
206890792Sgshapiro		  undo_no_pm:
206990792Sgshapiro			e->e_flags &= ~EF_PM_NOTIFY;
207090792Sgshapiro		  undo:
207190792Sgshapiro			break;
207290792Sgshapiro
207338032Speter		  case CMDRCPT:		/* rcpt -- designate recipient */
207490792Sgshapiro			DELAY_CONN("RCPT");
207590792Sgshapiro			if (!smtp.sm_gotmail)
207638032Speter			{
207764562Sgshapiro				usrerr("503 5.0.0 Need MAIL before RCPT");
207838032Speter				break;
207938032Speter			}
208038032Speter			SmtpPhase = "server RCPT";
208190792Sgshapiro		    SM_TRY
208290792Sgshapiro		    {
208390792Sgshapiro			QuickAbort = true;
208490792Sgshapiro			LogUsrErrs = true;
208538032Speter
208638032Speter			/* limit flooding of our machine */
208790792Sgshapiro			if (MaxRcptPerMsg > 0 &&
208890792Sgshapiro			    smtp.sm_nrcpts >= MaxRcptPerMsg)
208938032Speter			{
209090792Sgshapiro				/* sleep(1); / * slow down? */
209164562Sgshapiro				usrerr("452 4.5.3 Too many recipients");
209290792Sgshapiro				goto rcpt_done;
209338032Speter			}
209438032Speter
209538032Speter			if (e->e_sendmode != SM_DELIVER)
209638032Speter				e->e_flags |= EF_VRFYONLY;
209738032Speter
209890792Sgshapiro#if MILTER
209964562Sgshapiro			/*
210064562Sgshapiro			**  If the filter will be deleting recipients,
210164562Sgshapiro			**  don't expand them at RCPT time (in the call
210264562Sgshapiro			**  to recipient()).  If they are expanded, it
210364562Sgshapiro			**  is impossible for removefromlist() to figure
210464562Sgshapiro			**  out the expanded members of the original
210564562Sgshapiro			**  recipient and mark them as QS_DONTSEND.
210664562Sgshapiro			*/
210764562Sgshapiro
210864562Sgshapiro			if (milter_can_delrcpts())
210964562Sgshapiro				e->e_flags |= EF_VRFYONLY;
211090792Sgshapiro#endif /* MILTER */
211164562Sgshapiro
211238032Speter			p = skipword(p, "to");
211338032Speter			if (p == NULL)
211490792Sgshapiro				goto rcpt_done;
211590792Sgshapiro			macdefine(&e->e_macro, A_PERM,
211690792Sgshapiro				macid("{addr_type}"), "e r");
211790792Sgshapiro			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
211890792Sgshapiro				      e, true);
211990792Sgshapiro			macdefine(&e->e_macro, A_PERM,
212090792Sgshapiro				macid("{addr_type}"), NULL);
212190792Sgshapiro			if (BadRcptThrottle > 0 &&
212290792Sgshapiro			    n_badrcpts >= BadRcptThrottle)
212390792Sgshapiro			{
212490792Sgshapiro				if (LogLevel > 5 &&
212590792Sgshapiro				    n_badrcpts == BadRcptThrottle)
212690792Sgshapiro				{
212790792Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
212890792Sgshapiro						  "%.100s: Possible SMTP RCPT flood, throttling.",
212990792Sgshapiro						  CurSmtpClient);
213090792Sgshapiro
213190792Sgshapiro					/* To avoid duplicated message */
213290792Sgshapiro					n_badrcpts++;
213390792Sgshapiro				}
213490792Sgshapiro
213590792Sgshapiro				/*
213690792Sgshapiro				**  Don't use exponential backoff for now.
213790792Sgshapiro				**  Some servers will open more connections
213890792Sgshapiro				**  and actually overload the receiver even
213990792Sgshapiro				**  more.
214090792Sgshapiro				*/
214190792Sgshapiro
214290792Sgshapiro				(void) sleep(1);
214390792Sgshapiro			}
214442575Speter			if (Errors > 0)
214590792Sgshapiro				goto rcpt_done;
214642575Speter			if (a == NULL)
214742575Speter			{
214864562Sgshapiro				usrerr("501 5.0.0 Missing recipient");
214990792Sgshapiro				goto rcpt_done;
215042575Speter			}
215142575Speter
215238032Speter			if (delimptr != NULL && *delimptr != '\0')
215338032Speter				*delimptr++ = '\0';
215438032Speter
215564562Sgshapiro			/* put resulting triple from parseaddr() into macros */
215664562Sgshapiro			if (a->q_mailer != NULL)
215790792Sgshapiro				macdefine(&e->e_macro, A_PERM,
215890792Sgshapiro					macid("{rcpt_mailer}"),
215990792Sgshapiro					a->q_mailer->m_name);
216064562Sgshapiro			else
216190792Sgshapiro				macdefine(&e->e_macro, A_PERM,
216290792Sgshapiro					macid("{rcpt_mailer}"), NULL);
216364562Sgshapiro			if (a->q_host != NULL)
216490792Sgshapiro				macdefine(&e->e_macro, A_PERM,
216590792Sgshapiro					macid("{rcpt_host}"), a->q_host);
216664562Sgshapiro			else
216790792Sgshapiro				macdefine(&e->e_macro, A_PERM,
216890792Sgshapiro					macid("{rcpt_host}"), "localhost");
216964562Sgshapiro			if (a->q_user != NULL)
217090792Sgshapiro				macdefine(&e->e_macro, A_PERM,
217190792Sgshapiro					macid("{rcpt_addr}"), a->q_user);
217264562Sgshapiro			else
217390792Sgshapiro				macdefine(&e->e_macro, A_PERM,
217490792Sgshapiro					macid("{rcpt_addr}"), NULL);
217564562Sgshapiro			if (Errors > 0)
217690792Sgshapiro				goto rcpt_done;
217738032Speter
217838032Speter			/* now parse ESMTP arguments */
217964562Sgshapiro			addr = p;
218064562Sgshapiro			argno = 0;
218164562Sgshapiro			args[argno++] = p;
218238032Speter			p = delimptr;
218338032Speter			while (p != NULL && *p != '\0')
218438032Speter			{
218538032Speter				char *kp;
218638032Speter				char *vp = NULL;
218764562Sgshapiro				char *equal = NULL;
218838032Speter
218938032Speter				/* locate the beginning of the keyword */
219090792Sgshapiro				SKIP_SPACE(p);
219138032Speter				if (*p == '\0')
219238032Speter					break;
219338032Speter				kp = p;
219438032Speter
219538032Speter				/* skip to the value portion */
219638032Speter				while ((isascii(*p) && isalnum(*p)) || *p == '-')
219738032Speter					p++;
219838032Speter				if (*p == '=')
219938032Speter				{
220064562Sgshapiro					equal = p;
220138032Speter					*p++ = '\0';
220238032Speter					vp = p;
220338032Speter
220438032Speter					/* skip to the end of the value */
220538032Speter					while (*p != '\0' && *p != ' ' &&
220638032Speter					       !(isascii(*p) && iscntrl(*p)) &&
220738032Speter					       *p != '=')
220838032Speter						p++;
220938032Speter				}
221038032Speter
221138032Speter				if (*p != '\0')
221238032Speter					*p++ = '\0';
221338032Speter
221438032Speter				if (tTd(19, 1))
221590792Sgshapiro					sm_dprintf("RCPT: got arg %s=\"%s\"\n", kp,
221638032Speter						vp == NULL ? "<null>" : vp);
221738032Speter
221838032Speter				rcpt_esmtp_args(a, kp, vp, e);
221964562Sgshapiro				if (equal != NULL)
222064562Sgshapiro					*equal = '=';
222164562Sgshapiro				args[argno++] = kp;
222264562Sgshapiro				if (argno >= MAXSMTPARGS - 1)
222364562Sgshapiro					usrerr("501 5.5.4 Too many parameters");
222438032Speter				if (Errors > 0)
222538032Speter					break;
222638032Speter			}
222764562Sgshapiro			args[argno] = NULL;
222838032Speter			if (Errors > 0)
222990792Sgshapiro				goto rcpt_done;
223038032Speter
223164562Sgshapiro			/* do config file checking of the recipient */
223290792Sgshapiro			macdefine(&e->e_macro, A_PERM,
223390792Sgshapiro				macid("{addr_type}"), "e r");
223464562Sgshapiro			if (rscheck("check_rcpt", addr,
223590792Sgshapiro				    NULL, e, true, true, 3, NULL,
223690792Sgshapiro				    e->e_id) != EX_OK ||
223764562Sgshapiro			    Errors > 0)
223890792Sgshapiro				goto rcpt_done;
223990792Sgshapiro			macdefine(&e->e_macro, A_PERM,
224090792Sgshapiro				macid("{addr_type}"), NULL);
224164562Sgshapiro
224290792Sgshapiro#if MILTER
224390792Sgshapiro			if (smtp.sm_milterlist && smtp.sm_milterize &&
224490792Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
224564562Sgshapiro			{
224664562Sgshapiro				char state;
224764562Sgshapiro				char *response;
224864562Sgshapiro
224964562Sgshapiro				response = milter_envrcpt(args, e, &state);
225090792Sgshapiro				MILTER_REPLY("to");
225164562Sgshapiro			}
225290792Sgshapiro#endif /* MILTER */
225364562Sgshapiro
225490792Sgshapiro			macdefine(&e->e_macro, A_PERM,
225590792Sgshapiro				macid("{rcpt_mailer}"), NULL);
225690792Sgshapiro			macdefine(&e->e_macro, A_PERM,
225790792Sgshapiro				macid("{rcpt_relay}"), NULL);
225890792Sgshapiro			macdefine(&e->e_macro, A_PERM,
225990792Sgshapiro				macid("{rcpt_addr}"), NULL);
226090792Sgshapiro			macdefine(&e->e_macro, A_PERM,
226190792Sgshapiro				macid("{dsn_notify}"), NULL);
226264562Sgshapiro			if (Errors > 0)
226390792Sgshapiro				goto rcpt_done;
226464562Sgshapiro
226538032Speter			/* save in recipient list after ESMTP mods */
226638032Speter			a = recipient(a, &e->e_sendqueue, 0, e);
226738032Speter			if (Errors > 0)
226890792Sgshapiro				goto rcpt_done;
226938032Speter
227038032Speter			/* no errors during parsing, but might be a duplicate */
227138032Speter			e->e_to = a->q_paddr;
227264562Sgshapiro			if (!QS_IS_BADADDR(a->q_state))
227338032Speter			{
227490792Sgshapiro				if (smtp.sm_nrcpts == 0)
227564562Sgshapiro					initsys(e);
227664562Sgshapiro				message("250 2.1.5 Recipient ok%s",
227764562Sgshapiro					QS_IS_QUEUEUP(a->q_state) ?
227838032Speter						" (will queue)" : "");
227990792Sgshapiro				smtp.sm_nrcpts++;
228038032Speter			}
228138032Speter			else
228238032Speter			{
228338032Speter				/* punt -- should keep message in ADDRESS.... */
228464562Sgshapiro				usrerr("550 5.1.1 Addressee unknown");
228538032Speter			}
228690792Sgshapiro		    rcpt_done:
228790792Sgshapiro			if (Errors > 0)
228890792Sgshapiro				++n_badrcpts;
228990792Sgshapiro		    }
229090792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
229190792Sgshapiro		    {
229290792Sgshapiro			/* An exception occurred while processing RCPT */
229390792Sgshapiro			e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
229490792Sgshapiro			++n_badrcpts;
229590792Sgshapiro		    }
229690792Sgshapiro		    SM_END_TRY
229738032Speter			break;
229838032Speter
229938032Speter		  case CMDDATA:		/* data -- text of mail */
230090792Sgshapiro			DELAY_CONN("DATA");
230190792Sgshapiro			smtp_data(&smtp, e);
230238032Speter			break;
230338032Speter
230438032Speter		  case CMDRSET:		/* rset -- reset state */
230538032Speter			if (tTd(94, 100))
230664562Sgshapiro				message("451 4.0.0 Test failure");
230738032Speter			else
230864562Sgshapiro				message("250 2.0.0 Reset state");
230990792Sgshapiro			CLEAR_STATE(cmdbuf);
231090792Sgshapiro#if _FFR_QUARANTINE
231190792Sgshapiro			/* restore connection quarantining */
231290792Sgshapiro			if (smtp.sm_quarmsg == NULL)
231390792Sgshapiro			{
231490792Sgshapiro				e->e_quarmsg = NULL;
231590792Sgshapiro				macdefine(&e->e_macro, A_PERM,
231690792Sgshapiro					  macid("{quarantine}"), "");
231790792Sgshapiro			}
231890792Sgshapiro			else
231990792Sgshapiro			{
232090792Sgshapiro				e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,
232190792Sgshapiro								 smtp.sm_quarmsg);
232290792Sgshapiro				macdefine(&e->e_macro, A_PERM,
232390792Sgshapiro					  macid("{quarantine}"), e->e_quarmsg);
232490792Sgshapiro			}
232590792Sgshapiro#endif /* _FFR_QUARANTINE */
232638032Speter			break;
232738032Speter
232838032Speter		  case CMDVRFY:		/* vrfy -- verify address */
232938032Speter		  case CMDEXPN:		/* expn -- expand address */
233090792Sgshapiro			vrfy = c->cmd_code == CMDVRFY;
233190792Sgshapiro			DELAY_CONN(vrfy ? "VRFY" : "EXPN");
233264562Sgshapiro			if (tempfail)
233364562Sgshapiro			{
233464562Sgshapiro				if (LogLevel > 9)
233564562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
233664562Sgshapiro						  "SMTP %s command (%.100s) from %.100s tempfailed (due to previous checks)",
233790792Sgshapiro						  vrfy ? "VRFY" : "EXPN",
233864562Sgshapiro						  p, CurSmtpClient);
233990792Sgshapiro
234090792Sgshapiro				/* RFC 821 doesn't allow 4xy reply code */
234164562Sgshapiro				usrerr("550 5.7.1 Please try again later");
234264562Sgshapiro				break;
234364562Sgshapiro			}
234490792Sgshapiro			wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
234590792Sgshapiro					     false, vrfy ? "VRFY" : "EXPN", e);
234664562Sgshapiro			previous = curtime();
234738032Speter			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
234890792Sgshapiro				   PrivacyFlags))
234938032Speter			{
235038032Speter				if (vrfy)
235164562Sgshapiro					message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
235238032Speter				else
235364562Sgshapiro					message("502 5.7.0 Sorry, we do not allow this operation");
235438032Speter				if (LogLevel > 5)
235538032Speter					sm_syslog(LOG_INFO, e->e_id,
235664562Sgshapiro						  "%.100s: %s [rejected]",
235764562Sgshapiro						  CurSmtpClient,
235864562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
235938032Speter				break;
236038032Speter			}
236138032Speter			else if (!gothello &&
236238032Speter				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
236338032Speter						PrivacyFlags))
236438032Speter			{
236564562Sgshapiro				usrerr("503 5.0.0 I demand that you introduce yourself first");
236638032Speter				break;
236738032Speter			}
236890792Sgshapiro			if (Errors > 0)
236938032Speter				break;
237038032Speter			if (LogLevel > 5)
237190792Sgshapiro				sm_syslog(LOG_INFO, e->e_id, "%.100s: %s",
237264562Sgshapiro					  CurSmtpClient,
237364562Sgshapiro					  shortenstring(inp, MAXSHORTSTR));
237490792Sgshapiro		    SM_TRY
237590792Sgshapiro		    {
237690792Sgshapiro			QuickAbort = true;
237738032Speter			vrfyqueue = NULL;
237838032Speter			if (vrfy)
237938032Speter				e->e_flags |= EF_VRFYONLY;
238038032Speter			while (*p != '\0' && isascii(*p) && isspace(*p))
238138032Speter				p++;
238238032Speter			if (*p == '\0')
238338032Speter			{
238464562Sgshapiro				usrerr("501 5.5.2 Argument required");
238538032Speter			}
238638032Speter			else
238738032Speter			{
238864562Sgshapiro				/* do config file checking of the address */
238964562Sgshapiro				if (rscheck(vrfy ? "check_vrfy" : "check_expn",
239090792Sgshapiro					    p, NULL, e, true, false, 3, NULL,
239190792Sgshapiro					    NOQID) != EX_OK ||
239290792Sgshapiro				    Errors > 0)
239390792Sgshapiro					sm_exc_raisenew_x(&EtypeQuickAbort, 1);
239438032Speter				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
239538032Speter			}
239664562Sgshapiro			if (wt > 0)
239771345Sgshapiro			{
239871345Sgshapiro				time_t t;
239971345Sgshapiro
240071345Sgshapiro				t = wt - (curtime() - previous);
240171345Sgshapiro				if (t > 0)
240271345Sgshapiro					(void) sleep(t);
240371345Sgshapiro			}
240438032Speter			if (Errors > 0)
240590792Sgshapiro				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
240638032Speter			if (vrfyqueue == NULL)
240738032Speter			{
240864562Sgshapiro				usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
240938032Speter			}
241038032Speter			while (vrfyqueue != NULL)
241138032Speter			{
241264562Sgshapiro				if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
241364562Sgshapiro				{
241464562Sgshapiro					vrfyqueue = vrfyqueue->q_next;
241564562Sgshapiro					continue;
241664562Sgshapiro				}
241738032Speter
241864562Sgshapiro				/* see if there is more in the vrfy list */
241938032Speter				a = vrfyqueue;
242038032Speter				while ((a = a->q_next) != NULL &&
242166494Sgshapiro				       (!QS_IS_UNDELIVERED(a->q_state)))
242238032Speter					continue;
242364562Sgshapiro				printvrfyaddr(vrfyqueue, a == NULL, vrfy);
242464562Sgshapiro				vrfyqueue = a;
242538032Speter			}
242690792Sgshapiro		    }
242790792Sgshapiro		    SM_EXCEPT(exc, "[!F]*")
242890792Sgshapiro		    {
242990792Sgshapiro			/*
243090792Sgshapiro			**  An exception occurred while processing VRFY/EXPN
243190792Sgshapiro			*/
243290792Sgshapiro
243390792Sgshapiro			sm_exc_free(exc);
243490792Sgshapiro			goto undo;
243590792Sgshapiro		    }
243690792Sgshapiro		    SM_END_TRY
243738032Speter			break;
243838032Speter
243938032Speter		  case CMDETRN:		/* etrn -- force queue flush */
244090792Sgshapiro			DELAY_CONN("ETRN");
244190792Sgshapiro
244290792Sgshapiro			/* Don't leak queue information via debug flags */
244390792Sgshapiro			if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
244490792Sgshapiro			    (RealUid != 0 && RealUid != TrustedUid &&
244590792Sgshapiro			     OpMode == MD_SMTP))
244638032Speter			{
244764562Sgshapiro				/* different message for MSA ? */
244864562Sgshapiro				message("502 5.7.0 Sorry, we do not allow this operation");
244938032Speter				if (LogLevel > 5)
245038032Speter					sm_syslog(LOG_INFO, e->e_id,
245164562Sgshapiro						  "%.100s: %s [rejected]",
245264562Sgshapiro						  CurSmtpClient,
245364562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
245438032Speter				break;
245538032Speter			}
245664562Sgshapiro			if (tempfail)
245764562Sgshapiro			{
245864562Sgshapiro				if (LogLevel > 9)
245964562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
246064562Sgshapiro						  "SMTP ETRN command (%.100s) from %.100s tempfailed (due to previous checks)",
246164562Sgshapiro						  p, CurSmtpClient);
246290792Sgshapiro				usrerr(MSG_TEMPFAIL);
246364562Sgshapiro				break;
246464562Sgshapiro			}
246538032Speter
246638032Speter			if (strlen(p) <= 0)
246738032Speter			{
246864562Sgshapiro				usrerr("500 5.5.2 Parameter required");
246938032Speter				break;
247038032Speter			}
247138032Speter
247238032Speter			/* crude way to avoid denial-of-service attacks */
247390792Sgshapiro			(void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, true,
247464562Sgshapiro					     "ETRN", e);
247538032Speter
247690792Sgshapiro			/*
247790792Sgshapiro			**  Do config file checking of the parameter.
247890792Sgshapiro			**  Even though we have srv_features now, we still
247990792Sgshapiro			**  need this ruleset because the former is called
248090792Sgshapiro			**  when the connection has been established, while
248190792Sgshapiro			**  this ruleset is called when the command is
248290792Sgshapiro			**  actually issued and therefore has all information
248390792Sgshapiro			**  available to make a decision.
248490792Sgshapiro			*/
248590792Sgshapiro
248690792Sgshapiro			if (rscheck("check_etrn", p, NULL, e, true, false, 3,
248790792Sgshapiro				    NULL, NOQID) != EX_OK || Errors > 0)
248864562Sgshapiro				break;
248964562Sgshapiro
249038032Speter			if (LogLevel > 5)
249138032Speter				sm_syslog(LOG_INFO, e->e_id,
249290792Sgshapiro					  "%.100s: ETRN %s", CurSmtpClient,
249364562Sgshapiro					  shortenstring(p, MAXSHORTSTR));
249438032Speter
249538032Speter			id = p;
249690792Sgshapiro			if (*id == '#')
249790792Sgshapiro			{
249890792Sgshapiro				int wgrp;
249990792Sgshapiro
250090792Sgshapiro				id++;
250190792Sgshapiro				wgrp = name2qid(id);
250290792Sgshapiro				if (!ISVALIDQGRP(wgrp))
250390792Sgshapiro				{
250490792Sgshapiro					usrerr("459 4.5.4 Queue %s unknown",
250590792Sgshapiro					       id);
250690792Sgshapiro					break;
250790792Sgshapiro				}
250890792Sgshapiro				ok = run_work_group(wgrp, true, false,
250990792Sgshapiro						    false, true);
251090792Sgshapiro				if (ok && Errors == 0)
251190792Sgshapiro					message("250 2.0.0 Queuing for queue group %s started", id);
251290792Sgshapiro				break;
251390792Sgshapiro			}
251490792Sgshapiro
251538032Speter			if (*id == '@')
251638032Speter				id++;
251738032Speter			else
251838032Speter				*--id = '@';
251964562Sgshapiro
252090792Sgshapiro			new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
252190792Sgshapiro			if (new == NULL)
252290792Sgshapiro			{
252390792Sgshapiro				syserr("500 5.5.0 ETRN out of memory");
252490792Sgshapiro				break;
252590792Sgshapiro			}
252638032Speter			new->queue_match = id;
252790792Sgshapiro			new->queue_negate = false;
252838032Speter			new->queue_next = NULL;
252938032Speter			QueueLimitRecipient = new;
253090792Sgshapiro			ok = runqueue(true, false, false, true);
253190792Sgshapiro			sm_free(QueueLimitRecipient); /* XXX */
253238032Speter			QueueLimitRecipient = NULL;
253338032Speter			if (ok && Errors == 0)
253464562Sgshapiro				message("250 2.0.0 Queuing for node %s started", p);
253538032Speter			break;
253638032Speter
253738032Speter		  case CMDHELP:		/* help -- give user info */
253890792Sgshapiro			DELAY_CONN("HELP");
253964562Sgshapiro			help(p, e);
254038032Speter			break;
254138032Speter
254238032Speter		  case CMDNOOP:		/* noop -- do nothing */
254390792Sgshapiro			DELAY_CONN("NOOP");
254490792Sgshapiro			(void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true,
254564562Sgshapiro					       "NOOP", e);
254664562Sgshapiro			message("250 2.0.0 OK");
254738032Speter			break;
254838032Speter
254938032Speter		  case CMDQUIT:		/* quit -- leave mail */
255064562Sgshapiro			message("221 2.0.0 %s closing connection", MyHostName);
255190792Sgshapiro#if PIPELINING
255290792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
255390792Sgshapiro#endif /* PIPELINING */
255438032Speter
255590792Sgshapiro			if (smtp.sm_nrcpts > 0)
255690792Sgshapiro				logundelrcpts(e, "aborted by sender", 9, false);
255790792Sgshapiro
255838032Speter			/* arrange to ignore any current send list */
255938032Speter			e->e_sendqueue = NULL;
256038032Speter
256190792Sgshapiro#if STARTTLS
256264562Sgshapiro			/* shutdown TLS connection */
256364562Sgshapiro			if (tls_active)
256464562Sgshapiro			{
256564562Sgshapiro				(void) endtls(srv_ssl, "server");
256690792Sgshapiro				tls_active = false;
256764562Sgshapiro			}
256890792Sgshapiro#endif /* STARTTLS */
256990792Sgshapiro#if SASL
257064562Sgshapiro			if (authenticating == SASL_IS_AUTH)
257164562Sgshapiro			{
257264562Sgshapiro				sasl_dispose(&conn);
257364562Sgshapiro				authenticating = SASL_NOT_AUTH;
257490792Sgshapiro				/* XXX sasl_done(); this is a child */
257564562Sgshapiro			}
257690792Sgshapiro#endif /* SASL */
257764562Sgshapiro
257864562Sgshapirodoquit:
257938032Speter			/* avoid future 050 messages */
258038032Speter			disconnect(1, e);
258138032Speter
258290792Sgshapiro#if MILTER
258364562Sgshapiro			/* close out milter filters */
258464562Sgshapiro			milter_quit(e);
258590792Sgshapiro#endif /* MILTER */
258664562Sgshapiro
258764562Sgshapiro			if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
258864562Sgshapiro				logsender(e, NULL);
258964562Sgshapiro			e->e_flags &= ~EF_LOGSENDER;
259064562Sgshapiro
259138032Speter			if (lognullconnection && LogLevel > 5)
259264562Sgshapiro			{
259364562Sgshapiro				char *d;
259464562Sgshapiro
259590792Sgshapiro				d = macvalue(macid("{daemon_name}"), e);
259664562Sgshapiro				if (d == NULL)
259764562Sgshapiro					d = "stdin";
259890792Sgshapiro
259990792Sgshapiro				/*
260090792Sgshapiro				**  even though this id is "bogus", it makes
260190792Sgshapiro				**  it simpler to "grep" related events, e.g.,
260290792Sgshapiro				**  timeouts for the same connection.
260390792Sgshapiro				*/
260490792Sgshapiro
260590792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
260690792Sgshapiro					  "%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
260764562Sgshapiro					  CurSmtpClient, d);
260864562Sgshapiro			}
260990792Sgshapiro#if PROFILING
261090792Sgshapiro			return;
261190792Sgshapiro#endif /* PROFILING */
261290792Sgshapiro			finis(true, true, ExitStat);
261364562Sgshapiro			/* NOTREACHED */
261438032Speter
261538032Speter		  case CMDVERB:		/* set verbose mode */
261690792Sgshapiro			DELAY_CONN("VERB");
261738032Speter			if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
261890792Sgshapiro			    !bitset(SRV_OFFER_VERB, features) ||
261938032Speter			    bitset(PRIV_NOVERB, PrivacyFlags))
262038032Speter			{
262138032Speter				/* this would give out the same info */
262264562Sgshapiro				message("502 5.7.0 Verbose unavailable");
262338032Speter				break;
262438032Speter			}
262590792Sgshapiro			(void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, true,
262664562Sgshapiro					       "VERB", e);
262738032Speter			Verbose = 1;
262864562Sgshapiro			set_delivery_mode(SM_DELIVER, e);
262964562Sgshapiro			message("250 2.0.0 Verbose mode");
263038032Speter			break;
263138032Speter
263290792Sgshapiro#if SMTPDEBUG
263338032Speter		  case CMDDBGQSHOW:	/* show queues */
263490792Sgshapiro			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
263590792Sgshapiro					     "Send Queue=");
263690792Sgshapiro			printaddr(e->e_sendqueue, true);
263738032Speter			break;
263838032Speter
263938032Speter		  case CMDDBGDEBUG:	/* set debug mode */
264038032Speter			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
264138032Speter			tTflag(p);
264264562Sgshapiro			message("200 2.0.0 Debug set");
264338032Speter			break;
264438032Speter
264590792Sgshapiro#else /* SMTPDEBUG */
264638032Speter		  case CMDDBGQSHOW:	/* show queues */
264738032Speter		  case CMDDBGDEBUG:	/* set debug mode */
264890792Sgshapiro#endif /* SMTPDEBUG */
264938032Speter		  case CMDLOGBOGUS:	/* bogus command */
265090792Sgshapiro			DELAY_CONN("Bogus");
265138032Speter			if (LogLevel > 0)
265238032Speter				sm_syslog(LOG_CRIT, e->e_id,
265364562Sgshapiro					  "\"%s\" command from %.100s (%.100s)",
265464562Sgshapiro					  c->cmd_name, CurSmtpClient,
265564562Sgshapiro					  anynet_ntoa(&RealHostAddr));
265664562Sgshapiro			/* FALLTHROUGH */
265738032Speter
265838032Speter		  case CMDERROR:	/* unknown command */
265990792Sgshapiro#if MAXBADCOMMANDS > 0
266090792Sgshapiro			if (++n_badcmds > MAXBADCOMMANDS)
266138032Speter			{
266264562Sgshapiro				message("421 4.7.0 %s Too many bad commands; closing connection",
266338032Speter					MyHostName);
266464562Sgshapiro
266564562Sgshapiro				/* arrange to ignore any current send list */
266664562Sgshapiro				e->e_sendqueue = NULL;
266738032Speter				goto doquit;
266838032Speter			}
266990792Sgshapiro#endif /* MAXBADCOMMANDS > 0 */
267038032Speter
267164562Sgshapiro			usrerr("500 5.5.1 Command unrecognized: \"%s\"",
267264562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
267338032Speter			break;
267438032Speter
267564562Sgshapiro		  case CMDUNIMPL:
267690792Sgshapiro			DELAY_CONN("Unimpl");
267764562Sgshapiro			usrerr("502 5.5.1 Command not implemented: \"%s\"",
267864562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
267964562Sgshapiro			break;
268064562Sgshapiro
268138032Speter		  default:
268290792Sgshapiro			DELAY_CONN("default");
268338032Speter			errno = 0;
268464562Sgshapiro			syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
268538032Speter			break;
268638032Speter		}
268790792Sgshapiro#if SASL
268864562Sgshapiro		}
268990792Sgshapiro#endif /* SASL */
269090792Sgshapiro	    }
269190792Sgshapiro	    SM_EXCEPT(exc, "[!F]*")
269290792Sgshapiro	    {
269390792Sgshapiro		/*
269490792Sgshapiro		**  The only possible exception is "E:mta.quickabort".
269590792Sgshapiro		**  There is nothing to do except fall through and loop.
269690792Sgshapiro		*/
269790792Sgshapiro	    }
269890792Sgshapiro	    SM_END_TRY
269938032Speter	}
270090792Sgshapiro}
270190792Sgshapiro/*
270290792Sgshapiro**  SMTP_DATA -- implement the SMTP DATA command.
270390792Sgshapiro**
270490792Sgshapiro**	Parameters:
270590792Sgshapiro**		smtp -- status of SMTP connection.
270690792Sgshapiro**		e -- envelope.
270790792Sgshapiro**
270890792Sgshapiro**	Returns:
270990792Sgshapiro**		none.
271090792Sgshapiro**
271190792Sgshapiro**	Side Effects:
271290792Sgshapiro**		possibly sends message.
271390792Sgshapiro*/
271464562Sgshapiro
271590792Sgshapirostatic void
271690792Sgshapirosmtp_data(smtp, e)
271790792Sgshapiro	SMTP_T *smtp;
271890792Sgshapiro	ENVELOPE *e;
271990792Sgshapiro{
272090792Sgshapiro#if MILTER
272190792Sgshapiro	bool milteraccept;
272290792Sgshapiro#endif /* MILTER */
272390792Sgshapiro	bool aborting;
272490792Sgshapiro	bool doublequeue;
272590792Sgshapiro	ADDRESS *a;
272690792Sgshapiro	ENVELOPE *ee;
272790792Sgshapiro	char *id;
272890792Sgshapiro	char buf[32];
272990792Sgshapiro
273090792Sgshapiro	SmtpPhase = "server DATA";
273190792Sgshapiro	if (!smtp->sm_gotmail)
273290792Sgshapiro	{
273390792Sgshapiro		usrerr("503 5.0.0 Need MAIL command");
273490792Sgshapiro		return;
273590792Sgshapiro	}
273690792Sgshapiro	else if (smtp->sm_nrcpts <= 0)
273790792Sgshapiro	{
273890792Sgshapiro		usrerr("503 5.0.0 Need RCPT (recipient)");
273990792Sgshapiro		return;
274090792Sgshapiro	}
274190792Sgshapiro	(void) sm_snprintf(buf, sizeof buf, "%u", smtp->sm_nrcpts);
274290792Sgshapiro	if (rscheck("check_data", buf, NULL, e,
274390792Sgshapiro		    true, false, 3, NULL, e->e_id) != EX_OK)
274490792Sgshapiro		return;
274590792Sgshapiro
274690792Sgshapiro	/* put back discard bit */
274790792Sgshapiro	if (smtp->sm_discard)
274890792Sgshapiro		e->e_flags |= EF_DISCARD;
274990792Sgshapiro
275090792Sgshapiro	/* check to see if we need to re-expand aliases */
275190792Sgshapiro	/* also reset QS_BADADDR on already-diagnosted addrs */
275290792Sgshapiro	doublequeue = false;
275390792Sgshapiro	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
275490792Sgshapiro	{
275590792Sgshapiro		if (QS_IS_VERIFIED(a->q_state) &&
275690792Sgshapiro		    !bitset(EF_DISCARD, e->e_flags))
275790792Sgshapiro		{
275890792Sgshapiro			/* need to re-expand aliases */
275990792Sgshapiro			doublequeue = true;
276090792Sgshapiro		}
276190792Sgshapiro		if (QS_IS_BADADDR(a->q_state))
276290792Sgshapiro		{
276390792Sgshapiro			/* make this "go away" */
276490792Sgshapiro			a->q_state = QS_DONTSEND;
276590792Sgshapiro		}
276690792Sgshapiro	}
276790792Sgshapiro
276890792Sgshapiro	/* collect the text of the message */
276990792Sgshapiro	SmtpPhase = "collect";
277090792Sgshapiro	buffer_errors();
277190792Sgshapiro
277290792Sgshapiro#if _FFR_ADAPTIVE_EOL
277390792Sgshapiro	/* triggers error in collect, disabled for now */
277490792Sgshapiro	if (smtp->sm_crlf)
277590792Sgshapiro		e->e_flags |= EF_NL_NOT_EOL;
277690792Sgshapiro#endif /* _FFR_ADAPTIVE_EOL */
277790792Sgshapiro
277890792Sgshapiro	collect(InChannel, true, NULL, e);
277990792Sgshapiro
278090792Sgshapiro	/* redefine message size */
278190792Sgshapiro	(void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
278290792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
278390792Sgshapiro
278490792Sgshapiro#if _FFR_CHECK_EOM
278590792Sgshapiro	/* rscheck() will set Errors or EF_DISCARD if it trips */
278690792Sgshapiro	(void) rscheck("check_eom", buf, NULL, e, false,
278790792Sgshapiro		       true, 3, NULL, e->e_id);
278890792Sgshapiro#endif /* _FFR_CHECK_EOM */
278990792Sgshapiro
279090792Sgshapiro#if MILTER
279190792Sgshapiro	milteraccept = true;
279290792Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize &&
279390792Sgshapiro	    Errors <= 0 &&
279490792Sgshapiro	    !bitset(EF_DISCARD, e->e_flags))
279590792Sgshapiro	{
279690792Sgshapiro		char state;
279790792Sgshapiro		char *response;
279890792Sgshapiro
279990792Sgshapiro		response = milter_data(e, &state);
280090792Sgshapiro		switch (state)
280190792Sgshapiro		{
280290792Sgshapiro		  case SMFIR_REPLYCODE:
280390792Sgshapiro			if (MilterLogLevel > 3)
280490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
280590792Sgshapiro					  "Milter: data, reject=%s",
280690792Sgshapiro					  response);
280790792Sgshapiro			milteraccept = false;
280890792Sgshapiro			usrerr(response);
280990792Sgshapiro			break;
281090792Sgshapiro
281190792Sgshapiro		  case SMFIR_REJECT:
281290792Sgshapiro			milteraccept = false;
281390792Sgshapiro			if (MilterLogLevel > 3)
281490792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
281590792Sgshapiro					  "Milter: data, reject=554 5.7.1 Command rejected");
281690792Sgshapiro			usrerr("554 5.7.1 Command rejected");
281790792Sgshapiro			break;
281890792Sgshapiro
281990792Sgshapiro		  case SMFIR_DISCARD:
282090792Sgshapiro			if (MilterLogLevel > 3)
282190792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
282290792Sgshapiro					  "Milter: data, discard");
282390792Sgshapiro			milteraccept = false;
282490792Sgshapiro			e->e_flags |= EF_DISCARD;
282590792Sgshapiro			break;
282690792Sgshapiro
282790792Sgshapiro		  case SMFIR_TEMPFAIL:
282890792Sgshapiro			if (MilterLogLevel > 3)
282990792Sgshapiro				sm_syslog(LOG_INFO, e->e_id,
283090792Sgshapiro					  "Milter: data, reject=%s",
283190792Sgshapiro					  MSG_TEMPFAIL);
283290792Sgshapiro			milteraccept = false;
283390792Sgshapiro			usrerr(MSG_TEMPFAIL);
283490792Sgshapiro			break;
283590792Sgshapiro		}
283690792Sgshapiro		if (response != NULL)
283790792Sgshapiro			sm_free(response);
283890792Sgshapiro	}
283990792Sgshapiro
284090792Sgshapiro	/* Milter may have changed message size */
284190792Sgshapiro	(void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
284290792Sgshapiro	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
284390792Sgshapiro
284490792Sgshapiro	/* abort message filters that didn't get the body & log msg is OK */
284590792Sgshapiro	if (smtp->sm_milterlist && smtp->sm_milterize)
284690792Sgshapiro	{
284790792Sgshapiro		milter_abort(e);
284890792Sgshapiro		if (milteraccept && MilterLogLevel > 9)
284990792Sgshapiro			sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
285090792Sgshapiro	}
285190792Sgshapiro#endif /* MILTER */
285290792Sgshapiro
285390792Sgshapiro#if _FFR_QUARANTINE
285490792Sgshapiro	/* Check if quarantining stats should be updated */
285590792Sgshapiro	if (e->e_quarmsg != NULL)
285690792Sgshapiro		markstats(e, NULL, STATS_QUARANTINE);
285790792Sgshapiro#endif /* _FFR_QUARANTINE */
285890792Sgshapiro
285990792Sgshapiro	/*
286090792Sgshapiro	**  If a header/body check (header checks or milter)
286190792Sgshapiro	**  set EF_DISCARD, don't queueup the message --
286290792Sgshapiro	**  that would lose the EF_DISCARD bit and deliver
286390792Sgshapiro	**  the message.
286490792Sgshapiro	*/
286590792Sgshapiro
286690792Sgshapiro	if (bitset(EF_DISCARD, e->e_flags))
286790792Sgshapiro		doublequeue = false;
286890792Sgshapiro
286990792Sgshapiro	aborting = Errors > 0;
287090792Sgshapiro	if (!aborting &&
287190792Sgshapiro#if _FFR_QUARANTINE
287290792Sgshapiro	    (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
287390792Sgshapiro#endif /* _FFR_QUARANTINE */
287490792Sgshapiro	    !split_by_recipient(e))
287590792Sgshapiro		aborting = bitset(EF_FATALERRS, e->e_flags);
287690792Sgshapiro
287790792Sgshapiro	if (aborting)
287890792Sgshapiro	{
287990792Sgshapiro		/* Log who the mail would have gone to */
288090792Sgshapiro		logundelrcpts(e, e->e_message, 8, false);
288190792Sgshapiro		flush_errors(true);
288290792Sgshapiro		buffer_errors();
288390792Sgshapiro		goto abortmessage;
288490792Sgshapiro	}
288590792Sgshapiro
288690792Sgshapiro	/* from now on, we have to operate silently */
288790792Sgshapiro	buffer_errors();
288890792Sgshapiro
288990792Sgshapiro#if 0
289090792Sgshapiro	/*
289190792Sgshapiro	**  Clear message, it may contain an error from the SMTP dialogue.
289290792Sgshapiro	**  This error must not show up in the queue.
289390792Sgshapiro	**	Some error message should show up, e.g., alias database
289490792Sgshapiro	**	not available, but others shouldn't, e.g., from check_rcpt.
289590792Sgshapiro	*/
289690792Sgshapiro
289790792Sgshapiro	e->e_message = NULL;
289890792Sgshapiro#endif /* 0 */
289990792Sgshapiro
290090792Sgshapiro	/*
290190792Sgshapiro	**  Arrange to send to everyone.
290290792Sgshapiro	**	If sending to multiple people, mail back
290390792Sgshapiro	**		errors rather than reporting directly.
290490792Sgshapiro	**	In any case, don't mail back errors for
290590792Sgshapiro	**		anything that has happened up to
290690792Sgshapiro	**		now (the other end will do this).
290790792Sgshapiro	**	Truncate our transcript -- the mail has gotten
290890792Sgshapiro	**		to us successfully, and if we have
290990792Sgshapiro	**		to mail this back, it will be easier
291090792Sgshapiro	**		on the reader.
291190792Sgshapiro	**	Then send to everyone.
291290792Sgshapiro	**	Finally give a reply code.  If an error has
291390792Sgshapiro	**		already been given, don't mail a
291490792Sgshapiro	**		message back.
291590792Sgshapiro	**	We goose error returns by clearing error bit.
291690792Sgshapiro	*/
291790792Sgshapiro
291890792Sgshapiro	SmtpPhase = "delivery";
291990792Sgshapiro	(void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
292090792Sgshapiro	id = e->e_id;
292190792Sgshapiro
292290792Sgshapiro#if NAMED_BIND
292390792Sgshapiro	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
292490792Sgshapiro	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
292590792Sgshapiro#endif /* NAMED_BIND */
292690792Sgshapiro
292790792Sgshapiro	for (ee = e; ee != NULL; ee = ee->e_sibling)
292890792Sgshapiro	{
292990792Sgshapiro		/* make sure we actually do delivery */
293090792Sgshapiro		ee->e_flags &= ~EF_CLRQUEUE;
293190792Sgshapiro
293290792Sgshapiro		/* from now on, operate silently */
293390792Sgshapiro		ee->e_errormode = EM_MAIL;
293490792Sgshapiro
293590792Sgshapiro		if (doublequeue)
293690792Sgshapiro		{
293790792Sgshapiro			/* make sure it is in the queue */
293890792Sgshapiro			queueup(ee, false, true);
293990792Sgshapiro		}
294090792Sgshapiro		else
294190792Sgshapiro		{
294290792Sgshapiro			/* send to all recipients */
294390792Sgshapiro			sendall(ee, SM_DEFAULT);
294490792Sgshapiro		}
294590792Sgshapiro		ee->e_to = NULL;
294690792Sgshapiro	}
294790792Sgshapiro
294890792Sgshapiro	/* issue success message */
294990792Sgshapiro	message("250 2.0.0 %s Message accepted for delivery", id);
295090792Sgshapiro
295190792Sgshapiro	/* if we just queued, poke it */
295290792Sgshapiro	if (doublequeue)
295390792Sgshapiro	{
295490792Sgshapiro		bool anything_to_send = false;
295590792Sgshapiro
295690792Sgshapiro		sm_getla();
295790792Sgshapiro		for (ee = e; ee != NULL; ee = ee->e_sibling)
295890792Sgshapiro		{
295990792Sgshapiro			if (WILL_BE_QUEUED(ee->e_sendmode))
296090792Sgshapiro				continue;
296190792Sgshapiro			if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
296290792Sgshapiro			{
296390792Sgshapiro				ee->e_sendmode = SM_QUEUE;
296490792Sgshapiro				continue;
296590792Sgshapiro			}
296690792Sgshapiro#if _FFR_QUARANTINE
296790792Sgshapiro			else if (QueueMode != QM_QUARANTINE &&
296890792Sgshapiro				 ee->e_quarmsg != NULL)
296990792Sgshapiro			{
297090792Sgshapiro				ee->e_sendmode = SM_QUEUE;
297190792Sgshapiro				continue;
297290792Sgshapiro			}
297390792Sgshapiro#endif /* _FFR_QUARANTINE */
297490792Sgshapiro			anything_to_send = true;
297590792Sgshapiro
297690792Sgshapiro			/* close all the queue files */
297790792Sgshapiro			closexscript(ee);
297890792Sgshapiro			if (ee->e_dfp != NULL)
297990792Sgshapiro			{
298090792Sgshapiro				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
298190792Sgshapiro				ee->e_dfp = NULL;
298290792Sgshapiro			}
298390792Sgshapiro			unlockqueue(ee);
298490792Sgshapiro		}
298590792Sgshapiro		if (anything_to_send)
298690792Sgshapiro		{
298790792Sgshapiro#if PIPELINING
298890792Sgshapiro			/*
298990792Sgshapiro			**  XXX if we don't do this, we get 250 twice
299090792Sgshapiro			**	because it is also flushed in the child.
299190792Sgshapiro			*/
299290792Sgshapiro
299390792Sgshapiro			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
299490792Sgshapiro#endif /* PIPELINING */
299590792Sgshapiro			(void) doworklist(e, true, true);
299690792Sgshapiro		}
299790792Sgshapiro	}
299890792Sgshapiro
299990792Sgshapiro  abortmessage:
300090792Sgshapiro	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
300190792Sgshapiro		logsender(e, NULL);
300290792Sgshapiro	e->e_flags &= ~EF_LOGSENDER;
300390792Sgshapiro
300490792Sgshapiro	/* clean up a bit */
300590792Sgshapiro	smtp->sm_gotmail = false;
300690792Sgshapiro
300790792Sgshapiro	/*
300890792Sgshapiro	**  Call dropenvelope if and only if the envelope is *not*
300990792Sgshapiro	**  being processed by the child process forked by doworklist().
301090792Sgshapiro	*/
301190792Sgshapiro
301290792Sgshapiro	if (aborting || bitset(EF_DISCARD, e->e_flags))
301390792Sgshapiro		dropenvelope(e, true, false);
301490792Sgshapiro	else
301590792Sgshapiro	{
301690792Sgshapiro		for (ee = e; ee != NULL; ee = ee->e_sibling)
301790792Sgshapiro		{
301890792Sgshapiro#if _FFR_QUARANTINE
301990792Sgshapiro			if (!doublequeue &&
302090792Sgshapiro			    QueueMode != QM_QUARANTINE &&
302190792Sgshapiro			    ee->e_quarmsg != NULL)
302290792Sgshapiro			{
302390792Sgshapiro				dropenvelope(ee, true, false);
302490792Sgshapiro				continue;
302590792Sgshapiro			}
302690792Sgshapiro#endif /* _FFR_QUARANTINE */
302790792Sgshapiro			if (WILL_BE_QUEUED(ee->e_sendmode))
302890792Sgshapiro				dropenvelope(ee, true, false);
302990792Sgshapiro		}
303090792Sgshapiro	}
303190792Sgshapiro	sm_rpool_free(e->e_rpool);
303290792Sgshapiro
303390792Sgshapiro	/*
303490792Sgshapiro	**  At this point, e == &MainEnvelope, but if we did splitting,
303590792Sgshapiro	**  then CurEnv may point to an envelope structure that was just
303690792Sgshapiro	**  freed with the rpool.  So reset CurEnv *before* calling
303790792Sgshapiro	**  newenvelope.
303890792Sgshapiro	*/
303990792Sgshapiro
304090792Sgshapiro	CurEnv = e;
304190792Sgshapiro	newenvelope(e, e, sm_rpool_new_x(NULL));
304290792Sgshapiro	e->e_flags = BlankEnvelope.e_flags;
304390792Sgshapiro
304490792Sgshapiro#if _FFR_QUARANTINE
304590792Sgshapiro	/* restore connection quarantining */
304690792Sgshapiro	if (smtp->sm_quarmsg == NULL)
304790792Sgshapiro	{
304890792Sgshapiro		e->e_quarmsg = NULL;
304990792Sgshapiro		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
305090792Sgshapiro	}
305190792Sgshapiro	else
305290792Sgshapiro	{
305390792Sgshapiro		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
305490792Sgshapiro		macdefine(&e->e_macro, A_PERM,
305590792Sgshapiro			  macid("{quarantine}"), e->e_quarmsg);
305690792Sgshapiro	}
305790792Sgshapiro#endif /* _FFR_QUARANTINE */
305838032Speter}
305990792Sgshapiro/*
306090792Sgshapiro**  LOGUNDELRCPTS -- log undelivered (or all) recipients.
306190792Sgshapiro**
306290792Sgshapiro**	Parameters:
306390792Sgshapiro**		e -- envelope.
306490792Sgshapiro**		msg -- message for Stat=
306590792Sgshapiro**		level -- log level.
306690792Sgshapiro**		all -- log all recipients.
306790792Sgshapiro**
306890792Sgshapiro**	Returns:
306990792Sgshapiro**		none.
307090792Sgshapiro**
307190792Sgshapiro**	Side Effects:
307290792Sgshapiro**		logs undelivered (or all) recipients
307390792Sgshapiro*/
307490792Sgshapiro
307590792Sgshapirovoid
307690792Sgshapirologundelrcpts(e, msg, level, all)
307790792Sgshapiro	ENVELOPE *e;
307890792Sgshapiro	char *msg;
307990792Sgshapiro	int level;
308090792Sgshapiro	bool all;
308190792Sgshapiro{
308290792Sgshapiro	ADDRESS *a;
308390792Sgshapiro
308490792Sgshapiro	if (LogLevel <= level || msg == NULL || *msg == '\0')
308590792Sgshapiro		return;
308690792Sgshapiro
308790792Sgshapiro	/* Clear $h so relay= doesn't get mislogged by logdelivery() */
308890792Sgshapiro	macdefine(&e->e_macro, A_PERM, 'h', NULL);
308990792Sgshapiro
309090792Sgshapiro	/* Log who the mail would have gone to */
309190792Sgshapiro	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
309290792Sgshapiro	{
309390792Sgshapiro		if (!QS_IS_UNDELIVERED(a->q_state) && !all)
309490792Sgshapiro			continue;
309590792Sgshapiro		e->e_to = a->q_paddr;
309690792Sgshapiro		logdelivery(NULL, NULL, a->q_status, msg, NULL,
309790792Sgshapiro			    (time_t) 0, e);
309890792Sgshapiro	}
309990792Sgshapiro	e->e_to = NULL;
310090792Sgshapiro}
310190792Sgshapiro/*
310238032Speter**  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
310338032Speter**
310438032Speter**	Parameters:
310538032Speter**		pcounter -- pointer to a counter for this command.
310638032Speter**		maxcount -- maximum value for this counter before we
310738032Speter**			slow down.
310864562Sgshapiro**		waitnow -- sleep now (in this routine)?
310938032Speter**		cname -- command name for logging.
311038032Speter**		e -- the current envelope.
311138032Speter**
311238032Speter**	Returns:
311371345Sgshapiro**		time to wait.
311438032Speter**
311538032Speter**	Side Effects:
311638032Speter**		Slows down if we seem to be under attack.
311738032Speter*/
311838032Speter
311964562Sgshapirostatic time_t
312064562Sgshapirochecksmtpattack(pcounter, maxcount, waitnow, cname, e)
312190792Sgshapiro	volatile unsigned int *pcounter;
312238032Speter	int maxcount;
312364562Sgshapiro	bool waitnow;
312438032Speter	char *cname;
312538032Speter	ENVELOPE *e;
312638032Speter{
312790792Sgshapiro	if (maxcount <= 0)	/* no limit */
312890792Sgshapiro		return (time_t) 0;
312990792Sgshapiro
313038032Speter	if (++(*pcounter) >= maxcount)
313138032Speter	{
313264562Sgshapiro		time_t s;
313364562Sgshapiro
313438032Speter		if (*pcounter == maxcount && LogLevel > 5)
313538032Speter		{
313638032Speter			sm_syslog(LOG_INFO, e->e_id,
313790792Sgshapiro				  "%.100s: possible SMTP attack: command=%.40s, count=%u",
313877349Sgshapiro				  CurSmtpClient, cname, *pcounter);
313938032Speter		}
314064562Sgshapiro		s = 1 << (*pcounter - maxcount);
314190792Sgshapiro		if (s >= MAXTIMEOUT || s <= 0)
314264562Sgshapiro			s = MAXTIMEOUT;
314390792Sgshapiro
314464562Sgshapiro		/* sleep at least 1 second before returning */
314564562Sgshapiro		(void) sleep(*pcounter / maxcount);
314664562Sgshapiro		s -= *pcounter / maxcount;
314764562Sgshapiro		if (waitnow)
314864562Sgshapiro		{
314964562Sgshapiro			(void) sleep(s);
315090792Sgshapiro			return 0;
315164562Sgshapiro		}
315290792Sgshapiro		return s;
315338032Speter	}
315490792Sgshapiro	return (time_t) 0;
315538032Speter}
315690792Sgshapiro/*
315790792Sgshapiro**  SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
315890792Sgshapiro**
315990792Sgshapiro**	Parameters:
316090792Sgshapiro**		none.
316190792Sgshapiro**
316290792Sgshapiro**	Returns:
316390792Sgshapiro**		nothing.
316490792Sgshapiro**
316590792Sgshapiro**	Side Effects:
316690792Sgshapiro**		may change I/O fd.
316790792Sgshapiro*/
316890792Sgshapiro
316990792Sgshapirostatic void
317090792Sgshapirosetup_smtpd_io()
317190792Sgshapiro{
317290792Sgshapiro	int inchfd, outchfd, outfd;
317390792Sgshapiro
317490792Sgshapiro	inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
317590792Sgshapiro	outchfd  = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
317690792Sgshapiro	outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
317790792Sgshapiro	if (outchfd != outfd)
317890792Sgshapiro	{
317990792Sgshapiro		/* arrange for debugging output to go to remote host */
318090792Sgshapiro		(void) dup2(outchfd, outfd);
318190792Sgshapiro	}
318290792Sgshapiro
318390792Sgshapiro	/*
318490792Sgshapiro	**  if InChannel and OutChannel are stdin/stdout
318590792Sgshapiro	**  and connected to ttys
318690792Sgshapiro	**  and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
318790792Sgshapiro	**  then "chain" them together.
318890792Sgshapiro	*/
318990792Sgshapiro
319090792Sgshapiro	if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
319190792Sgshapiro	    isatty(inchfd) && isatty(outchfd))
319290792Sgshapiro	{
319390792Sgshapiro		int inmode, outmode;
319490792Sgshapiro
319590792Sgshapiro		inmode = fcntl(inchfd, F_GETFL, 0);
319690792Sgshapiro		if (inmode == -1)
319790792Sgshapiro		{
319890792Sgshapiro			if (LogLevel > 11)
319990792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
320090792Sgshapiro					"fcntl(inchfd, F_GETFL) failed: %s",
320190792Sgshapiro					sm_errstring(errno));
320290792Sgshapiro			return;
320390792Sgshapiro		}
320490792Sgshapiro		outmode = fcntl(outchfd, F_GETFL, 0);
320590792Sgshapiro		if (outmode == -1)
320690792Sgshapiro		{
320790792Sgshapiro			if (LogLevel > 11)
320890792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
320990792Sgshapiro					"fcntl(outchfd, F_GETFL) failed: %s",
321090792Sgshapiro					sm_errstring(errno));
321190792Sgshapiro			return;
321290792Sgshapiro		}
321390792Sgshapiro		if (bitset(O_NONBLOCK, inmode) ||
321490792Sgshapiro		    bitset(O_NONBLOCK, outmode) ||
321590792Sgshapiro		    fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
321690792Sgshapiro			return;
321790792Sgshapiro		outmode = fcntl(outchfd, F_GETFL, 0);
321890792Sgshapiro		if (outmode != -1 && bitset(O_NONBLOCK, outmode))
321990792Sgshapiro		{
322090792Sgshapiro			/* changing InChannel also changes OutChannel */
322190792Sgshapiro			sm_io_automode(OutChannel, InChannel);
322290792Sgshapiro			if (tTd(97, 4) && LogLevel > 9)
322390792Sgshapiro				sm_syslog(LOG_INFO, NOQID,
322490792Sgshapiro					  "set automode for I (%d)/O (%d) in SMTP server",
322590792Sgshapiro					  inchfd, outchfd);
322690792Sgshapiro		}
322790792Sgshapiro
322890792Sgshapiro		/* undo change of inchfd */
322990792Sgshapiro		(void) fcntl(inchfd, F_SETFL, inmode);
323090792Sgshapiro	}
323190792Sgshapiro}
323290792Sgshapiro/*
323338032Speter**  SKIPWORD -- skip a fixed word.
323438032Speter**
323538032Speter**	Parameters:
323638032Speter**		p -- place to start looking.
323738032Speter**		w -- word to skip.
323838032Speter**
323938032Speter**	Returns:
324038032Speter**		p following w.
324138032Speter**		NULL on error.
324238032Speter**
324338032Speter**	Side Effects:
324438032Speter**		clobbers the p data area.
324538032Speter*/
324638032Speter
324738032Speterstatic char *
324838032Speterskipword(p, w)
324938032Speter	register char *volatile p;
325038032Speter	char *w;
325138032Speter{
325238032Speter	register char *q;
325338032Speter	char *firstp = p;
325438032Speter
325538032Speter	/* find beginning of word */
325690792Sgshapiro	SKIP_SPACE(p);
325738032Speter	q = p;
325838032Speter
325938032Speter	/* find end of word */
326038032Speter	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
326138032Speter		p++;
326238032Speter	while (isascii(*p) && isspace(*p))
326338032Speter		*p++ = '\0';
326438032Speter	if (*p != ':')
326538032Speter	{
326638032Speter	  syntax:
326764562Sgshapiro		usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
326838032Speter			shortenstring(firstp, MAXSHORTSTR));
326964562Sgshapiro		return NULL;
327038032Speter	}
327138032Speter	*p++ = '\0';
327290792Sgshapiro	SKIP_SPACE(p);
327338032Speter
327438032Speter	if (*p == '\0')
327538032Speter		goto syntax;
327638032Speter
327738032Speter	/* see if the input word matches desired word */
327890792Sgshapiro	if (sm_strcasecmp(q, w))
327938032Speter		goto syntax;
328038032Speter
328164562Sgshapiro	return p;
328238032Speter}
328390792Sgshapiro/*
328438032Speter**  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
328538032Speter**
328638032Speter**	Parameters:
328738032Speter**		kp -- the parameter key.
328838032Speter**		vp -- the value of that parameter.
328938032Speter**		e -- the envelope.
329038032Speter**
329138032Speter**	Returns:
329238032Speter**		none.
329338032Speter*/
329438032Speter
329564562Sgshapirostatic void
329638032Spetermail_esmtp_args(kp, vp, e)
329738032Speter	char *kp;
329838032Speter	char *vp;
329938032Speter	ENVELOPE *e;
330038032Speter{
330190792Sgshapiro	if (sm_strcasecmp(kp, "size") == 0)
330238032Speter	{
330338032Speter		if (vp == NULL)
330438032Speter		{
330564562Sgshapiro			usrerr("501 5.5.2 SIZE requires a value");
330638032Speter			/* NOTREACHED */
330738032Speter		}
330890792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
330990792Sgshapiro		errno = 0;
331071345Sgshapiro		e->e_msgsize = strtol(vp, (char **) NULL, 10);
331166494Sgshapiro		if (e->e_msgsize == LONG_MAX && errno == ERANGE)
331266494Sgshapiro		{
331366494Sgshapiro			usrerr("552 5.2.3 Message size exceeds maximum value");
331466494Sgshapiro			/* NOTREACHED */
331566494Sgshapiro		}
331690792Sgshapiro		if (e->e_msgsize < 0)
331790792Sgshapiro		{
331890792Sgshapiro			usrerr("552 5.2.3 Message size invalid");
331990792Sgshapiro			/* NOTREACHED */
332090792Sgshapiro		}
332138032Speter	}
332290792Sgshapiro	else if (sm_strcasecmp(kp, "body") == 0)
332338032Speter	{
332438032Speter		if (vp == NULL)
332538032Speter		{
332664562Sgshapiro			usrerr("501 5.5.2 BODY requires a value");
332738032Speter			/* NOTREACHED */
332838032Speter		}
332990792Sgshapiro		else if (sm_strcasecmp(vp, "8bitmime") == 0)
333038032Speter		{
333190792Sgshapiro			SevenBitInput = false;
333238032Speter		}
333390792Sgshapiro		else if (sm_strcasecmp(vp, "7bit") == 0)
333438032Speter		{
333590792Sgshapiro			SevenBitInput = true;
333638032Speter		}
333738032Speter		else
333838032Speter		{
333990792Sgshapiro			usrerr("501 5.5.4 Unknown BODY type %s", vp);
334038032Speter			/* NOTREACHED */
334138032Speter		}
334290792Sgshapiro		e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
334338032Speter	}
334490792Sgshapiro	else if (sm_strcasecmp(kp, "envid") == 0)
334538032Speter	{
334664562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
334764562Sgshapiro		{
334864562Sgshapiro			usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
334964562Sgshapiro			/* NOTREACHED */
335064562Sgshapiro		}
335138032Speter		if (vp == NULL)
335238032Speter		{
335364562Sgshapiro			usrerr("501 5.5.2 ENVID requires a value");
335438032Speter			/* NOTREACHED */
335538032Speter		}
335638032Speter		if (!xtextok(vp))
335738032Speter		{
335864562Sgshapiro			usrerr("501 5.5.4 Syntax error in ENVID parameter value");
335938032Speter			/* NOTREACHED */
336038032Speter		}
336138032Speter		if (e->e_envid != NULL)
336238032Speter		{
336364562Sgshapiro			usrerr("501 5.5.0 Duplicate ENVID parameter");
336438032Speter			/* NOTREACHED */
336538032Speter		}
336690792Sgshapiro		e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
336790792Sgshapiro		macdefine(&e->e_macro, A_PERM,
336890792Sgshapiro			macid("{dsn_envid}"), e->e_envid);
336938032Speter	}
337090792Sgshapiro	else if (sm_strcasecmp(kp, "ret") == 0)
337138032Speter	{
337264562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
337364562Sgshapiro		{
337464562Sgshapiro			usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
337564562Sgshapiro			/* NOTREACHED */
337664562Sgshapiro		}
337738032Speter		if (vp == NULL)
337838032Speter		{
337964562Sgshapiro			usrerr("501 5.5.2 RET requires a value");
338038032Speter			/* NOTREACHED */
338138032Speter		}
338238032Speter		if (bitset(EF_RET_PARAM, e->e_flags))
338338032Speter		{
338464562Sgshapiro			usrerr("501 5.5.0 Duplicate RET parameter");
338538032Speter			/* NOTREACHED */
338638032Speter		}
338738032Speter		e->e_flags |= EF_RET_PARAM;
338890792Sgshapiro		if (sm_strcasecmp(vp, "hdrs") == 0)
338938032Speter			e->e_flags |= EF_NO_BODY_RETN;
339090792Sgshapiro		else if (sm_strcasecmp(vp, "full") != 0)
339138032Speter		{
339264562Sgshapiro			usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
339338032Speter			/* NOTREACHED */
339438032Speter		}
339590792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
339638032Speter	}
339790792Sgshapiro#if SASL
339890792Sgshapiro	else if (sm_strcasecmp(kp, "auth") == 0)
339964562Sgshapiro	{
340064562Sgshapiro		int len;
340164562Sgshapiro		char *q;
340264562Sgshapiro		char *auth_param;	/* the value of the AUTH=x */
340364562Sgshapiro		bool saveQuickAbort = QuickAbort;
340464562Sgshapiro		bool saveSuprErrs = SuprErrs;
340590792Sgshapiro		bool saveExitStat = ExitStat;
340664562Sgshapiro		char pbuf[256];
340764562Sgshapiro
340864562Sgshapiro		if (vp == NULL)
340964562Sgshapiro		{
341064562Sgshapiro			usrerr("501 5.5.2 AUTH= requires a value");
341164562Sgshapiro			/* NOTREACHED */
341264562Sgshapiro		}
341364562Sgshapiro		if (e->e_auth_param != NULL)
341464562Sgshapiro		{
341564562Sgshapiro			usrerr("501 5.5.0 Duplicate AUTH parameter");
341664562Sgshapiro			/* NOTREACHED */
341764562Sgshapiro		}
341864562Sgshapiro		if ((q = strchr(vp, ' ')) != NULL)
341964562Sgshapiro			len = q - vp + 1;
342064562Sgshapiro		else
342164562Sgshapiro			len = strlen(vp) + 1;
342264562Sgshapiro		auth_param = xalloc(len);
342390792Sgshapiro		(void) sm_strlcpy(auth_param, vp, len);
342464562Sgshapiro		if (!xtextok(auth_param))
342564562Sgshapiro		{
342664562Sgshapiro			usrerr("501 5.5.4 Syntax error in AUTH parameter value");
342764562Sgshapiro			/* just a warning? */
342864562Sgshapiro			/* NOTREACHED */
342964562Sgshapiro		}
343064562Sgshapiro
343164562Sgshapiro		/* XXX this might be cut off */
343290792Sgshapiro		(void) sm_strlcpy(pbuf, xuntextify(auth_param), sizeof pbuf);
343364562Sgshapiro		/* xalloc() the buffer instead? */
343464562Sgshapiro
343564562Sgshapiro		/* XXX define this always or only if trusted? */
343690792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"), pbuf);
343764562Sgshapiro
343864562Sgshapiro		/*
343964562Sgshapiro		**  call Strust_auth to find out whether
344064562Sgshapiro		**  auth_param is acceptable (trusted)
344164562Sgshapiro		**  we shouldn't trust it if not authenticated
344264562Sgshapiro		**  (required by RFC, leave it to ruleset?)
344364562Sgshapiro		*/
344464562Sgshapiro
344590792Sgshapiro		SuprErrs = true;
344690792Sgshapiro		QuickAbort = false;
344764562Sgshapiro		if (strcmp(auth_param, "<>") != 0 &&
344890792Sgshapiro		     (rscheck("trust_auth", pbuf, NULL, e, true, false, 9,
344990792Sgshapiro			      NULL, NOQID) != EX_OK || Errors > 0))
345064562Sgshapiro		{
345164562Sgshapiro			if (tTd(95, 8))
345264562Sgshapiro			{
345364562Sgshapiro				q = e->e_auth_param;
345490792Sgshapiro				sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
345564562Sgshapiro					pbuf, (q == NULL) ? "" : q);
345664562Sgshapiro			}
345790792Sgshapiro
345864562Sgshapiro			/* not trusted */
345990792Sgshapiro			e->e_auth_param = "<>";
346090792Sgshapiro# if _FFR_AUTH_PASSING
346190792Sgshapiro			macdefine(&BlankEnvelope.e_macro, A_PERM,
346290792Sgshapiro				  macid("{auth_author}"), NULL);
346390792Sgshapiro# endif /* _FFR_AUTH_PASSING */
346464562Sgshapiro		}
346564562Sgshapiro		else
346664562Sgshapiro		{
346764562Sgshapiro			if (tTd(95, 8))
346890792Sgshapiro				sm_dprintf("auth=\"%.100s\" trusted\n", pbuf);
346990792Sgshapiro			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
347090792Sgshapiro							    auth_param);
347164562Sgshapiro		}
347290792Sgshapiro		sm_free(auth_param); /* XXX */
347377349Sgshapiro
347464562Sgshapiro		/* reset values */
347564562Sgshapiro		Errors = 0;
347664562Sgshapiro		QuickAbort = saveQuickAbort;
347764562Sgshapiro		SuprErrs = saveSuprErrs;
347890792Sgshapiro		ExitStat = saveExitStat;
347964562Sgshapiro	}
348090792Sgshapiro#endif /* SASL */
348190792Sgshapiro#define PRTCHAR(c)	((isascii(c) && isprint(c)) ? (c) : '?')
348290792Sgshapiro
348390792Sgshapiro	/*
348490792Sgshapiro	**  "by" is only accepted if DeliverByMin >= 0.
348590792Sgshapiro	**  We maybe could add this to the list of server_features.
348690792Sgshapiro	*/
348790792Sgshapiro
348890792Sgshapiro	else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
348990792Sgshapiro	{
349090792Sgshapiro		char *s;
349190792Sgshapiro
349290792Sgshapiro		if (vp == NULL)
349390792Sgshapiro		{
349490792Sgshapiro			usrerr("501 5.5.2 BY= requires a value");
349590792Sgshapiro			/* NOTREACHED */
349690792Sgshapiro		}
349790792Sgshapiro		errno = 0;
349890792Sgshapiro		e->e_deliver_by = strtol(vp, &s, 10);
349990792Sgshapiro		if (e->e_deliver_by == LONG_MIN ||
350090792Sgshapiro		    e->e_deliver_by == LONG_MAX ||
350190792Sgshapiro		    e->e_deliver_by > 999999999l ||
350290792Sgshapiro		    e->e_deliver_by < -999999999l)
350390792Sgshapiro		{
350490792Sgshapiro			usrerr("501 5.5.2 BY=%s out of range", vp);
350590792Sgshapiro			/* NOTREACHED */
350690792Sgshapiro		}
350790792Sgshapiro		if (s == NULL || *s != ';')
350890792Sgshapiro		{
350990792Sgshapiro			usrerr("501 5.5.2 BY= missing ';'");
351090792Sgshapiro			/* NOTREACHED */
351190792Sgshapiro		}
351290792Sgshapiro		e->e_dlvr_flag = 0;
351390792Sgshapiro		++s;	/* XXX: spaces allowed? */
351490792Sgshapiro		SKIP_SPACE(s);
351590792Sgshapiro		switch (tolower(*s))
351690792Sgshapiro		{
351790792Sgshapiro		  case 'n':
351890792Sgshapiro			e->e_dlvr_flag = DLVR_NOTIFY;
351990792Sgshapiro			break;
352090792Sgshapiro		  case 'r':
352190792Sgshapiro			e->e_dlvr_flag = DLVR_RETURN;
352290792Sgshapiro			if (e->e_deliver_by <= 0)
352390792Sgshapiro			{
352490792Sgshapiro				usrerr("501 5.5.4 mode R requires BY time > 0");
352590792Sgshapiro				/* NOTREACHED */
352690792Sgshapiro			}
352790792Sgshapiro			if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
352890792Sgshapiro			    e->e_deliver_by < DeliverByMin)
352990792Sgshapiro			{
353090792Sgshapiro				usrerr("555 5.5.2 time %ld less than %ld",
353190792Sgshapiro					e->e_deliver_by, (long) DeliverByMin);
353290792Sgshapiro				/* NOTREACHED */
353390792Sgshapiro			}
353490792Sgshapiro			break;
353590792Sgshapiro		  default:
353690792Sgshapiro			usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
353790792Sgshapiro			/* NOTREACHED */
353890792Sgshapiro		}
353990792Sgshapiro		++s;	/* XXX: spaces allowed? */
354090792Sgshapiro		SKIP_SPACE(s);
354190792Sgshapiro		switch (tolower(*s))
354290792Sgshapiro		{
354390792Sgshapiro		  case 't':
354490792Sgshapiro			e->e_dlvr_flag |= DLVR_TRACE;
354590792Sgshapiro			break;
354690792Sgshapiro		  case '\0':
354790792Sgshapiro			break;
354890792Sgshapiro		  default:
354990792Sgshapiro			usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
355090792Sgshapiro			/* NOTREACHED */
355190792Sgshapiro		}
355290792Sgshapiro
355390792Sgshapiro		/* XXX: check whether more characters follow? */
355490792Sgshapiro	}
355538032Speter	else
355638032Speter	{
355766494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
355838032Speter		/* NOTREACHED */
355938032Speter	}
356038032Speter}
356190792Sgshapiro/*
356238032Speter**  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
356338032Speter**
356438032Speter**	Parameters:
356538032Speter**		a -- the address corresponding to the To: parameter.
356638032Speter**		kp -- the parameter key.
356738032Speter**		vp -- the value of that parameter.
356838032Speter**		e -- the envelope.
356938032Speter**
357038032Speter**	Returns:
357138032Speter**		none.
357238032Speter*/
357338032Speter
357464562Sgshapirostatic void
357538032Speterrcpt_esmtp_args(a, kp, vp, e)
357638032Speter	ADDRESS *a;
357738032Speter	char *kp;
357838032Speter	char *vp;
357938032Speter	ENVELOPE *e;
358038032Speter{
358190792Sgshapiro	if (sm_strcasecmp(kp, "notify") == 0)
358238032Speter	{
358338032Speter		char *p;
358438032Speter
358564562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
358664562Sgshapiro		{
358764562Sgshapiro			usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
358864562Sgshapiro			/* NOTREACHED */
358964562Sgshapiro		}
359038032Speter		if (vp == NULL)
359138032Speter		{
359264562Sgshapiro			usrerr("501 5.5.2 NOTIFY requires a value");
359338032Speter			/* NOTREACHED */
359438032Speter		}
359538032Speter		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
359638032Speter		a->q_flags |= QHASNOTIFY;
359790792Sgshapiro		macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
359864562Sgshapiro
359990792Sgshapiro		if (sm_strcasecmp(vp, "never") == 0)
360038032Speter			return;
360138032Speter		for (p = vp; p != NULL; vp = p)
360238032Speter		{
360338032Speter			p = strchr(p, ',');
360438032Speter			if (p != NULL)
360538032Speter				*p++ = '\0';
360690792Sgshapiro			if (sm_strcasecmp(vp, "success") == 0)
360738032Speter				a->q_flags |= QPINGONSUCCESS;
360890792Sgshapiro			else if (sm_strcasecmp(vp, "failure") == 0)
360938032Speter				a->q_flags |= QPINGONFAILURE;
361090792Sgshapiro			else if (sm_strcasecmp(vp, "delay") == 0)
361138032Speter				a->q_flags |= QPINGONDELAY;
361238032Speter			else
361338032Speter			{
361464562Sgshapiro				usrerr("501 5.5.4 Bad argument \"%s\"  to NOTIFY",
361538032Speter					vp);
361638032Speter				/* NOTREACHED */
361738032Speter			}
361838032Speter		}
361938032Speter	}
362090792Sgshapiro	else if (sm_strcasecmp(kp, "orcpt") == 0)
362138032Speter	{
362264562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
362364562Sgshapiro		{
362464562Sgshapiro			usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
362564562Sgshapiro			/* NOTREACHED */
362664562Sgshapiro		}
362738032Speter		if (vp == NULL)
362838032Speter		{
362964562Sgshapiro			usrerr("501 5.5.2 ORCPT requires a value");
363038032Speter			/* NOTREACHED */
363138032Speter		}
363238032Speter		if (strchr(vp, ';') == NULL || !xtextok(vp))
363338032Speter		{
363464562Sgshapiro			usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
363538032Speter			/* NOTREACHED */
363638032Speter		}
363738032Speter		if (a->q_orcpt != NULL)
363838032Speter		{
363964562Sgshapiro			usrerr("501 5.5.0 Duplicate ORCPT parameter");
364038032Speter			/* NOTREACHED */
364138032Speter		}
364290792Sgshapiro		a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
364338032Speter	}
364438032Speter	else
364538032Speter	{
364666494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
364738032Speter		/* NOTREACHED */
364838032Speter	}
364938032Speter}
365090792Sgshapiro/*
365138032Speter**  PRINTVRFYADDR -- print an entry in the verify queue
365238032Speter**
365338032Speter**	Parameters:
365490792Sgshapiro**		a -- the address to print.
365538032Speter**		last -- set if this is the last one.
365638032Speter**		vrfy -- set if this is a VRFY command.
365738032Speter**
365838032Speter**	Returns:
365938032Speter**		none.
366038032Speter**
366138032Speter**	Side Effects:
366238032Speter**		Prints the appropriate 250 codes.
366338032Speter*/
366464562Sgshapiro#define OFFF	(3 + 1 + 5 + 1)	/* offset in fmt: SMTP reply + enh. code */
366538032Speter
366664562Sgshapirostatic void
366738032Speterprintvrfyaddr(a, last, vrfy)
366838032Speter	register ADDRESS *a;
366938032Speter	bool last;
367038032Speter	bool vrfy;
367138032Speter{
367264562Sgshapiro	char fmtbuf[30];
367338032Speter
367438032Speter	if (vrfy && a->q_mailer != NULL &&
367538032Speter	    !bitnset(M_VRFY250, a->q_mailer->m_flags))
367690792Sgshapiro		(void) sm_strlcpy(fmtbuf, "252", sizeof fmtbuf);
367738032Speter	else
367890792Sgshapiro		(void) sm_strlcpy(fmtbuf, "250", sizeof fmtbuf);
367938032Speter	fmtbuf[3] = last ? ' ' : '-';
368090792Sgshapiro	(void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4);
368138032Speter	if (a->q_fullname == NULL)
368238032Speter	{
368364562Sgshapiro		if ((a->q_mailer == NULL ||
368464562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
368590792Sgshapiro		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
368664562Sgshapiro		    strchr(a->q_user, '@') == NULL)
368790792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
368864562Sgshapiro				       sizeof fmtbuf - OFFF);
368938032Speter		else
369090792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
369164562Sgshapiro				       sizeof fmtbuf - OFFF);
369238032Speter		message(fmtbuf, a->q_user, MyHostName);
369338032Speter	}
369438032Speter	else
369538032Speter	{
369664562Sgshapiro		if ((a->q_mailer == NULL ||
369764562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
369890792Sgshapiro		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
369964562Sgshapiro		    strchr(a->q_user, '@') == NULL)
370090792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
370164562Sgshapiro				       sizeof fmtbuf - OFFF);
370238032Speter		else
370390792Sgshapiro			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
370464562Sgshapiro				       sizeof fmtbuf - OFFF);
370538032Speter		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
370638032Speter	}
370738032Speter}
370838032Speter
370990792Sgshapiro#if SASL
371090792Sgshapiro/*
371164562Sgshapiro**  SASLMECHS -- get list of possible AUTH mechanisms
371264562Sgshapiro**
371364562Sgshapiro**	Parameters:
371490792Sgshapiro**		conn -- SASL connection info.
371590792Sgshapiro**		mechlist -- output parameter for list of mechanisms.
371664562Sgshapiro**
371764562Sgshapiro**	Returns:
371890792Sgshapiro**		number of mechs.
371964562Sgshapiro*/
372064562Sgshapiro
372164562Sgshapirostatic int
372264562Sgshapirosaslmechs(conn, mechlist)
372364562Sgshapiro	sasl_conn_t *conn;
372464562Sgshapiro	char **mechlist;
372564562Sgshapiro{
372664562Sgshapiro	int len, num, result;
372764562Sgshapiro
372864562Sgshapiro	/* "user" is currently unused */
372964562Sgshapiro	result = sasl_listmech(conn, "user", /* XXX */
373064562Sgshapiro			       "", " ", "", mechlist,
373190792Sgshapiro			       (unsigned int *)&len, (unsigned int *)&num);
373290792Sgshapiro	if (result != SASL_OK)
373364562Sgshapiro	{
373490792Sgshapiro		if (LogLevel > 9)
373590792Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
373690792Sgshapiro				  "AUTH error: listmech=%d, num=%d",
373790792Sgshapiro				  result, num);
373890792Sgshapiro		num = 0;
373990792Sgshapiro	}
374090792Sgshapiro	if (num > 0)
374190792Sgshapiro	{
374264562Sgshapiro		if (LogLevel > 11)
374364562Sgshapiro			sm_syslog(LOG_INFO, NOQID,
374490792Sgshapiro				  "AUTH: available mech=%s, allowed mech=%s",
374564562Sgshapiro				  *mechlist, AuthMechanisms);
374690792Sgshapiro		*mechlist = intersect(AuthMechanisms, *mechlist, NULL);
374764562Sgshapiro	}
374864562Sgshapiro	else
374964562Sgshapiro	{
375090792Sgshapiro		*mechlist = NULL;	/* be paranoid... */
375190792Sgshapiro		if (result == SASL_OK && LogLevel > 9)
375264562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
375390792Sgshapiro				  "AUTH warning: no mechanisms");
375464562Sgshapiro	}
375564562Sgshapiro	return num;
375664562Sgshapiro}
375790792Sgshapiro/*
375864562Sgshapiro**  PROXY_POLICY -- define proxy policy for AUTH
375964562Sgshapiro**
376064562Sgshapiro**	Parameters:
376190792Sgshapiro**		context -- unused.
376290792Sgshapiro**		auth_identity -- authentication identity.
376390792Sgshapiro**		requested_user -- authorization identity.
376490792Sgshapiro**		user -- allowed user (output).
376590792Sgshapiro**		errstr -- possible error string (output).
376664562Sgshapiro**
376764562Sgshapiro**	Returns:
376864562Sgshapiro**		ok?
376964562Sgshapiro*/
377064562Sgshapiro
377164562Sgshapiroint
377264562Sgshapiroproxy_policy(context, auth_identity, requested_user, user, errstr)
377364562Sgshapiro	void *context;
377464562Sgshapiro	const char *auth_identity;
377564562Sgshapiro	const char *requested_user;
377664562Sgshapiro	const char **user;
377764562Sgshapiro	const char **errstr;
377864562Sgshapiro{
377964562Sgshapiro	if (user == NULL || auth_identity == NULL)
378064562Sgshapiro		return SASL_FAIL;
378164562Sgshapiro	*user = newstr(auth_identity);
378264562Sgshapiro	return SASL_OK;
378364562Sgshapiro}
378490792Sgshapiro#endif /* SASL */
378564562Sgshapiro
378690792Sgshapiro#if STARTTLS
378790792Sgshapiro/*
378890792Sgshapiro**  INITSRVTLS -- initialize server side TLS
378964562Sgshapiro**
379064562Sgshapiro**	Parameters:
379190792Sgshapiro**		tls_ok -- should tls initialization be done?
379264562Sgshapiro**
379364562Sgshapiro**	Returns:
379490792Sgshapiro**		succeeded?
379564562Sgshapiro**
379664562Sgshapiro**	Side Effects:
379790792Sgshapiro**		sets tls_ok_srv which is a static variable in this module.
379890792Sgshapiro**		Do NOT remove assignments to it!
379964562Sgshapiro*/
380064562Sgshapiro
380166494Sgshapirobool
380290792Sgshapiroinitsrvtls(tls_ok)
380390792Sgshapiro	bool tls_ok;
380464562Sgshapiro{
380590792Sgshapiro	if (!tls_ok)
380690792Sgshapiro		return false;
380764562Sgshapiro
380890792Sgshapiro	/* do NOT remove assignment */
380990792Sgshapiro	tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCERTfile,
381090792Sgshapiro			     Srvkeyfile, CACERTpath, CACERTfile, DHParams);
381190792Sgshapiro	return tls_ok_srv;
381264562Sgshapiro}
381390792Sgshapiro#endif /* STARTTLS */
381464562Sgshapiro/*
381590792Sgshapiro**  SRVFEATURES -- get features for SMTP server
381664562Sgshapiro**
381764562Sgshapiro**	Parameters:
381890792Sgshapiro**		e -- envelope (should be session context).
381990792Sgshapiro**		clientname -- name of client.
382090792Sgshapiro**		features -- default features for this invocation.
382164562Sgshapiro**
382264562Sgshapiro**	Returns:
382390792Sgshapiro**		server features.
382464562Sgshapiro*/
382564562Sgshapiro
382690792Sgshapiro/* table with options: it uses just one character, how about strings? */
382790792Sgshapirostatic struct
382864562Sgshapiro{
382990792Sgshapiro	char		srvf_opt;
383090792Sgshapiro	unsigned int	srvf_flag;
383190792Sgshapiro} srv_feat_table[] =
383264562Sgshapiro{
383390792Sgshapiro	{ 'A',	SRV_OFFER_AUTH	},
383490792Sgshapiro	{ 'B',	SRV_OFFER_VERB	},
383590792Sgshapiro	{ 'D',	SRV_OFFER_DSN	},
383690792Sgshapiro	{ 'E',	SRV_OFFER_ETRN	},
383790792Sgshapiro	{ 'L',	SRV_REQ_AUTH	},	/* not documented in 8.12 */
383890792Sgshapiro#if PIPELINING
383990792Sgshapiro# if _FFR_NO_PIPE
384090792Sgshapiro	{ 'N',	SRV_NO_PIPE	},
384190792Sgshapiro# endif /* _FFR_NO_PIPE */
384290792Sgshapiro	{ 'P',	SRV_OFFER_PIPE	},
384390792Sgshapiro#endif /* PIPELINING */
384490792Sgshapiro	{ 'R',	SRV_VRFY_CLT	},
384590792Sgshapiro	{ 'S',	SRV_OFFER_TLS	},
384690792Sgshapiro/*	{ 'T',	SRV_TMP_FAIL	},	*/
384790792Sgshapiro	{ 'V',	SRV_VRFY_CLT	},
384890792Sgshapiro	{ 'X',	SRV_OFFER_EXPN	},
384990792Sgshapiro/*	{ 'Y',	SRV_OFFER_VRFY	},	*/
385090792Sgshapiro	{ '\0',	SRV_NONE	}
385190792Sgshapiro};
385264562Sgshapiro
385390792Sgshapirostatic unsigned int
385490792Sgshapirosrvfeatures(e, clientname, features)
385590792Sgshapiro	ENVELOPE *e;
385690792Sgshapiro	char *clientname;
385790792Sgshapiro	unsigned int features;
385877349Sgshapiro{
385990792Sgshapiro	int r, i, j;
386090792Sgshapiro	char **pvp, c, opt;
386190792Sgshapiro	char pvpbuf[PSBUFSIZE];
386277349Sgshapiro
386390792Sgshapiro	pvp = NULL;
386490792Sgshapiro	r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
386590792Sgshapiro		  sizeof(pvpbuf));
386690792Sgshapiro	if (r != EX_OK)
386790792Sgshapiro		return features;
386890792Sgshapiro	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
386990792Sgshapiro		return features;
387090792Sgshapiro	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
387190792Sgshapiro		return SRV_TMP_FAIL;
387277349Sgshapiro
387364562Sgshapiro	/*
387490792Sgshapiro	**  General rule (see sendmail.h, d_flags):
387590792Sgshapiro	**  lower case: required/offered, upper case: Not required/available
387690792Sgshapiro	**
387790792Sgshapiro	**  Since we can change some features per daemon, we have both
387890792Sgshapiro	**  cases here: turn on/off a feature.
387964562Sgshapiro	*/
388064562Sgshapiro
388190792Sgshapiro	for (i = 1; pvp[i] != NULL; i++)
388264562Sgshapiro	{
388390792Sgshapiro		c = pvp[i][0];
388490792Sgshapiro		j = 0;
388590792Sgshapiro		for (;;)
388664562Sgshapiro		{
388790792Sgshapiro			if ((opt = srv_feat_table[j].srvf_opt) == '\0')
388864562Sgshapiro			{
388990792Sgshapiro				if (LogLevel > 9)
389090792Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
389190792Sgshapiro						  "srvfeatures: unknown feature %s",
389290792Sgshapiro						  pvp[i]);
389390792Sgshapiro				break;
389464562Sgshapiro			}
389590792Sgshapiro			if (c == opt)
389664562Sgshapiro			{
389790792Sgshapiro				features &= ~(srv_feat_table[j].srvf_flag);
389890792Sgshapiro				break;
389964562Sgshapiro			}
390090792Sgshapiro			if (c == tolower(opt))
390164562Sgshapiro			{
390290792Sgshapiro				features |= srv_feat_table[j].srvf_flag;
390390792Sgshapiro				break;
390464562Sgshapiro			}
390590792Sgshapiro			++j;
390664562Sgshapiro		}
390764562Sgshapiro	}
390890792Sgshapiro	return features;
390964562Sgshapiro}
391064562Sgshapiro
391190792Sgshapiro/*
391238032Speter**  HELP -- implement the HELP command.
391338032Speter**
391438032Speter**	Parameters:
391538032Speter**		topic -- the topic we want help for.
391690792Sgshapiro**		e -- envelope.
391738032Speter**
391838032Speter**	Returns:
391938032Speter**		none.
392038032Speter**
392138032Speter**	Side Effects:
392238032Speter**		outputs the help file to message output.
392338032Speter*/
392464562Sgshapiro#define HELPVSTR	"#vers	"
392564562Sgshapiro#define HELPVERSION	2
392638032Speter
392738032Spetervoid
392864562Sgshapirohelp(topic, e)
392938032Speter	char *topic;
393064562Sgshapiro	ENVELOPE *e;
393138032Speter{
393290792Sgshapiro	register SM_FILE_T *hf;
393364562Sgshapiro	register char *p;
393438032Speter	int len;
393538032Speter	bool noinfo;
393690792Sgshapiro	bool first = true;
393764562Sgshapiro	long sff = SFF_OPENASROOT|SFF_REGONLY;
393838032Speter	char buf[MAXLINE];
393964562Sgshapiro	char inp[MAXLINE];
394064562Sgshapiro	static int foundvers = -1;
394138032Speter	extern char Version[];
394238032Speter
394338032Speter	if (DontLockReadFiles)
394438032Speter		sff |= SFF_NOLOCK;
394564562Sgshapiro	if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
394638032Speter		sff |= SFF_SAFEDIRPATH;
394738032Speter
394838032Speter	if (HelpFile == NULL ||
394938032Speter	    (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
395038032Speter	{
395138032Speter		/* no help */
395238032Speter		errno = 0;
395364562Sgshapiro		message("502 5.3.0 Sendmail %s -- HELP not implemented",
395464562Sgshapiro			Version);
395538032Speter		return;
395638032Speter	}
395738032Speter
395838032Speter	if (topic == NULL || *topic == '\0')
395938032Speter	{
396038032Speter		topic = "smtp";
396190792Sgshapiro		noinfo = false;
396238032Speter	}
396338032Speter	else
396438032Speter	{
396538032Speter		makelower(topic);
396690792Sgshapiro		noinfo = true;
396738032Speter	}
396838032Speter
396938032Speter	len = strlen(topic);
397038032Speter
397190792Sgshapiro	while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
397238032Speter	{
397364562Sgshapiro		if (buf[0] == '#')
397464562Sgshapiro		{
397564562Sgshapiro			if (foundvers < 0 &&
397664562Sgshapiro			    strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
397764562Sgshapiro			{
397864562Sgshapiro				int h;
397964562Sgshapiro
398090792Sgshapiro				if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
398190792Sgshapiro						 &h) == 1)
398264562Sgshapiro					foundvers = h;
398364562Sgshapiro			}
398464562Sgshapiro			continue;
398564562Sgshapiro		}
398638032Speter		if (strncmp(buf, topic, len) == 0)
398738032Speter		{
398864562Sgshapiro			if (first)
398964562Sgshapiro			{
399090792Sgshapiro				first = false;
399138032Speter
399264562Sgshapiro				/* print version if no/old vers# in file */
399364562Sgshapiro				if (foundvers < 2 && !noinfo)
399464562Sgshapiro					message("214-2.0.0 This is Sendmail version %s", Version);
399564562Sgshapiro			}
399664562Sgshapiro			p = strpbrk(buf, " \t");
399738032Speter			if (p == NULL)
399864562Sgshapiro				p = buf + strlen(buf) - 1;
399938032Speter			else
400038032Speter				p++;
400190792Sgshapiro			fixcrlf(p, true);
400264562Sgshapiro			if (foundvers >= 2)
400364562Sgshapiro			{
400464562Sgshapiro				translate_dollars(p);
400564562Sgshapiro				expand(p, inp, sizeof inp, e);
400664562Sgshapiro				p = inp;
400764562Sgshapiro			}
400864562Sgshapiro			message("214-2.0.0 %s", p);
400990792Sgshapiro			noinfo = false;
401038032Speter		}
401138032Speter	}
401238032Speter
401338032Speter	if (noinfo)
401464562Sgshapiro		message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
401538032Speter	else
401664562Sgshapiro		message("214 2.0.0 End of HELP info");
401764562Sgshapiro
401864562Sgshapiro	if (foundvers != 0 && foundvers < HELPVERSION)
401964562Sgshapiro	{
402064562Sgshapiro		if (LogLevel > 1)
402164562Sgshapiro			sm_syslog(LOG_WARNING, e->e_id,
402264562Sgshapiro				  "%s too old (require version %d)",
402364562Sgshapiro				  HelpFile, HELPVERSION);
402464562Sgshapiro
402564562Sgshapiro		/* avoid log next time */
402664562Sgshapiro		foundvers = 0;
402764562Sgshapiro	}
402864562Sgshapiro
402990792Sgshapiro	(void) sm_io_close(hf, SM_TIME_DEFAULT);
403038032Speter}
4031