xref: /titanic_53/usr/src/cmd/sendmail/src/srvrsmtp.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1998-2005 Sendmail, Inc. and its suppliers.
3*7c478bd9Sstevel@tonic-gate  *	All rights reserved.
4*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5*7c478bd9Sstevel@tonic-gate  * Copyright (c) 1988, 1993
6*7c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
7*7c478bd9Sstevel@tonic-gate  *
8*7c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
9*7c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
10*7c478bd9Sstevel@tonic-gate  * the sendmail distribution.
11*7c478bd9Sstevel@tonic-gate  *
12*7c478bd9Sstevel@tonic-gate  */
13*7c478bd9Sstevel@tonic-gate 
14*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
15*7c478bd9Sstevel@tonic-gate 
16*7c478bd9Sstevel@tonic-gate #include <sendmail.h>
17*7c478bd9Sstevel@tonic-gate #if MILTER
18*7c478bd9Sstevel@tonic-gate # include <libmilter/mfapi.h>
19*7c478bd9Sstevel@tonic-gate # include <libmilter/mfdef.h>
20*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
21*7c478bd9Sstevel@tonic-gate 
22*7c478bd9Sstevel@tonic-gate SM_RCSID("@(#)$Id: srvrsmtp.c,v 8.906 2005/03/16 00:36:09 ca Exp $")
23*7c478bd9Sstevel@tonic-gate 
24*7c478bd9Sstevel@tonic-gate #include <sys/time.h>
25*7c478bd9Sstevel@tonic-gate #include <sm/fdset.h>
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #if SASL || STARTTLS
28*7c478bd9Sstevel@tonic-gate # include "sfsasl.h"
29*7c478bd9Sstevel@tonic-gate #endif /* SASL || STARTTLS */
30*7c478bd9Sstevel@tonic-gate #if SASL
31*7c478bd9Sstevel@tonic-gate # define ENC64LEN(l)	(((l) + 2) * 4 / 3 + 1)
32*7c478bd9Sstevel@tonic-gate static int saslmechs __P((sasl_conn_t *, char **));
33*7c478bd9Sstevel@tonic-gate #endif /* SASL */
34*7c478bd9Sstevel@tonic-gate #if STARTTLS
35*7c478bd9Sstevel@tonic-gate # include <sysexits.h>
36*7c478bd9Sstevel@tonic-gate 
37*7c478bd9Sstevel@tonic-gate static SSL_CTX	*srv_ctx = NULL;	/* TLS server context */
38*7c478bd9Sstevel@tonic-gate static SSL	*srv_ssl = NULL;	/* per connection context */
39*7c478bd9Sstevel@tonic-gate 
40*7c478bd9Sstevel@tonic-gate static bool	tls_ok_srv = false;
41*7c478bd9Sstevel@tonic-gate 
42*7c478bd9Sstevel@tonic-gate extern void	tls_set_verify __P((SSL_CTX *, SSL *, bool));
43*7c478bd9Sstevel@tonic-gate # define TLS_VERIFY_CLIENT() tls_set_verify(srv_ctx, srv_ssl, \
44*7c478bd9Sstevel@tonic-gate 				bitset(SRV_VRFY_CLT, features))
45*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
46*7c478bd9Sstevel@tonic-gate 
47*7c478bd9Sstevel@tonic-gate /* server features */
48*7c478bd9Sstevel@tonic-gate #define SRV_NONE	0x0000	/* none... */
49*7c478bd9Sstevel@tonic-gate #define SRV_OFFER_TLS	0x0001	/* offer STARTTLS */
50*7c478bd9Sstevel@tonic-gate #define SRV_VRFY_CLT	0x0002	/* request a cert */
51*7c478bd9Sstevel@tonic-gate #define SRV_OFFER_AUTH	0x0004	/* offer AUTH */
52*7c478bd9Sstevel@tonic-gate #define SRV_OFFER_ETRN	0x0008	/* offer ETRN */
53*7c478bd9Sstevel@tonic-gate #define SRV_OFFER_VRFY	0x0010	/* offer VRFY (not yet used) */
54*7c478bd9Sstevel@tonic-gate #define SRV_OFFER_EXPN	0x0020	/* offer EXPN */
55*7c478bd9Sstevel@tonic-gate #define SRV_OFFER_VERB	0x0040	/* offer VERB */
56*7c478bd9Sstevel@tonic-gate #define SRV_OFFER_DSN	0x0080	/* offer DSN */
57*7c478bd9Sstevel@tonic-gate #if PIPELINING
58*7c478bd9Sstevel@tonic-gate # define SRV_OFFER_PIPE	0x0100	/* offer PIPELINING */
59*7c478bd9Sstevel@tonic-gate # if _FFR_NO_PIPE
60*7c478bd9Sstevel@tonic-gate #  define SRV_NO_PIPE	0x0200	/* disable PIPELINING, sleep if used */
61*7c478bd9Sstevel@tonic-gate # endif /* _FFR_NO_PIPE */
62*7c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
63*7c478bd9Sstevel@tonic-gate #define SRV_REQ_AUTH	0x0400	/* require AUTH */
64*7c478bd9Sstevel@tonic-gate #define SRV_REQ_SEC	0x0800	/* require security - equiv to AuthOptions=p */
65*7c478bd9Sstevel@tonic-gate #define SRV_TMP_FAIL	0x1000	/* ruleset caused a temporary failure */
66*7c478bd9Sstevel@tonic-gate 
67*7c478bd9Sstevel@tonic-gate static unsigned int	srvfeatures __P((ENVELOPE *, char *, unsigned int));
68*7c478bd9Sstevel@tonic-gate 
69*7c478bd9Sstevel@tonic-gate #define	STOP_ATTACK	((time_t) -1)
70*7c478bd9Sstevel@tonic-gate static time_t	checksmtpattack __P((volatile unsigned int *, unsigned int,
71*7c478bd9Sstevel@tonic-gate 				     bool, char *, ENVELOPE *));
72*7c478bd9Sstevel@tonic-gate static void	mail_esmtp_args __P((char *, char *, ENVELOPE *));
73*7c478bd9Sstevel@tonic-gate static void	printvrfyaddr __P((ADDRESS *, bool, bool));
74*7c478bd9Sstevel@tonic-gate static void	rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
75*7c478bd9Sstevel@tonic-gate static char	*skipword __P((char *volatile, char *));
76*7c478bd9Sstevel@tonic-gate static void	setup_smtpd_io __P((void));
77*7c478bd9Sstevel@tonic-gate 
78*7c478bd9Sstevel@tonic-gate #if SASL
79*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
80*7c478bd9Sstevel@tonic-gate static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
81*7c478bd9Sstevel@tonic-gate 				char *_remoteip, char *_localip,
82*7c478bd9Sstevel@tonic-gate 				char *_auth_id, sasl_ssf_t *_ext_ssf));
83*7c478bd9Sstevel@tonic-gate 
84*7c478bd9Sstevel@tonic-gate # define RESET_SASLCONN	\
85*7c478bd9Sstevel@tonic-gate 	do							\
86*7c478bd9Sstevel@tonic-gate 	{							\
87*7c478bd9Sstevel@tonic-gate 		result = reset_saslconn(&conn, AuthRealm, remoteip, \
88*7c478bd9Sstevel@tonic-gate 					localip, auth_id, &ext_ssf); \
89*7c478bd9Sstevel@tonic-gate 		if (result != SASL_OK)				\
90*7c478bd9Sstevel@tonic-gate 			sasl_ok = false;			\
91*7c478bd9Sstevel@tonic-gate 	} while (0)
92*7c478bd9Sstevel@tonic-gate 
93*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
94*7c478bd9Sstevel@tonic-gate static int reset_saslconn __P((sasl_conn_t **_conn, char *_hostname,
95*7c478bd9Sstevel@tonic-gate 				struct sockaddr_in *_saddr_r,
96*7c478bd9Sstevel@tonic-gate 				struct sockaddr_in *_saddr_l,
97*7c478bd9Sstevel@tonic-gate 				sasl_external_properties_t *_ext_ssf));
98*7c478bd9Sstevel@tonic-gate # define RESET_SASLCONN	\
99*7c478bd9Sstevel@tonic-gate 	do							\
100*7c478bd9Sstevel@tonic-gate 	{							\
101*7c478bd9Sstevel@tonic-gate 		result = reset_saslconn(&conn, AuthRealm, &saddr_r, \
102*7c478bd9Sstevel@tonic-gate 					&saddr_l, &ext_ssf);	\
103*7c478bd9Sstevel@tonic-gate 		if (result != SASL_OK)				\
104*7c478bd9Sstevel@tonic-gate 			sasl_ok = false;			\
105*7c478bd9Sstevel@tonic-gate 	} while (0)
106*7c478bd9Sstevel@tonic-gate 
107*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
108*7c478bd9Sstevel@tonic-gate #endif /* SASL */
109*7c478bd9Sstevel@tonic-gate 
110*7c478bd9Sstevel@tonic-gate extern ENVELOPE	BlankEnvelope;
111*7c478bd9Sstevel@tonic-gate 
112*7c478bd9Sstevel@tonic-gate #define NBADRCPTS						\
113*7c478bd9Sstevel@tonic-gate 	do							\
114*7c478bd9Sstevel@tonic-gate 	{							\
115*7c478bd9Sstevel@tonic-gate 		char buf[16];					\
116*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(buf, sizeof buf, "%d",	\
117*7c478bd9Sstevel@tonic-gate 			BadRcptThrottle > 0 && n_badrcpts > BadRcptThrottle \
118*7c478bd9Sstevel@tonic-gate 				? n_badrcpts - 1 : n_badrcpts);	\
119*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_TEMP, macid("{nbadrcpts}"), buf); \
120*7c478bd9Sstevel@tonic-gate 	} while (0)
121*7c478bd9Sstevel@tonic-gate 
122*7c478bd9Sstevel@tonic-gate #define SKIP_SPACE(s)	while (isascii(*s) && isspace(*s))	\
123*7c478bd9Sstevel@tonic-gate 				(s)++
124*7c478bd9Sstevel@tonic-gate 
125*7c478bd9Sstevel@tonic-gate /*
126*7c478bd9Sstevel@tonic-gate **  SMTP -- run the SMTP protocol.
127*7c478bd9Sstevel@tonic-gate **
128*7c478bd9Sstevel@tonic-gate **	Parameters:
129*7c478bd9Sstevel@tonic-gate **		nullserver -- if non-NULL, rejection message for
130*7c478bd9Sstevel@tonic-gate **			(almost) all SMTP commands.
131*7c478bd9Sstevel@tonic-gate **		d_flags -- daemon flags
132*7c478bd9Sstevel@tonic-gate **		e -- the envelope.
133*7c478bd9Sstevel@tonic-gate **
134*7c478bd9Sstevel@tonic-gate **	Returns:
135*7c478bd9Sstevel@tonic-gate **		never.
136*7c478bd9Sstevel@tonic-gate **
137*7c478bd9Sstevel@tonic-gate **	Side Effects:
138*7c478bd9Sstevel@tonic-gate **		Reads commands from the input channel and processes them.
139*7c478bd9Sstevel@tonic-gate */
140*7c478bd9Sstevel@tonic-gate 
141*7c478bd9Sstevel@tonic-gate /*
142*7c478bd9Sstevel@tonic-gate **  Notice: The smtp server doesn't have a session context like the client
143*7c478bd9Sstevel@tonic-gate **	side has (mci). Therefore some data (session oriented) is allocated
144*7c478bd9Sstevel@tonic-gate **	or assigned to the "wrong" structure (esp. STARTTLS, AUTH).
145*7c478bd9Sstevel@tonic-gate **	This should be fixed in a successor version.
146*7c478bd9Sstevel@tonic-gate */
147*7c478bd9Sstevel@tonic-gate 
148*7c478bd9Sstevel@tonic-gate struct cmd
149*7c478bd9Sstevel@tonic-gate {
150*7c478bd9Sstevel@tonic-gate 	char	*cmd_name;	/* command name */
151*7c478bd9Sstevel@tonic-gate 	int	cmd_code;	/* internal code, see below */
152*7c478bd9Sstevel@tonic-gate };
153*7c478bd9Sstevel@tonic-gate 
154*7c478bd9Sstevel@tonic-gate /* values for cmd_code */
155*7c478bd9Sstevel@tonic-gate #define CMDERROR	0	/* bad command */
156*7c478bd9Sstevel@tonic-gate #define CMDMAIL	1	/* mail -- designate sender */
157*7c478bd9Sstevel@tonic-gate #define CMDRCPT	2	/* rcpt -- designate recipient */
158*7c478bd9Sstevel@tonic-gate #define CMDDATA	3	/* data -- send message text */
159*7c478bd9Sstevel@tonic-gate #define CMDRSET	4	/* rset -- reset state */
160*7c478bd9Sstevel@tonic-gate #define CMDVRFY	5	/* vrfy -- verify address */
161*7c478bd9Sstevel@tonic-gate #define CMDEXPN	6	/* expn -- expand address */
162*7c478bd9Sstevel@tonic-gate #define CMDNOOP	7	/* noop -- do nothing */
163*7c478bd9Sstevel@tonic-gate #define CMDQUIT	8	/* quit -- close connection and die */
164*7c478bd9Sstevel@tonic-gate #define CMDHELO	9	/* helo -- be polite */
165*7c478bd9Sstevel@tonic-gate #define CMDHELP	10	/* help -- give usage info */
166*7c478bd9Sstevel@tonic-gate #define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
167*7c478bd9Sstevel@tonic-gate #define CMDETRN	12	/* etrn -- flush queue */
168*7c478bd9Sstevel@tonic-gate #if SASL
169*7c478bd9Sstevel@tonic-gate # define CMDAUTH	13	/* auth -- SASL authenticate */
170*7c478bd9Sstevel@tonic-gate #endif /* SASL */
171*7c478bd9Sstevel@tonic-gate #if STARTTLS
172*7c478bd9Sstevel@tonic-gate # define CMDSTLS	14	/* STARTTLS -- start TLS session */
173*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
174*7c478bd9Sstevel@tonic-gate /* non-standard commands */
175*7c478bd9Sstevel@tonic-gate #define CMDVERB	17	/* verb -- go into verbose mode */
176*7c478bd9Sstevel@tonic-gate /* unimplemented commands from RFC 821 */
177*7c478bd9Sstevel@tonic-gate #define CMDUNIMPL	19	/* unimplemented rfc821 commands */
178*7c478bd9Sstevel@tonic-gate /* use this to catch and log "door handle" attempts on your system */
179*7c478bd9Sstevel@tonic-gate #define CMDLOGBOGUS	23	/* bogus command that should be logged */
180*7c478bd9Sstevel@tonic-gate /* debugging-only commands, only enabled if SMTPDEBUG is defined */
181*7c478bd9Sstevel@tonic-gate #define CMDDBGQSHOW	24	/* showq -- show send queue */
182*7c478bd9Sstevel@tonic-gate #define CMDDBGDEBUG	25	/* debug -- set debug mode */
183*7c478bd9Sstevel@tonic-gate 
184*7c478bd9Sstevel@tonic-gate /*
185*7c478bd9Sstevel@tonic-gate **  Note: If you change this list, remember to update 'helpfile'
186*7c478bd9Sstevel@tonic-gate */
187*7c478bd9Sstevel@tonic-gate 
188*7c478bd9Sstevel@tonic-gate static struct cmd	CmdTab[] =
189*7c478bd9Sstevel@tonic-gate {
190*7c478bd9Sstevel@tonic-gate 	{ "mail",	CMDMAIL		},
191*7c478bd9Sstevel@tonic-gate 	{ "rcpt",	CMDRCPT		},
192*7c478bd9Sstevel@tonic-gate 	{ "data",	CMDDATA		},
193*7c478bd9Sstevel@tonic-gate 	{ "rset",	CMDRSET		},
194*7c478bd9Sstevel@tonic-gate 	{ "vrfy",	CMDVRFY		},
195*7c478bd9Sstevel@tonic-gate 	{ "expn",	CMDEXPN		},
196*7c478bd9Sstevel@tonic-gate 	{ "help",	CMDHELP		},
197*7c478bd9Sstevel@tonic-gate 	{ "noop",	CMDNOOP		},
198*7c478bd9Sstevel@tonic-gate 	{ "quit",	CMDQUIT		},
199*7c478bd9Sstevel@tonic-gate 	{ "helo",	CMDHELO		},
200*7c478bd9Sstevel@tonic-gate 	{ "ehlo",	CMDEHLO		},
201*7c478bd9Sstevel@tonic-gate 	{ "etrn",	CMDETRN		},
202*7c478bd9Sstevel@tonic-gate 	{ "verb",	CMDVERB		},
203*7c478bd9Sstevel@tonic-gate 	{ "send",	CMDUNIMPL	},
204*7c478bd9Sstevel@tonic-gate 	{ "saml",	CMDUNIMPL	},
205*7c478bd9Sstevel@tonic-gate 	{ "soml",	CMDUNIMPL	},
206*7c478bd9Sstevel@tonic-gate 	{ "turn",	CMDUNIMPL	},
207*7c478bd9Sstevel@tonic-gate #if SASL
208*7c478bd9Sstevel@tonic-gate 	{ "auth",	CMDAUTH,	},
209*7c478bd9Sstevel@tonic-gate #endif /* SASL */
210*7c478bd9Sstevel@tonic-gate #if STARTTLS
211*7c478bd9Sstevel@tonic-gate 	{ "starttls",	CMDSTLS,	},
212*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
213*7c478bd9Sstevel@tonic-gate     /* remaining commands are here only to trap and log attempts to use them */
214*7c478bd9Sstevel@tonic-gate 	{ "showq",	CMDDBGQSHOW	},
215*7c478bd9Sstevel@tonic-gate 	{ "debug",	CMDDBGDEBUG	},
216*7c478bd9Sstevel@tonic-gate 	{ "wiz",	CMDLOGBOGUS	},
217*7c478bd9Sstevel@tonic-gate 
218*7c478bd9Sstevel@tonic-gate 	{ NULL,		CMDERROR	}
219*7c478bd9Sstevel@tonic-gate };
220*7c478bd9Sstevel@tonic-gate 
221*7c478bd9Sstevel@tonic-gate static char	*CurSmtpClient;		/* who's at the other end of channel */
222*7c478bd9Sstevel@tonic-gate 
223*7c478bd9Sstevel@tonic-gate #ifndef MAXBADCOMMANDS
224*7c478bd9Sstevel@tonic-gate # define MAXBADCOMMANDS 25	/* maximum number of bad commands */
225*7c478bd9Sstevel@tonic-gate #endif /* ! MAXBADCOMMANDS */
226*7c478bd9Sstevel@tonic-gate #ifndef MAXNOOPCOMMANDS
227*7c478bd9Sstevel@tonic-gate # define MAXNOOPCOMMANDS 20	/* max "noise" commands before slowdown */
228*7c478bd9Sstevel@tonic-gate #endif /* ! MAXNOOPCOMMANDS */
229*7c478bd9Sstevel@tonic-gate #ifndef MAXHELOCOMMANDS
230*7c478bd9Sstevel@tonic-gate # define MAXHELOCOMMANDS 3	/* max HELO/EHLO commands before slowdown */
231*7c478bd9Sstevel@tonic-gate #endif /* ! MAXHELOCOMMANDS */
232*7c478bd9Sstevel@tonic-gate #ifndef MAXVRFYCOMMANDS
233*7c478bd9Sstevel@tonic-gate # define MAXVRFYCOMMANDS 6	/* max VRFY/EXPN commands before slowdown */
234*7c478bd9Sstevel@tonic-gate #endif /* ! MAXVRFYCOMMANDS */
235*7c478bd9Sstevel@tonic-gate #ifndef MAXETRNCOMMANDS
236*7c478bd9Sstevel@tonic-gate # define MAXETRNCOMMANDS 8	/* max ETRN commands before slowdown */
237*7c478bd9Sstevel@tonic-gate #endif /* ! MAXETRNCOMMANDS */
238*7c478bd9Sstevel@tonic-gate #ifndef MAXTIMEOUT
239*7c478bd9Sstevel@tonic-gate # define MAXTIMEOUT (4 * 60)	/* max timeout for bad commands */
240*7c478bd9Sstevel@tonic-gate #endif /* ! MAXTIMEOUT */
241*7c478bd9Sstevel@tonic-gate 
242*7c478bd9Sstevel@tonic-gate /*
243*7c478bd9Sstevel@tonic-gate **  Maximum shift value to compute timeout for bad commands.
244*7c478bd9Sstevel@tonic-gate **  This introduces an upper limit of 2^MAXSHIFT for the timeout.
245*7c478bd9Sstevel@tonic-gate */
246*7c478bd9Sstevel@tonic-gate 
247*7c478bd9Sstevel@tonic-gate #ifndef MAXSHIFT
248*7c478bd9Sstevel@tonic-gate # define MAXSHIFT 8
249*7c478bd9Sstevel@tonic-gate #endif /* ! MAXSHIFT */
250*7c478bd9Sstevel@tonic-gate #if MAXSHIFT > 31
251*7c478bd9Sstevel@tonic-gate  ERROR _MAXSHIFT > 31 is invalid
252*7c478bd9Sstevel@tonic-gate #endif /* MAXSHIFT */
253*7c478bd9Sstevel@tonic-gate 
254*7c478bd9Sstevel@tonic-gate 
255*7c478bd9Sstevel@tonic-gate #if MAXBADCOMMANDS > 0
256*7c478bd9Sstevel@tonic-gate # define STOP_IF_ATTACK(r)	do		\
257*7c478bd9Sstevel@tonic-gate 	{					\
258*7c478bd9Sstevel@tonic-gate 		if ((r) == STOP_ATTACK)		\
259*7c478bd9Sstevel@tonic-gate 			goto stopattack;	\
260*7c478bd9Sstevel@tonic-gate 	} while (0)
261*7c478bd9Sstevel@tonic-gate 
262*7c478bd9Sstevel@tonic-gate #else /* MAXBADCOMMANDS > 0 */
263*7c478bd9Sstevel@tonic-gate # define STOP_IF_ATTACK(r)	r
264*7c478bd9Sstevel@tonic-gate #endif /* MAXBADCOMMANDS > 0 */
265*7c478bd9Sstevel@tonic-gate 
266*7c478bd9Sstevel@tonic-gate 
267*7c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
268*7c478bd9Sstevel@tonic-gate static SM_DEBUG_T DebugLeakSmtp = SM_DEBUG_INITIALIZER("leak_smtp",
269*7c478bd9Sstevel@tonic-gate 	"@(#)$Debug: leak_smtp - trace memory leaks during SMTP processing $");
270*7c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
271*7c478bd9Sstevel@tonic-gate 
272*7c478bd9Sstevel@tonic-gate typedef struct
273*7c478bd9Sstevel@tonic-gate {
274*7c478bd9Sstevel@tonic-gate 	bool	sm_gotmail;	/* mail command received */
275*7c478bd9Sstevel@tonic-gate 	unsigned int sm_nrcpts;	/* number of successful RCPT commands */
276*7c478bd9Sstevel@tonic-gate 	bool	sm_discard;
277*7c478bd9Sstevel@tonic-gate #if MILTER
278*7c478bd9Sstevel@tonic-gate 	bool	sm_milterize;
279*7c478bd9Sstevel@tonic-gate 	bool	sm_milterlist;	/* any filters in the list? */
280*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
281*7c478bd9Sstevel@tonic-gate 	char	*sm_quarmsg;	/* carry quarantining across messages */
282*7c478bd9Sstevel@tonic-gate } SMTP_T;
283*7c478bd9Sstevel@tonic-gate 
284*7c478bd9Sstevel@tonic-gate static bool	smtp_data __P((SMTP_T *, ENVELOPE *));
285*7c478bd9Sstevel@tonic-gate 
286*7c478bd9Sstevel@tonic-gate #define MSG_TEMPFAIL "451 4.3.2 Please try again later"
287*7c478bd9Sstevel@tonic-gate 
288*7c478bd9Sstevel@tonic-gate #if MILTER
289*7c478bd9Sstevel@tonic-gate # define MILTER_ABORT(e)	milter_abort((e))
290*7c478bd9Sstevel@tonic-gate 
291*7c478bd9Sstevel@tonic-gate # define MILTER_REPLY(str)						\
292*7c478bd9Sstevel@tonic-gate 	{								\
293*7c478bd9Sstevel@tonic-gate 		int savelogusrerrs = LogUsrErrs;			\
294*7c478bd9Sstevel@tonic-gate 									\
295*7c478bd9Sstevel@tonic-gate 		switch (state)						\
296*7c478bd9Sstevel@tonic-gate 		{							\
297*7c478bd9Sstevel@tonic-gate 		  case SMFIR_REPLYCODE:					\
298*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)				\
299*7c478bd9Sstevel@tonic-gate 			{						\
300*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,		\
301*7c478bd9Sstevel@tonic-gate 					  "Milter: %s=%s, reject=%s",	\
302*7c478bd9Sstevel@tonic-gate 					  str, addr, response);		\
303*7c478bd9Sstevel@tonic-gate 				LogUsrErrs = false;			\
304*7c478bd9Sstevel@tonic-gate 			}						\
305*7c478bd9Sstevel@tonic-gate 			if (strncmp(response, "421 ", 4) == 0)		\
306*7c478bd9Sstevel@tonic-gate 			{						\
307*7c478bd9Sstevel@tonic-gate 				bool tsave = QuickAbort;		\
308*7c478bd9Sstevel@tonic-gate 									\
309*7c478bd9Sstevel@tonic-gate 				QuickAbort = false;			\
310*7c478bd9Sstevel@tonic-gate 				usrerr(response);			\
311*7c478bd9Sstevel@tonic-gate 				QuickAbort = tsave;			\
312*7c478bd9Sstevel@tonic-gate 				e->e_sendqueue = NULL;			\
313*7c478bd9Sstevel@tonic-gate 				goto doquit;				\
314*7c478bd9Sstevel@tonic-gate 			}						\
315*7c478bd9Sstevel@tonic-gate 			else						\
316*7c478bd9Sstevel@tonic-gate 				usrerr(response);			\
317*7c478bd9Sstevel@tonic-gate 			break;						\
318*7c478bd9Sstevel@tonic-gate 									\
319*7c478bd9Sstevel@tonic-gate 		  case SMFIR_REJECT:					\
320*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)				\
321*7c478bd9Sstevel@tonic-gate 			{						\
322*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,		\
323*7c478bd9Sstevel@tonic-gate 					  "Milter: %s=%s, reject=550 5.7.1 Command rejected", \
324*7c478bd9Sstevel@tonic-gate 					  str, addr);			\
325*7c478bd9Sstevel@tonic-gate 				LogUsrErrs = false;			\
326*7c478bd9Sstevel@tonic-gate 			}						\
327*7c478bd9Sstevel@tonic-gate 			usrerr("550 5.7.1 Command rejected");		\
328*7c478bd9Sstevel@tonic-gate 			break;						\
329*7c478bd9Sstevel@tonic-gate 									\
330*7c478bd9Sstevel@tonic-gate 		  case SMFIR_DISCARD:					\
331*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)				\
332*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,		\
333*7c478bd9Sstevel@tonic-gate 					  "Milter: %s=%s, discard",	\
334*7c478bd9Sstevel@tonic-gate 					  str, addr);			\
335*7c478bd9Sstevel@tonic-gate 			e->e_flags |= EF_DISCARD;			\
336*7c478bd9Sstevel@tonic-gate 			break;						\
337*7c478bd9Sstevel@tonic-gate 									\
338*7c478bd9Sstevel@tonic-gate 		  case SMFIR_TEMPFAIL:					\
339*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)				\
340*7c478bd9Sstevel@tonic-gate 			{						\
341*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,		\
342*7c478bd9Sstevel@tonic-gate 					  "Milter: %s=%s, reject=%s",	\
343*7c478bd9Sstevel@tonic-gate 					  str, addr, MSG_TEMPFAIL);	\
344*7c478bd9Sstevel@tonic-gate 				LogUsrErrs = false;			\
345*7c478bd9Sstevel@tonic-gate 			}						\
346*7c478bd9Sstevel@tonic-gate 			usrerr(MSG_TEMPFAIL);				\
347*7c478bd9Sstevel@tonic-gate 			break;						\
348*7c478bd9Sstevel@tonic-gate 		}							\
349*7c478bd9Sstevel@tonic-gate 		LogUsrErrs = savelogusrerrs;				\
350*7c478bd9Sstevel@tonic-gate 		if (response != NULL)					\
351*7c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */			\
352*7c478bd9Sstevel@tonic-gate 	}
353*7c478bd9Sstevel@tonic-gate 
354*7c478bd9Sstevel@tonic-gate #else /* MILTER */
355*7c478bd9Sstevel@tonic-gate # define MILTER_ABORT(e)
356*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
357*7c478bd9Sstevel@tonic-gate 
358*7c478bd9Sstevel@tonic-gate /* clear all SMTP state (for HELO/EHLO/RSET) */
359*7c478bd9Sstevel@tonic-gate #define CLEAR_STATE(cmd)					\
360*7c478bd9Sstevel@tonic-gate do								\
361*7c478bd9Sstevel@tonic-gate {								\
362*7c478bd9Sstevel@tonic-gate 	/* abort milter filters */				\
363*7c478bd9Sstevel@tonic-gate 	MILTER_ABORT(e);					\
364*7c478bd9Sstevel@tonic-gate 								\
365*7c478bd9Sstevel@tonic-gate 	if (smtp.sm_nrcpts > 0)					\
366*7c478bd9Sstevel@tonic-gate 	{							\
367*7c478bd9Sstevel@tonic-gate 		logundelrcpts(e, cmd, 10, false);		\
368*7c478bd9Sstevel@tonic-gate 		smtp.sm_nrcpts = 0;				\
369*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM,			\
370*7c478bd9Sstevel@tonic-gate 			  macid("{nrcpts}"), "0");		\
371*7c478bd9Sstevel@tonic-gate 	}							\
372*7c478bd9Sstevel@tonic-gate 								\
373*7c478bd9Sstevel@tonic-gate 	e->e_sendqueue = NULL;					\
374*7c478bd9Sstevel@tonic-gate 	e->e_flags |= EF_CLRQUEUE;				\
375*7c478bd9Sstevel@tonic-gate 								\
376*7c478bd9Sstevel@tonic-gate 	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))	\
377*7c478bd9Sstevel@tonic-gate 		logsender(e, NULL);				\
378*7c478bd9Sstevel@tonic-gate 	e->e_flags &= ~EF_LOGSENDER;				\
379*7c478bd9Sstevel@tonic-gate 								\
380*7c478bd9Sstevel@tonic-gate 	/* clean up a bit */					\
381*7c478bd9Sstevel@tonic-gate 	smtp.sm_gotmail = false;				\
382*7c478bd9Sstevel@tonic-gate 	SuprErrs = true;					\
383*7c478bd9Sstevel@tonic-gate 	dropenvelope(e, true, false);				\
384*7c478bd9Sstevel@tonic-gate 	sm_rpool_free(e->e_rpool);				\
385*7c478bd9Sstevel@tonic-gate 	e = newenvelope(e, CurEnv, sm_rpool_new_x(NULL));	\
386*7c478bd9Sstevel@tonic-gate 	CurEnv = e;						\
387*7c478bd9Sstevel@tonic-gate 								\
388*7c478bd9Sstevel@tonic-gate 	/* put back discard bit */				\
389*7c478bd9Sstevel@tonic-gate 	if (smtp.sm_discard)					\
390*7c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_DISCARD;			\
391*7c478bd9Sstevel@tonic-gate 								\
392*7c478bd9Sstevel@tonic-gate 	/* restore connection quarantining */			\
393*7c478bd9Sstevel@tonic-gate 	if (smtp.sm_quarmsg == NULL)				\
394*7c478bd9Sstevel@tonic-gate 	{							\
395*7c478bd9Sstevel@tonic-gate 		e->e_quarmsg = NULL;				\
396*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM,			\
397*7c478bd9Sstevel@tonic-gate 			macid("{quarantine}"), "");		\
398*7c478bd9Sstevel@tonic-gate 	}							\
399*7c478bd9Sstevel@tonic-gate 	else							\
400*7c478bd9Sstevel@tonic-gate 	{							\
401*7c478bd9Sstevel@tonic-gate 		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool,	\
402*7c478bd9Sstevel@tonic-gate 						smtp.sm_quarmsg);	\
403*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"),	\
404*7c478bd9Sstevel@tonic-gate 			  e->e_quarmsg);			\
405*7c478bd9Sstevel@tonic-gate 	}							\
406*7c478bd9Sstevel@tonic-gate } while (0)
407*7c478bd9Sstevel@tonic-gate 
408*7c478bd9Sstevel@tonic-gate /* sleep to flatten out connection load */
409*7c478bd9Sstevel@tonic-gate #define MIN_DELAY_LOG	15	/* wait before logging this again */
410*7c478bd9Sstevel@tonic-gate 
411*7c478bd9Sstevel@tonic-gate /* is it worth setting the process title for 1s? */
412*7c478bd9Sstevel@tonic-gate #define DELAY_CONN(cmd)						\
413*7c478bd9Sstevel@tonic-gate 	if (DelayLA > 0 && (CurrentLA = getla()) >= DelayLA)	\
414*7c478bd9Sstevel@tonic-gate 	{							\
415*7c478bd9Sstevel@tonic-gate 		time_t dnow;					\
416*7c478bd9Sstevel@tonic-gate 								\
417*7c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, e,			\
418*7c478bd9Sstevel@tonic-gate 				"%s: %s: delaying %s: load average: %d", \
419*7c478bd9Sstevel@tonic-gate 				qid_printname(e), CurSmtpClient,	\
420*7c478bd9Sstevel@tonic-gate 				cmd, DelayLA);	\
421*7c478bd9Sstevel@tonic-gate 		if (LogLevel > 8 && (dnow = curtime()) > log_delay)	\
422*7c478bd9Sstevel@tonic-gate 		{						\
423*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,		\
424*7c478bd9Sstevel@tonic-gate 				  "delaying=%s, load average=%d >= %d",	\
425*7c478bd9Sstevel@tonic-gate 				  cmd, CurrentLA, DelayLA);		\
426*7c478bd9Sstevel@tonic-gate 			log_delay = dnow + MIN_DELAY_LOG;	\
427*7c478bd9Sstevel@tonic-gate 		}						\
428*7c478bd9Sstevel@tonic-gate 		(void) sleep(1);				\
429*7c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, e, "%s %s: %.80s",	\
430*7c478bd9Sstevel@tonic-gate 				qid_printname(e), CurSmtpClient, inp);	\
431*7c478bd9Sstevel@tonic-gate 	}
432*7c478bd9Sstevel@tonic-gate 
433*7c478bd9Sstevel@tonic-gate 
434*7c478bd9Sstevel@tonic-gate void
435*7c478bd9Sstevel@tonic-gate smtp(nullserver, d_flags, e)
436*7c478bd9Sstevel@tonic-gate 	char *volatile nullserver;
437*7c478bd9Sstevel@tonic-gate 	BITMAP256 d_flags;
438*7c478bd9Sstevel@tonic-gate 	register ENVELOPE *volatile e;
439*7c478bd9Sstevel@tonic-gate {
440*7c478bd9Sstevel@tonic-gate 	register char *volatile p;
441*7c478bd9Sstevel@tonic-gate 	register struct cmd *volatile c = NULL;
442*7c478bd9Sstevel@tonic-gate 	char *cmd;
443*7c478bd9Sstevel@tonic-gate 	auto ADDRESS *vrfyqueue;
444*7c478bd9Sstevel@tonic-gate 	ADDRESS *a;
445*7c478bd9Sstevel@tonic-gate 	volatile bool gothello;		/* helo command received */
446*7c478bd9Sstevel@tonic-gate 	bool vrfy;			/* set if this is a vrfy command */
447*7c478bd9Sstevel@tonic-gate 	char *volatile protocol;	/* sending protocol */
448*7c478bd9Sstevel@tonic-gate 	char *volatile sendinghost;	/* sending hostname */
449*7c478bd9Sstevel@tonic-gate 	char *volatile peerhostname;	/* name of SMTP peer or "localhost" */
450*7c478bd9Sstevel@tonic-gate 	auto char *delimptr;
451*7c478bd9Sstevel@tonic-gate 	char *id;
452*7c478bd9Sstevel@tonic-gate 	volatile unsigned int n_badcmds = 0;	/* count of bad commands */
453*7c478bd9Sstevel@tonic-gate 	volatile unsigned int n_badrcpts = 0;	/* number of rejected RCPT */
454*7c478bd9Sstevel@tonic-gate 	volatile unsigned int n_verifies = 0;	/* count of VRFY/EXPN */
455*7c478bd9Sstevel@tonic-gate 	volatile unsigned int n_etrn = 0;	/* count of ETRN */
456*7c478bd9Sstevel@tonic-gate 	volatile unsigned int n_noop = 0;	/* count of NOOP/VERB/etc */
457*7c478bd9Sstevel@tonic-gate 	volatile unsigned int n_helo = 0;	/* count of HELO/EHLO */
458*7c478bd9Sstevel@tonic-gate 	volatile int save_sevenbitinput;
459*7c478bd9Sstevel@tonic-gate 	bool ok;
460*7c478bd9Sstevel@tonic-gate #if _FFR_BLOCK_PROXIES
461*7c478bd9Sstevel@tonic-gate 	volatile bool first;
462*7c478bd9Sstevel@tonic-gate #endif /* _FFR_BLOCK_PROXIES */
463*7c478bd9Sstevel@tonic-gate 	volatile bool tempfail = false;
464*7c478bd9Sstevel@tonic-gate 	volatile time_t wt;		/* timeout after too many commands */
465*7c478bd9Sstevel@tonic-gate 	volatile time_t previous;	/* time after checksmtpattack() */
466*7c478bd9Sstevel@tonic-gate 	volatile bool lognullconnection = true;
467*7c478bd9Sstevel@tonic-gate 	register char *q;
468*7c478bd9Sstevel@tonic-gate 	SMTP_T smtp;
469*7c478bd9Sstevel@tonic-gate 	char *addr;
470*7c478bd9Sstevel@tonic-gate 	char *greetcode = "220";
471*7c478bd9Sstevel@tonic-gate 	char *hostname;			/* my hostname ($j) */
472*7c478bd9Sstevel@tonic-gate 	QUEUE_CHAR *new;
473*7c478bd9Sstevel@tonic-gate 	int argno;
474*7c478bd9Sstevel@tonic-gate 	char *args[MAXSMTPARGS];
475*7c478bd9Sstevel@tonic-gate 	char inp[MAXLINE];
476*7c478bd9Sstevel@tonic-gate 	char cmdbuf[MAXLINE];
477*7c478bd9Sstevel@tonic-gate #if SASL
478*7c478bd9Sstevel@tonic-gate 	sasl_conn_t *conn;
479*7c478bd9Sstevel@tonic-gate 	volatile bool sasl_ok;
480*7c478bd9Sstevel@tonic-gate 	volatile unsigned int n_auth = 0;	/* count of AUTH commands */
481*7c478bd9Sstevel@tonic-gate 	bool ismore;
482*7c478bd9Sstevel@tonic-gate 	int result;
483*7c478bd9Sstevel@tonic-gate 	volatile int authenticating;
484*7c478bd9Sstevel@tonic-gate 	char *user;
485*7c478bd9Sstevel@tonic-gate 	char *in, *out2;
486*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
487*7c478bd9Sstevel@tonic-gate 	char *auth_id;
488*7c478bd9Sstevel@tonic-gate 	const char *out;
489*7c478bd9Sstevel@tonic-gate 	sasl_ssf_t ext_ssf;
490*7c478bd9Sstevel@tonic-gate 	char localip[60], remoteip[60];
491*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
492*7c478bd9Sstevel@tonic-gate 	char *out;
493*7c478bd9Sstevel@tonic-gate 	const char *errstr;
494*7c478bd9Sstevel@tonic-gate 	sasl_external_properties_t ext_ssf;
495*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in saddr_l;
496*7c478bd9Sstevel@tonic-gate 	struct sockaddr_in saddr_r;
497*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
498*7c478bd9Sstevel@tonic-gate 	sasl_security_properties_t ssp;
499*7c478bd9Sstevel@tonic-gate 	sasl_ssf_t *ssf;
500*7c478bd9Sstevel@tonic-gate 	unsigned int inlen, out2len;
501*7c478bd9Sstevel@tonic-gate 	unsigned int outlen;
502*7c478bd9Sstevel@tonic-gate 	char *volatile auth_type;
503*7c478bd9Sstevel@tonic-gate 	char *mechlist;
504*7c478bd9Sstevel@tonic-gate 	volatile unsigned int n_mechs;
505*7c478bd9Sstevel@tonic-gate 	unsigned int len;
506*7c478bd9Sstevel@tonic-gate #endif /* SASL */
507*7c478bd9Sstevel@tonic-gate 	int r;
508*7c478bd9Sstevel@tonic-gate #if STARTTLS
509*7c478bd9Sstevel@tonic-gate 	int fdfl;
510*7c478bd9Sstevel@tonic-gate 	int rfd, wfd;
511*7c478bd9Sstevel@tonic-gate 	volatile bool tls_active = false;
512*7c478bd9Sstevel@tonic-gate 	volatile bool smtps = bitnset(D_SMTPS, d_flags);
513*7c478bd9Sstevel@tonic-gate 	bool saveQuickAbort;
514*7c478bd9Sstevel@tonic-gate 	bool saveSuprErrs;
515*7c478bd9Sstevel@tonic-gate 	time_t tlsstart;
516*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
517*7c478bd9Sstevel@tonic-gate 	volatile unsigned int features;
518*7c478bd9Sstevel@tonic-gate #if PIPELINING
519*7c478bd9Sstevel@tonic-gate # if _FFR_NO_PIPE
520*7c478bd9Sstevel@tonic-gate 	int np_log = 0;
521*7c478bd9Sstevel@tonic-gate # endif /* _FFR_NO_PIPE */
522*7c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
523*7c478bd9Sstevel@tonic-gate 	volatile time_t log_delay = (time_t) 0;
524*7c478bd9Sstevel@tonic-gate 
525*7c478bd9Sstevel@tonic-gate 	save_sevenbitinput = SevenBitInput;
526*7c478bd9Sstevel@tonic-gate 	smtp.sm_nrcpts = 0;
527*7c478bd9Sstevel@tonic-gate #if MILTER
528*7c478bd9Sstevel@tonic-gate 	smtp.sm_milterize = (nullserver == NULL);
529*7c478bd9Sstevel@tonic-gate 	smtp.sm_milterlist = false;
530*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
531*7c478bd9Sstevel@tonic-gate 
532*7c478bd9Sstevel@tonic-gate 	/* setup I/O fd correctly for the SMTP server */
533*7c478bd9Sstevel@tonic-gate 	setup_smtpd_io();
534*7c478bd9Sstevel@tonic-gate 
535*7c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
536*7c478bd9Sstevel@tonic-gate 	if (sm_debug_active(&DebugLeakSmtp, 1))
537*7c478bd9Sstevel@tonic-gate 	{
538*7c478bd9Sstevel@tonic-gate 		sm_heap_newgroup();
539*7c478bd9Sstevel@tonic-gate 		sm_dprintf("smtp() heap group #%d\n", sm_heap_group());
540*7c478bd9Sstevel@tonic-gate 	}
541*7c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
542*7c478bd9Sstevel@tonic-gate 
543*7c478bd9Sstevel@tonic-gate 	/* XXX the rpool should be set when e is initialized in main() */
544*7c478bd9Sstevel@tonic-gate 	e->e_rpool = sm_rpool_new_x(NULL);
545*7c478bd9Sstevel@tonic-gate 	e->e_macro.mac_rpool = e->e_rpool;
546*7c478bd9Sstevel@tonic-gate 
547*7c478bd9Sstevel@tonic-gate 	settime(e);
548*7c478bd9Sstevel@tonic-gate 	sm_getla();
549*7c478bd9Sstevel@tonic-gate 	peerhostname = RealHostName;
550*7c478bd9Sstevel@tonic-gate 	if (peerhostname == NULL)
551*7c478bd9Sstevel@tonic-gate 		peerhostname = "localhost";
552*7c478bd9Sstevel@tonic-gate 	CurHostName = peerhostname;
553*7c478bd9Sstevel@tonic-gate 	CurSmtpClient = macvalue('_', e);
554*7c478bd9Sstevel@tonic-gate 	if (CurSmtpClient == NULL)
555*7c478bd9Sstevel@tonic-gate 		CurSmtpClient = CurHostName;
556*7c478bd9Sstevel@tonic-gate 
557*7c478bd9Sstevel@tonic-gate 	/* check_relay may have set discard bit, save for later */
558*7c478bd9Sstevel@tonic-gate 	smtp.sm_discard = bitset(EF_DISCARD, e->e_flags);
559*7c478bd9Sstevel@tonic-gate 
560*7c478bd9Sstevel@tonic-gate #if PIPELINING
561*7c478bd9Sstevel@tonic-gate 	/* auto-flush output when reading input */
562*7c478bd9Sstevel@tonic-gate 	(void) sm_io_autoflush(InChannel, OutChannel);
563*7c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
564*7c478bd9Sstevel@tonic-gate 
565*7c478bd9Sstevel@tonic-gate 	sm_setproctitle(true, e, "server %s startup", CurSmtpClient);
566*7c478bd9Sstevel@tonic-gate 
567*7c478bd9Sstevel@tonic-gate 	/* Set default features for server. */
568*7c478bd9Sstevel@tonic-gate 	features = ((bitset(PRIV_NOETRN, PrivacyFlags) ||
569*7c478bd9Sstevel@tonic-gate 		     bitnset(D_NOETRN, d_flags)) ? SRV_NONE : SRV_OFFER_ETRN)
570*7c478bd9Sstevel@tonic-gate 		| (bitnset(D_AUTHREQ, d_flags) ? SRV_REQ_AUTH : SRV_NONE)
571*7c478bd9Sstevel@tonic-gate 		| (bitset(PRIV_NOEXPN, PrivacyFlags) ? SRV_NONE
572*7c478bd9Sstevel@tonic-gate 			: (SRV_OFFER_EXPN
573*7c478bd9Sstevel@tonic-gate 			  | (bitset(PRIV_NOVERB, PrivacyFlags)
574*7c478bd9Sstevel@tonic-gate 			     ? SRV_NONE : SRV_OFFER_VERB)))
575*7c478bd9Sstevel@tonic-gate 		| (bitset(PRIV_NORECEIPTS, PrivacyFlags) ? SRV_NONE
576*7c478bd9Sstevel@tonic-gate 							 : SRV_OFFER_DSN)
577*7c478bd9Sstevel@tonic-gate #if SASL
578*7c478bd9Sstevel@tonic-gate 		| (bitnset(D_NOAUTH, d_flags) ? SRV_NONE : SRV_OFFER_AUTH)
579*7c478bd9Sstevel@tonic-gate 		| (bitset(SASL_SEC_NOPLAINTEXT, SASLOpts) ? SRV_REQ_SEC
580*7c478bd9Sstevel@tonic-gate 							  : SRV_NONE)
581*7c478bd9Sstevel@tonic-gate #endif /* SASL */
582*7c478bd9Sstevel@tonic-gate #if PIPELINING
583*7c478bd9Sstevel@tonic-gate 		| SRV_OFFER_PIPE
584*7c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
585*7c478bd9Sstevel@tonic-gate #if STARTTLS
586*7c478bd9Sstevel@tonic-gate 		| (bitnset(D_NOTLS, d_flags) ? SRV_NONE : SRV_OFFER_TLS)
587*7c478bd9Sstevel@tonic-gate 		| (bitset(TLS_I_NO_VRFY, TLS_Srv_Opts) ? SRV_NONE
588*7c478bd9Sstevel@tonic-gate 						       : SRV_VRFY_CLT)
589*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
590*7c478bd9Sstevel@tonic-gate 		;
591*7c478bd9Sstevel@tonic-gate 	if (nullserver == NULL)
592*7c478bd9Sstevel@tonic-gate 	{
593*7c478bd9Sstevel@tonic-gate 		features = srvfeatures(e, CurSmtpClient, features);
594*7c478bd9Sstevel@tonic-gate 		if (bitset(SRV_TMP_FAIL, features))
595*7c478bd9Sstevel@tonic-gate 		{
596*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 4)
597*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
598*7c478bd9Sstevel@tonic-gate 					  "ERROR: srv_features=tempfail, relay=%.100s, access temporarily disabled",
599*7c478bd9Sstevel@tonic-gate 					  CurSmtpClient);
600*7c478bd9Sstevel@tonic-gate 			nullserver = "450 4.3.0 Please try again later.";
601*7c478bd9Sstevel@tonic-gate 		}
602*7c478bd9Sstevel@tonic-gate 		else
603*7c478bd9Sstevel@tonic-gate 		{
604*7c478bd9Sstevel@tonic-gate #if PIPELINING
605*7c478bd9Sstevel@tonic-gate # if _FFR_NO_PIPE
606*7c478bd9Sstevel@tonic-gate 			if (bitset(SRV_NO_PIPE, features))
607*7c478bd9Sstevel@tonic-gate 			{
608*7c478bd9Sstevel@tonic-gate 				/* for consistency */
609*7c478bd9Sstevel@tonic-gate 				features &= ~SRV_OFFER_PIPE;
610*7c478bd9Sstevel@tonic-gate 			}
611*7c478bd9Sstevel@tonic-gate # endif /* _FFR_NO_PIPE */
612*7c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
613*7c478bd9Sstevel@tonic-gate #if SASL
614*7c478bd9Sstevel@tonic-gate 			if (bitset(SRV_REQ_SEC, features))
615*7c478bd9Sstevel@tonic-gate 				SASLOpts |= SASL_SEC_NOPLAINTEXT;
616*7c478bd9Sstevel@tonic-gate 			else
617*7c478bd9Sstevel@tonic-gate 				SASLOpts &= ~SASL_SEC_NOPLAINTEXT;
618*7c478bd9Sstevel@tonic-gate #endif /* SASL */
619*7c478bd9Sstevel@tonic-gate 		}
620*7c478bd9Sstevel@tonic-gate 	}
621*7c478bd9Sstevel@tonic-gate 	else if (strncmp(nullserver, "421 ", 4) == 0)
622*7c478bd9Sstevel@tonic-gate 	{
623*7c478bd9Sstevel@tonic-gate 		message(nullserver);
624*7c478bd9Sstevel@tonic-gate 		goto doquit;
625*7c478bd9Sstevel@tonic-gate 	}
626*7c478bd9Sstevel@tonic-gate 
627*7c478bd9Sstevel@tonic-gate 	hostname = macvalue('j', e);
628*7c478bd9Sstevel@tonic-gate #if SASL
629*7c478bd9Sstevel@tonic-gate 	if (AuthRealm == NULL)
630*7c478bd9Sstevel@tonic-gate 		AuthRealm = hostname;
631*7c478bd9Sstevel@tonic-gate 	sasl_ok = bitset(SRV_OFFER_AUTH, features);
632*7c478bd9Sstevel@tonic-gate 	n_mechs = 0;
633*7c478bd9Sstevel@tonic-gate 	authenticating = SASL_NOT_AUTH;
634*7c478bd9Sstevel@tonic-gate 
635*7c478bd9Sstevel@tonic-gate 	/* SASL server new connection */
636*7c478bd9Sstevel@tonic-gate 	if (sasl_ok)
637*7c478bd9Sstevel@tonic-gate 	{
638*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
639*7c478bd9Sstevel@tonic-gate 		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, NULL,
640*7c478bd9Sstevel@tonic-gate 					 NULL, 0, &conn);
641*7c478bd9Sstevel@tonic-gate # elif SASL > 10505
642*7c478bd9Sstevel@tonic-gate 		/* use empty realm: only works in SASL > 1.5.5 */
643*7c478bd9Sstevel@tonic-gate 		result = sasl_server_new("smtp", AuthRealm, "", NULL, 0, &conn);
644*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
645*7c478bd9Sstevel@tonic-gate 		/* use no realm -> realm is set to hostname by SASL lib */
646*7c478bd9Sstevel@tonic-gate 		result = sasl_server_new("smtp", AuthRealm, NULL, NULL, 0,
647*7c478bd9Sstevel@tonic-gate 					 &conn);
648*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
649*7c478bd9Sstevel@tonic-gate 		sasl_ok = result == SASL_OK;
650*7c478bd9Sstevel@tonic-gate 		if (!sasl_ok)
651*7c478bd9Sstevel@tonic-gate 		{
652*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 9)
653*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_WARNING, NOQID,
654*7c478bd9Sstevel@tonic-gate 					  "AUTH error: sasl_server_new failed=%d",
655*7c478bd9Sstevel@tonic-gate 					  result);
656*7c478bd9Sstevel@tonic-gate 		}
657*7c478bd9Sstevel@tonic-gate 	}
658*7c478bd9Sstevel@tonic-gate 	if (sasl_ok)
659*7c478bd9Sstevel@tonic-gate 	{
660*7c478bd9Sstevel@tonic-gate 		/*
661*7c478bd9Sstevel@tonic-gate 		**  SASL set properties for sasl
662*7c478bd9Sstevel@tonic-gate 		**  set local/remote IP
663*7c478bd9Sstevel@tonic-gate 		**  XXX Cyrus SASL v1 only supports IPv4
664*7c478bd9Sstevel@tonic-gate 		**
665*7c478bd9Sstevel@tonic-gate 		**  XXX where exactly are these used/required?
666*7c478bd9Sstevel@tonic-gate 		**  Kerberos_v4
667*7c478bd9Sstevel@tonic-gate 		*/
668*7c478bd9Sstevel@tonic-gate 
669*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
670*7c478bd9Sstevel@tonic-gate 		localip[0] = remoteip[0] = '\0';
671*7c478bd9Sstevel@tonic-gate #  if NETINET || NETINET6
672*7c478bd9Sstevel@tonic-gate 		in = macvalue(macid("{daemon_family}"), e);
673*7c478bd9Sstevel@tonic-gate 		if (in != NULL && (
674*7c478bd9Sstevel@tonic-gate #   if NETINET6
675*7c478bd9Sstevel@tonic-gate 		    strcmp(in, "inet6") == 0 ||
676*7c478bd9Sstevel@tonic-gate #   endif /* NETINET6 */
677*7c478bd9Sstevel@tonic-gate 		    strcmp(in, "inet") == 0))
678*7c478bd9Sstevel@tonic-gate 		{
679*7c478bd9Sstevel@tonic-gate 			SOCKADDR_LEN_T addrsize;
680*7c478bd9Sstevel@tonic-gate 			SOCKADDR saddr_l;
681*7c478bd9Sstevel@tonic-gate 			SOCKADDR saddr_r;
682*7c478bd9Sstevel@tonic-gate 
683*7c478bd9Sstevel@tonic-gate 			addrsize = sizeof(saddr_r);
684*7c478bd9Sstevel@tonic-gate 			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
685*7c478bd9Sstevel@tonic-gate 						      NULL),
686*7c478bd9Sstevel@tonic-gate 					(struct sockaddr *) &saddr_r,
687*7c478bd9Sstevel@tonic-gate 					&addrsize) == 0)
688*7c478bd9Sstevel@tonic-gate 			{
689*7c478bd9Sstevel@tonic-gate 				if (iptostring(&saddr_r, addrsize,
690*7c478bd9Sstevel@tonic-gate 					       remoteip, sizeof remoteip))
691*7c478bd9Sstevel@tonic-gate 				{
692*7c478bd9Sstevel@tonic-gate 					sasl_setprop(conn, SASL_IPREMOTEPORT,
693*7c478bd9Sstevel@tonic-gate 						     remoteip);
694*7c478bd9Sstevel@tonic-gate 				}
695*7c478bd9Sstevel@tonic-gate 				addrsize = sizeof(saddr_l);
696*7c478bd9Sstevel@tonic-gate 				if (getsockname(sm_io_getinfo(InChannel,
697*7c478bd9Sstevel@tonic-gate 							      SM_IO_WHAT_FD,
698*7c478bd9Sstevel@tonic-gate 							      NULL),
699*7c478bd9Sstevel@tonic-gate 						(struct sockaddr *) &saddr_l,
700*7c478bd9Sstevel@tonic-gate 						&addrsize) == 0)
701*7c478bd9Sstevel@tonic-gate 				{
702*7c478bd9Sstevel@tonic-gate 					if (iptostring(&saddr_l, addrsize,
703*7c478bd9Sstevel@tonic-gate 						       localip,
704*7c478bd9Sstevel@tonic-gate 						       sizeof localip))
705*7c478bd9Sstevel@tonic-gate 					{
706*7c478bd9Sstevel@tonic-gate 						sasl_setprop(conn,
707*7c478bd9Sstevel@tonic-gate 							     SASL_IPLOCALPORT,
708*7c478bd9Sstevel@tonic-gate 							     localip);
709*7c478bd9Sstevel@tonic-gate 					}
710*7c478bd9Sstevel@tonic-gate 				}
711*7c478bd9Sstevel@tonic-gate 			}
712*7c478bd9Sstevel@tonic-gate 		}
713*7c478bd9Sstevel@tonic-gate #  endif /* NETINET || NETINET6 */
714*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
715*7c478bd9Sstevel@tonic-gate #  if NETINET
716*7c478bd9Sstevel@tonic-gate 		in = macvalue(macid("{daemon_family}"), e);
717*7c478bd9Sstevel@tonic-gate 		if (in != NULL && strcmp(in, "inet") == 0)
718*7c478bd9Sstevel@tonic-gate 		{
719*7c478bd9Sstevel@tonic-gate 			SOCKADDR_LEN_T addrsize;
720*7c478bd9Sstevel@tonic-gate 
721*7c478bd9Sstevel@tonic-gate 			addrsize = sizeof(struct sockaddr_in);
722*7c478bd9Sstevel@tonic-gate 			if (getpeername(sm_io_getinfo(InChannel, SM_IO_WHAT_FD,
723*7c478bd9Sstevel@tonic-gate 						      NULL),
724*7c478bd9Sstevel@tonic-gate 					(struct sockaddr *)&saddr_r,
725*7c478bd9Sstevel@tonic-gate 					&addrsize) == 0)
726*7c478bd9Sstevel@tonic-gate 			{
727*7c478bd9Sstevel@tonic-gate 				sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
728*7c478bd9Sstevel@tonic-gate 				addrsize = sizeof(struct sockaddr_in);
729*7c478bd9Sstevel@tonic-gate 				if (getsockname(sm_io_getinfo(InChannel,
730*7c478bd9Sstevel@tonic-gate 							      SM_IO_WHAT_FD,
731*7c478bd9Sstevel@tonic-gate 							      NULL),
732*7c478bd9Sstevel@tonic-gate 						(struct sockaddr *)&saddr_l,
733*7c478bd9Sstevel@tonic-gate 						&addrsize) == 0)
734*7c478bd9Sstevel@tonic-gate 					sasl_setprop(conn, SASL_IP_LOCAL,
735*7c478bd9Sstevel@tonic-gate 						     &saddr_l);
736*7c478bd9Sstevel@tonic-gate 			}
737*7c478bd9Sstevel@tonic-gate 		}
738*7c478bd9Sstevel@tonic-gate #  endif /* NETINET */
739*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
740*7c478bd9Sstevel@tonic-gate 
741*7c478bd9Sstevel@tonic-gate 		auth_type = NULL;
742*7c478bd9Sstevel@tonic-gate 		mechlist = NULL;
743*7c478bd9Sstevel@tonic-gate 		user = NULL;
744*7c478bd9Sstevel@tonic-gate # if 0
745*7c478bd9Sstevel@tonic-gate 		macdefine(&BlankEnvelope.e_macro, A_PERM,
746*7c478bd9Sstevel@tonic-gate 			macid("{auth_author}"), NULL);
747*7c478bd9Sstevel@tonic-gate # endif /* 0 */
748*7c478bd9Sstevel@tonic-gate 
749*7c478bd9Sstevel@tonic-gate 		/* set properties */
750*7c478bd9Sstevel@tonic-gate 		(void) memset(&ssp, '\0', sizeof ssp);
751*7c478bd9Sstevel@tonic-gate 
752*7c478bd9Sstevel@tonic-gate 		/* XXX should these be options settable via .cf ? */
753*7c478bd9Sstevel@tonic-gate 		/* ssp.min_ssf = 0; is default due to memset() */
754*7c478bd9Sstevel@tonic-gate 		{
755*7c478bd9Sstevel@tonic-gate 			ssp.max_ssf = MaxSLBits;
756*7c478bd9Sstevel@tonic-gate 			ssp.maxbufsize = MAXOUTLEN;
757*7c478bd9Sstevel@tonic-gate 		}
758*7c478bd9Sstevel@tonic-gate 		ssp.security_flags = SASLOpts & SASL_SEC_MASK;
759*7c478bd9Sstevel@tonic-gate 		sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
760*7c478bd9Sstevel@tonic-gate 
761*7c478bd9Sstevel@tonic-gate 		if (sasl_ok)
762*7c478bd9Sstevel@tonic-gate 		{
763*7c478bd9Sstevel@tonic-gate 			/*
764*7c478bd9Sstevel@tonic-gate 			**  external security strength factor;
765*7c478bd9Sstevel@tonic-gate 			**	currently we have none so zero
766*7c478bd9Sstevel@tonic-gate 			*/
767*7c478bd9Sstevel@tonic-gate 
768*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
769*7c478bd9Sstevel@tonic-gate 			ext_ssf = 0;
770*7c478bd9Sstevel@tonic-gate 			auth_id = NULL;
771*7c478bd9Sstevel@tonic-gate 			sasl_ok = ((sasl_setprop(conn, SASL_SSF_EXTERNAL,
772*7c478bd9Sstevel@tonic-gate 						 &ext_ssf) == SASL_OK) &&
773*7c478bd9Sstevel@tonic-gate 				   (sasl_setprop(conn, SASL_AUTH_EXTERNAL,
774*7c478bd9Sstevel@tonic-gate 						 auth_id) == SASL_OK));
775*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
776*7c478bd9Sstevel@tonic-gate 			ext_ssf.ssf = 0;
777*7c478bd9Sstevel@tonic-gate 			ext_ssf.auth_id = NULL;
778*7c478bd9Sstevel@tonic-gate 			sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
779*7c478bd9Sstevel@tonic-gate 					       &ext_ssf) == SASL_OK;
780*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
781*7c478bd9Sstevel@tonic-gate 		}
782*7c478bd9Sstevel@tonic-gate 		if (sasl_ok)
783*7c478bd9Sstevel@tonic-gate 			n_mechs = saslmechs(conn, &mechlist);
784*7c478bd9Sstevel@tonic-gate 	}
785*7c478bd9Sstevel@tonic-gate #endif /* SASL */
786*7c478bd9Sstevel@tonic-gate 
787*7c478bd9Sstevel@tonic-gate #if STARTTLS
788*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
789*7c478bd9Sstevel@tonic-gate 
790*7c478bd9Sstevel@tonic-gate #if MILTER
791*7c478bd9Sstevel@tonic-gate 	if (smtp.sm_milterize)
792*7c478bd9Sstevel@tonic-gate 	{
793*7c478bd9Sstevel@tonic-gate 		char state;
794*7c478bd9Sstevel@tonic-gate 
795*7c478bd9Sstevel@tonic-gate 		/* initialize mail filter connection */
796*7c478bd9Sstevel@tonic-gate 		smtp.sm_milterlist = milter_init(e, &state);
797*7c478bd9Sstevel@tonic-gate 		switch (state)
798*7c478bd9Sstevel@tonic-gate 		{
799*7c478bd9Sstevel@tonic-gate 		  case SMFIR_REJECT:
800*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
801*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
802*7c478bd9Sstevel@tonic-gate 					  "Milter: initialization failed, rejecting commands");
803*7c478bd9Sstevel@tonic-gate 			greetcode = "554";
804*7c478bd9Sstevel@tonic-gate 			nullserver = "Command rejected";
805*7c478bd9Sstevel@tonic-gate 			smtp.sm_milterize = false;
806*7c478bd9Sstevel@tonic-gate 			break;
807*7c478bd9Sstevel@tonic-gate 
808*7c478bd9Sstevel@tonic-gate 		  case SMFIR_TEMPFAIL:
809*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
810*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
811*7c478bd9Sstevel@tonic-gate 					  "Milter: initialization failed, temp failing commands");
812*7c478bd9Sstevel@tonic-gate 			tempfail = true;
813*7c478bd9Sstevel@tonic-gate 			smtp.sm_milterize = false;
814*7c478bd9Sstevel@tonic-gate 			break;
815*7c478bd9Sstevel@tonic-gate 		}
816*7c478bd9Sstevel@tonic-gate 	}
817*7c478bd9Sstevel@tonic-gate 
818*7c478bd9Sstevel@tonic-gate 	if (smtp.sm_milterlist && smtp.sm_milterize &&
819*7c478bd9Sstevel@tonic-gate 	    !bitset(EF_DISCARD, e->e_flags))
820*7c478bd9Sstevel@tonic-gate 	{
821*7c478bd9Sstevel@tonic-gate 		char state;
822*7c478bd9Sstevel@tonic-gate 		char *response;
823*7c478bd9Sstevel@tonic-gate 
824*7c478bd9Sstevel@tonic-gate 		response = milter_connect(peerhostname, RealHostAddr,
825*7c478bd9Sstevel@tonic-gate 					  e, &state);
826*7c478bd9Sstevel@tonic-gate 		switch (state)
827*7c478bd9Sstevel@tonic-gate 		{
828*7c478bd9Sstevel@tonic-gate 		  case SMFIR_REPLYCODE:	/* REPLYCODE shouldn't happen */
829*7c478bd9Sstevel@tonic-gate 		  case SMFIR_REJECT:
830*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
831*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
832*7c478bd9Sstevel@tonic-gate 					  "Milter: connect: host=%s, addr=%s, rejecting commands",
833*7c478bd9Sstevel@tonic-gate 					  peerhostname,
834*7c478bd9Sstevel@tonic-gate 					  anynet_ntoa(&RealHostAddr));
835*7c478bd9Sstevel@tonic-gate 			greetcode = "554";
836*7c478bd9Sstevel@tonic-gate 			nullserver = "Command rejected";
837*7c478bd9Sstevel@tonic-gate 			smtp.sm_milterize = false;
838*7c478bd9Sstevel@tonic-gate 			break;
839*7c478bd9Sstevel@tonic-gate 
840*7c478bd9Sstevel@tonic-gate 		  case SMFIR_TEMPFAIL:
841*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
842*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
843*7c478bd9Sstevel@tonic-gate 					  "Milter: connect: host=%s, addr=%s, temp failing commands",
844*7c478bd9Sstevel@tonic-gate 					  peerhostname,
845*7c478bd9Sstevel@tonic-gate 					  anynet_ntoa(&RealHostAddr));
846*7c478bd9Sstevel@tonic-gate 			tempfail = true;
847*7c478bd9Sstevel@tonic-gate 			smtp.sm_milterize = false;
848*7c478bd9Sstevel@tonic-gate 			break;
849*7c478bd9Sstevel@tonic-gate 
850*7c478bd9Sstevel@tonic-gate 		  case SMFIR_SHUTDOWN:
851*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
852*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
853*7c478bd9Sstevel@tonic-gate 					  "Milter: connect: host=%s, addr=%s, shutdown",
854*7c478bd9Sstevel@tonic-gate 					  peerhostname,
855*7c478bd9Sstevel@tonic-gate 					  anynet_ntoa(&RealHostAddr));
856*7c478bd9Sstevel@tonic-gate 			tempfail = true;
857*7c478bd9Sstevel@tonic-gate 			smtp.sm_milterize = false;
858*7c478bd9Sstevel@tonic-gate 			message("421 4.7.0 %s closing connection",
859*7c478bd9Sstevel@tonic-gate 					MyHostName);
860*7c478bd9Sstevel@tonic-gate 
861*7c478bd9Sstevel@tonic-gate 			/* arrange to ignore send list */
862*7c478bd9Sstevel@tonic-gate 			e->e_sendqueue = NULL;
863*7c478bd9Sstevel@tonic-gate 			goto doquit;
864*7c478bd9Sstevel@tonic-gate 		}
865*7c478bd9Sstevel@tonic-gate 		if (response != NULL)
866*7c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */
867*7c478bd9Sstevel@tonic-gate 	}
868*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
869*7c478bd9Sstevel@tonic-gate 
870*7c478bd9Sstevel@tonic-gate 	/*
871*7c478bd9Sstevel@tonic-gate 	**  Broken proxies and SMTP slammers
872*7c478bd9Sstevel@tonic-gate 	**  push data without waiting, catch them
873*7c478bd9Sstevel@tonic-gate 	*/
874*7c478bd9Sstevel@tonic-gate 
875*7c478bd9Sstevel@tonic-gate 	if (
876*7c478bd9Sstevel@tonic-gate #if STARTTLS
877*7c478bd9Sstevel@tonic-gate 	    !smtps &&
878*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
879*7c478bd9Sstevel@tonic-gate 	    *greetcode == '2')
880*7c478bd9Sstevel@tonic-gate 	{
881*7c478bd9Sstevel@tonic-gate 		time_t msecs = 0;
882*7c478bd9Sstevel@tonic-gate 		char **pvp;
883*7c478bd9Sstevel@tonic-gate 		char pvpbuf[PSBUFSIZE];
884*7c478bd9Sstevel@tonic-gate 
885*7c478bd9Sstevel@tonic-gate 		/* Ask the rulesets how long to pause */
886*7c478bd9Sstevel@tonic-gate 		pvp = NULL;
887*7c478bd9Sstevel@tonic-gate 		r = rscap("greet_pause", peerhostname,
888*7c478bd9Sstevel@tonic-gate 			  anynet_ntoa(&RealHostAddr), e,
889*7c478bd9Sstevel@tonic-gate 			  &pvp, pvpbuf, sizeof(pvpbuf));
890*7c478bd9Sstevel@tonic-gate 		if (r == EX_OK && pvp != NULL && pvp[0] != NULL &&
891*7c478bd9Sstevel@tonic-gate 		    (pvp[0][0] & 0377) == CANONNET && pvp[1] != NULL)
892*7c478bd9Sstevel@tonic-gate 		{
893*7c478bd9Sstevel@tonic-gate 			msecs = strtol(pvp[1], NULL, 10);
894*7c478bd9Sstevel@tonic-gate 		}
895*7c478bd9Sstevel@tonic-gate 
896*7c478bd9Sstevel@tonic-gate 		if (msecs > 0)
897*7c478bd9Sstevel@tonic-gate 		{
898*7c478bd9Sstevel@tonic-gate 			int fd;
899*7c478bd9Sstevel@tonic-gate 			fd_set readfds;
900*7c478bd9Sstevel@tonic-gate 			struct timeval timeout;
901*7c478bd9Sstevel@tonic-gate 
902*7c478bd9Sstevel@tonic-gate 			/* pause for a moment */
903*7c478bd9Sstevel@tonic-gate 			timeout.tv_sec = msecs / 1000;
904*7c478bd9Sstevel@tonic-gate 			timeout.tv_usec = (msecs % 1000) * 1000;
905*7c478bd9Sstevel@tonic-gate 
906*7c478bd9Sstevel@tonic-gate 			/* Obey RFC 2821: 4.3.5.2: 220 timeout of 5 minutes */
907*7c478bd9Sstevel@tonic-gate 			if (timeout.tv_sec >= 300)
908*7c478bd9Sstevel@tonic-gate 			{
909*7c478bd9Sstevel@tonic-gate 				timeout.tv_sec = 300;
910*7c478bd9Sstevel@tonic-gate 				timeout.tv_usec = 0;
911*7c478bd9Sstevel@tonic-gate 			}
912*7c478bd9Sstevel@tonic-gate 
913*7c478bd9Sstevel@tonic-gate 			/* check if data is on the socket during the pause */
914*7c478bd9Sstevel@tonic-gate 			fd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
915*7c478bd9Sstevel@tonic-gate 			FD_ZERO(&readfds);
916*7c478bd9Sstevel@tonic-gate 			SM_FD_SET(fd, &readfds);
917*7c478bd9Sstevel@tonic-gate 			if (select(fd + 1, FDSET_CAST &readfds,
918*7c478bd9Sstevel@tonic-gate 			    NULL, NULL, &timeout) > 0 &&
919*7c478bd9Sstevel@tonic-gate 			    FD_ISSET(fd, &readfds))
920*7c478bd9Sstevel@tonic-gate 			{
921*7c478bd9Sstevel@tonic-gate 				greetcode = "554";
922*7c478bd9Sstevel@tonic-gate 				nullserver = "Command rejected";
923*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
924*7c478bd9Sstevel@tonic-gate 					  "rejecting commands from %s [%s] due to pre-greeting traffic",
925*7c478bd9Sstevel@tonic-gate 					  peerhostname,
926*7c478bd9Sstevel@tonic-gate 					  anynet_ntoa(&RealHostAddr));
927*7c478bd9Sstevel@tonic-gate 			}
928*7c478bd9Sstevel@tonic-gate 		}
929*7c478bd9Sstevel@tonic-gate 	}
930*7c478bd9Sstevel@tonic-gate 
931*7c478bd9Sstevel@tonic-gate #if STARTTLS
932*7c478bd9Sstevel@tonic-gate 	/* If this an smtps connection, start TLS now */
933*7c478bd9Sstevel@tonic-gate 	if (smtps)
934*7c478bd9Sstevel@tonic-gate 	{
935*7c478bd9Sstevel@tonic-gate 		Errors = 0;
936*7c478bd9Sstevel@tonic-gate 		goto starttls;
937*7c478bd9Sstevel@tonic-gate 	}
938*7c478bd9Sstevel@tonic-gate 
939*7c478bd9Sstevel@tonic-gate   greeting:
940*7c478bd9Sstevel@tonic-gate 
941*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
942*7c478bd9Sstevel@tonic-gate 
943*7c478bd9Sstevel@tonic-gate 	/* output the first line, inserting "ESMTP" as second word */
944*7c478bd9Sstevel@tonic-gate 	if (*greetcode == '5')
945*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(inp, sizeof inp, "%s not accepting messages",
946*7c478bd9Sstevel@tonic-gate 				   hostname);
947*7c478bd9Sstevel@tonic-gate 	else
948*7c478bd9Sstevel@tonic-gate 		expand(SmtpGreeting, inp, sizeof inp, e);
949*7c478bd9Sstevel@tonic-gate 
950*7c478bd9Sstevel@tonic-gate 	p = strchr(inp, '\n');
951*7c478bd9Sstevel@tonic-gate 	if (p != NULL)
952*7c478bd9Sstevel@tonic-gate 		*p++ = '\0';
953*7c478bd9Sstevel@tonic-gate 	id = strchr(inp, ' ');
954*7c478bd9Sstevel@tonic-gate 	if (id == NULL)
955*7c478bd9Sstevel@tonic-gate 		id = &inp[strlen(inp)];
956*7c478bd9Sstevel@tonic-gate 	if (p == NULL)
957*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(cmdbuf, sizeof cmdbuf,
958*7c478bd9Sstevel@tonic-gate 			 "%s %%.*s ESMTP%%s", greetcode);
959*7c478bd9Sstevel@tonic-gate 	else
960*7c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(cmdbuf, sizeof cmdbuf,
961*7c478bd9Sstevel@tonic-gate 			 "%s-%%.*s ESMTP%%s", greetcode);
962*7c478bd9Sstevel@tonic-gate 	message(cmdbuf, (int) (id - inp), inp, id);
963*7c478bd9Sstevel@tonic-gate 
964*7c478bd9Sstevel@tonic-gate 	/* output remaining lines */
965*7c478bd9Sstevel@tonic-gate 	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
966*7c478bd9Sstevel@tonic-gate 	{
967*7c478bd9Sstevel@tonic-gate 		*p++ = '\0';
968*7c478bd9Sstevel@tonic-gate 		if (isascii(*id) && isspace(*id))
969*7c478bd9Sstevel@tonic-gate 			id++;
970*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, "-%s");
971*7c478bd9Sstevel@tonic-gate 		message(cmdbuf, id);
972*7c478bd9Sstevel@tonic-gate 	}
973*7c478bd9Sstevel@tonic-gate 	if (id != NULL)
974*7c478bd9Sstevel@tonic-gate 	{
975*7c478bd9Sstevel@tonic-gate 		if (isascii(*id) && isspace(*id))
976*7c478bd9Sstevel@tonic-gate 			id++;
977*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpyn(cmdbuf, sizeof cmdbuf, 2, greetcode, " %s");
978*7c478bd9Sstevel@tonic-gate 		message(cmdbuf, id);
979*7c478bd9Sstevel@tonic-gate 	}
980*7c478bd9Sstevel@tonic-gate 
981*7c478bd9Sstevel@tonic-gate 	protocol = NULL;
982*7c478bd9Sstevel@tonic-gate 	sendinghost = macvalue('s', e);
983*7c478bd9Sstevel@tonic-gate 
984*7c478bd9Sstevel@tonic-gate 	/* If quarantining by a connect/ehlo action, save between messages */
985*7c478bd9Sstevel@tonic-gate 	if (e->e_quarmsg == NULL)
986*7c478bd9Sstevel@tonic-gate 		smtp.sm_quarmsg = NULL;
987*7c478bd9Sstevel@tonic-gate 	else
988*7c478bd9Sstevel@tonic-gate 		smtp.sm_quarmsg = newstr(e->e_quarmsg);
989*7c478bd9Sstevel@tonic-gate 
990*7c478bd9Sstevel@tonic-gate 	/* sendinghost's storage must outlive the current envelope */
991*7c478bd9Sstevel@tonic-gate 	if (sendinghost != NULL)
992*7c478bd9Sstevel@tonic-gate 		sendinghost = sm_strdup_x(sendinghost);
993*7c478bd9Sstevel@tonic-gate #if _FFR_BLOCK_PROXIES
994*7c478bd9Sstevel@tonic-gate 	first = true;
995*7c478bd9Sstevel@tonic-gate #endif /* _FFR_BLOCK_PROXIES */
996*7c478bd9Sstevel@tonic-gate 	gothello = false;
997*7c478bd9Sstevel@tonic-gate 	smtp.sm_gotmail = false;
998*7c478bd9Sstevel@tonic-gate 	for (;;)
999*7c478bd9Sstevel@tonic-gate 	{
1000*7c478bd9Sstevel@tonic-gate 	    SM_TRY
1001*7c478bd9Sstevel@tonic-gate 	    {
1002*7c478bd9Sstevel@tonic-gate 		QuickAbort = false;
1003*7c478bd9Sstevel@tonic-gate 		HoldErrs = false;
1004*7c478bd9Sstevel@tonic-gate 		SuprErrs = false;
1005*7c478bd9Sstevel@tonic-gate 		LogUsrErrs = false;
1006*7c478bd9Sstevel@tonic-gate 		OnlyOneError = true;
1007*7c478bd9Sstevel@tonic-gate 		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
1008*7c478bd9Sstevel@tonic-gate 
1009*7c478bd9Sstevel@tonic-gate 		/* setup for the read */
1010*7c478bd9Sstevel@tonic-gate 		e->e_to = NULL;
1011*7c478bd9Sstevel@tonic-gate 		Errors = 0;
1012*7c478bd9Sstevel@tonic-gate 		FileName = NULL;
1013*7c478bd9Sstevel@tonic-gate 		(void) sm_io_flush(smioout, SM_TIME_DEFAULT);
1014*7c478bd9Sstevel@tonic-gate 
1015*7c478bd9Sstevel@tonic-gate 		/* read the input line */
1016*7c478bd9Sstevel@tonic-gate 		SmtpPhase = "server cmd read";
1017*7c478bd9Sstevel@tonic-gate 		sm_setproctitle(true, e, "server %s cmd read", CurSmtpClient);
1018*7c478bd9Sstevel@tonic-gate #if SASL
1019*7c478bd9Sstevel@tonic-gate 		/*
1020*7c478bd9Sstevel@tonic-gate 		**  XXX SMTP AUTH requires accepting any length,
1021*7c478bd9Sstevel@tonic-gate 		**	at least for challenge/response
1022*7c478bd9Sstevel@tonic-gate 		*/
1023*7c478bd9Sstevel@tonic-gate #endif /* SASL */
1024*7c478bd9Sstevel@tonic-gate 
1025*7c478bd9Sstevel@tonic-gate 		/* handle errors */
1026*7c478bd9Sstevel@tonic-gate 		if (sm_io_error(OutChannel) ||
1027*7c478bd9Sstevel@tonic-gate 		    (p = sfgets(inp, sizeof inp, InChannel,
1028*7c478bd9Sstevel@tonic-gate 				TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
1029*7c478bd9Sstevel@tonic-gate 		{
1030*7c478bd9Sstevel@tonic-gate 			char *d;
1031*7c478bd9Sstevel@tonic-gate 
1032*7c478bd9Sstevel@tonic-gate 			d = macvalue(macid("{daemon_name}"), e);
1033*7c478bd9Sstevel@tonic-gate 			if (d == NULL)
1034*7c478bd9Sstevel@tonic-gate 				d = "stdin";
1035*7c478bd9Sstevel@tonic-gate 			/* end of file, just die */
1036*7c478bd9Sstevel@tonic-gate 			disconnect(1, e);
1037*7c478bd9Sstevel@tonic-gate 
1038*7c478bd9Sstevel@tonic-gate #if MILTER
1039*7c478bd9Sstevel@tonic-gate 			/* close out milter filters */
1040*7c478bd9Sstevel@tonic-gate 			milter_quit(e);
1041*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
1042*7c478bd9Sstevel@tonic-gate 
1043*7c478bd9Sstevel@tonic-gate 			message("421 4.4.1 %s Lost input channel from %s",
1044*7c478bd9Sstevel@tonic-gate 				MyHostName, CurSmtpClient);
1045*7c478bd9Sstevel@tonic-gate 			if (LogLevel > (smtp.sm_gotmail ? 1 : 19))
1046*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_NOTICE, e->e_id,
1047*7c478bd9Sstevel@tonic-gate 					  "lost input channel from %s to %s after %s",
1048*7c478bd9Sstevel@tonic-gate 					  CurSmtpClient, d,
1049*7c478bd9Sstevel@tonic-gate 					  (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
1050*7c478bd9Sstevel@tonic-gate 			/*
1051*7c478bd9Sstevel@tonic-gate 			**  If have not accepted mail (DATA), do not bounce
1052*7c478bd9Sstevel@tonic-gate 			**  bad addresses back to sender.
1053*7c478bd9Sstevel@tonic-gate 			*/
1054*7c478bd9Sstevel@tonic-gate 
1055*7c478bd9Sstevel@tonic-gate 			if (bitset(EF_CLRQUEUE, e->e_flags))
1056*7c478bd9Sstevel@tonic-gate 				e->e_sendqueue = NULL;
1057*7c478bd9Sstevel@tonic-gate 			goto doquit;
1058*7c478bd9Sstevel@tonic-gate 		}
1059*7c478bd9Sstevel@tonic-gate 
1060*7c478bd9Sstevel@tonic-gate #if _FFR_BLOCK_PROXIES
1061*7c478bd9Sstevel@tonic-gate 		if (first)
1062*7c478bd9Sstevel@tonic-gate 		{
1063*7c478bd9Sstevel@tonic-gate 			size_t inplen, cmdlen;
1064*7c478bd9Sstevel@tonic-gate 			int idx;
1065*7c478bd9Sstevel@tonic-gate 			char *http_cmd;
1066*7c478bd9Sstevel@tonic-gate 			static char *http_cmds[] = { "GET", "POST",
1067*7c478bd9Sstevel@tonic-gate 						     "CONNECT", "USER", NULL };
1068*7c478bd9Sstevel@tonic-gate 
1069*7c478bd9Sstevel@tonic-gate 			inplen = strlen(inp);
1070*7c478bd9Sstevel@tonic-gate 			for (idx = 0; (http_cmd = http_cmds[idx]) != NULL;
1071*7c478bd9Sstevel@tonic-gate 			     idx++)
1072*7c478bd9Sstevel@tonic-gate 			{
1073*7c478bd9Sstevel@tonic-gate 				cmdlen = strlen(http_cmd);
1074*7c478bd9Sstevel@tonic-gate 				if (cmdlen < inplen &&
1075*7c478bd9Sstevel@tonic-gate 				    sm_strncasecmp(inp, http_cmd, cmdlen) == 0 &&
1076*7c478bd9Sstevel@tonic-gate 				    isascii(inp[cmdlen]) && isspace(inp[cmdlen]))
1077*7c478bd9Sstevel@tonic-gate 				{
1078*7c478bd9Sstevel@tonic-gate 					/* Open proxy, drop it */
1079*7c478bd9Sstevel@tonic-gate 					message("421 4.7.0 %s Rejecting open proxy %s",
1080*7c478bd9Sstevel@tonic-gate 						MyHostName, CurSmtpClient);
1081*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
1082*7c478bd9Sstevel@tonic-gate 						  "%s: probable open proxy: command=%.40s",
1083*7c478bd9Sstevel@tonic-gate 						  CurSmtpClient, inp);
1084*7c478bd9Sstevel@tonic-gate 					goto doquit;
1085*7c478bd9Sstevel@tonic-gate 				}
1086*7c478bd9Sstevel@tonic-gate 			}
1087*7c478bd9Sstevel@tonic-gate 			first = false;
1088*7c478bd9Sstevel@tonic-gate 		}
1089*7c478bd9Sstevel@tonic-gate #endif /* _FFR_BLOCK_PROXIES */
1090*7c478bd9Sstevel@tonic-gate 
1091*7c478bd9Sstevel@tonic-gate 		/* clean up end of line */
1092*7c478bd9Sstevel@tonic-gate 		fixcrlf(inp, true);
1093*7c478bd9Sstevel@tonic-gate 
1094*7c478bd9Sstevel@tonic-gate #if PIPELINING
1095*7c478bd9Sstevel@tonic-gate # if _FFR_NO_PIPE
1096*7c478bd9Sstevel@tonic-gate 		/*
1097*7c478bd9Sstevel@tonic-gate 		**  if there is more input and pipelining is disabled:
1098*7c478bd9Sstevel@tonic-gate 		**	delay ... (and maybe discard the input?)
1099*7c478bd9Sstevel@tonic-gate 		**  XXX this doesn't really work, at least in tests using
1100*7c478bd9Sstevel@tonic-gate 		**  telnet SM_IO_IS_READABLE only returns 1 if there were
1101*7c478bd9Sstevel@tonic-gate 		**  more than 2 input lines available.
1102*7c478bd9Sstevel@tonic-gate 		*/
1103*7c478bd9Sstevel@tonic-gate 
1104*7c478bd9Sstevel@tonic-gate 		if (bitset(SRV_NO_PIPE, features) &&
1105*7c478bd9Sstevel@tonic-gate 		    sm_io_getinfo(InChannel, SM_IO_IS_READABLE, NULL) > 0)
1106*7c478bd9Sstevel@tonic-gate 		{
1107*7c478bd9Sstevel@tonic-gate 			if (++np_log < 3)
1108*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
1109*7c478bd9Sstevel@tonic-gate 					  "unauthorized PIPELINING, sleeping");
1110*7c478bd9Sstevel@tonic-gate 			sleep(1);
1111*7c478bd9Sstevel@tonic-gate 		}
1112*7c478bd9Sstevel@tonic-gate 
1113*7c478bd9Sstevel@tonic-gate # endif /* _FFR_NO_PIPE */
1114*7c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
1115*7c478bd9Sstevel@tonic-gate 
1116*7c478bd9Sstevel@tonic-gate #if SASL
1117*7c478bd9Sstevel@tonic-gate 		if (authenticating == SASL_PROC_AUTH)
1118*7c478bd9Sstevel@tonic-gate 		{
1119*7c478bd9Sstevel@tonic-gate # if 0
1120*7c478bd9Sstevel@tonic-gate 			if (*inp == '\0')
1121*7c478bd9Sstevel@tonic-gate 			{
1122*7c478bd9Sstevel@tonic-gate 				authenticating = SASL_NOT_AUTH;
1123*7c478bd9Sstevel@tonic-gate 				message("501 5.5.2 missing input");
1124*7c478bd9Sstevel@tonic-gate 				RESET_SASLCONN;
1125*7c478bd9Sstevel@tonic-gate 				continue;
1126*7c478bd9Sstevel@tonic-gate 			}
1127*7c478bd9Sstevel@tonic-gate # endif /* 0 */
1128*7c478bd9Sstevel@tonic-gate 			if (*inp == '*' && *(inp + 1) == '\0')
1129*7c478bd9Sstevel@tonic-gate 			{
1130*7c478bd9Sstevel@tonic-gate 				authenticating = SASL_NOT_AUTH;
1131*7c478bd9Sstevel@tonic-gate 
1132*7c478bd9Sstevel@tonic-gate 				/* rfc 2254 4. */
1133*7c478bd9Sstevel@tonic-gate 				message("501 5.0.0 AUTH aborted");
1134*7c478bd9Sstevel@tonic-gate 				RESET_SASLCONN;
1135*7c478bd9Sstevel@tonic-gate 				continue;
1136*7c478bd9Sstevel@tonic-gate 			}
1137*7c478bd9Sstevel@tonic-gate 
1138*7c478bd9Sstevel@tonic-gate 			/* could this be shorter? XXX */
1139*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
1140*7c478bd9Sstevel@tonic-gate 			in = xalloc(strlen(inp) + 1);
1141*7c478bd9Sstevel@tonic-gate 			result = sasl_decode64(inp, strlen(inp), in,
1142*7c478bd9Sstevel@tonic-gate 					       strlen(inp), &inlen);
1143*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
1144*7c478bd9Sstevel@tonic-gate 			out = xalloc(strlen(inp));
1145*7c478bd9Sstevel@tonic-gate 			result = sasl_decode64(inp, strlen(inp), out, &outlen);
1146*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1147*7c478bd9Sstevel@tonic-gate 			if (result != SASL_OK)
1148*7c478bd9Sstevel@tonic-gate 			{
1149*7c478bd9Sstevel@tonic-gate 				authenticating = SASL_NOT_AUTH;
1150*7c478bd9Sstevel@tonic-gate 
1151*7c478bd9Sstevel@tonic-gate 				/* rfc 2254 4. */
1152*7c478bd9Sstevel@tonic-gate 				message("501 5.5.4 cannot decode AUTH parameter %s",
1153*7c478bd9Sstevel@tonic-gate 					inp);
1154*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
1155*7c478bd9Sstevel@tonic-gate 				sm_free(in);
1156*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1157*7c478bd9Sstevel@tonic-gate 				RESET_SASLCONN;
1158*7c478bd9Sstevel@tonic-gate 				continue;
1159*7c478bd9Sstevel@tonic-gate 			}
1160*7c478bd9Sstevel@tonic-gate 
1161*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
1162*7c478bd9Sstevel@tonic-gate 			result = sasl_server_step(conn,	in, inlen,
1163*7c478bd9Sstevel@tonic-gate 						  &out, &outlen);
1164*7c478bd9Sstevel@tonic-gate 			sm_free(in);
1165*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
1166*7c478bd9Sstevel@tonic-gate 			result = sasl_server_step(conn,	out, outlen,
1167*7c478bd9Sstevel@tonic-gate 						  &out, &outlen, &errstr);
1168*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1169*7c478bd9Sstevel@tonic-gate 
1170*7c478bd9Sstevel@tonic-gate 			/* get an OK if we're done */
1171*7c478bd9Sstevel@tonic-gate 			if (result == SASL_OK)
1172*7c478bd9Sstevel@tonic-gate 			{
1173*7c478bd9Sstevel@tonic-gate   authenticated:
1174*7c478bd9Sstevel@tonic-gate 				message("235 2.0.0 OK Authenticated");
1175*7c478bd9Sstevel@tonic-gate 				authenticating = SASL_IS_AUTH;
1176*7c478bd9Sstevel@tonic-gate 				macdefine(&BlankEnvelope.e_macro, A_TEMP,
1177*7c478bd9Sstevel@tonic-gate 					macid("{auth_type}"), auth_type);
1178*7c478bd9Sstevel@tonic-gate 
1179*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
1180*7c478bd9Sstevel@tonic-gate 				user = macvalue(macid("{auth_authen}"), e);
1181*7c478bd9Sstevel@tonic-gate 
1182*7c478bd9Sstevel@tonic-gate 				/* get security strength (features) */
1183*7c478bd9Sstevel@tonic-gate 				result = sasl_getprop(conn, SASL_SSF,
1184*7c478bd9Sstevel@tonic-gate 						      (const void **) &ssf);
1185*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
1186*7c478bd9Sstevel@tonic-gate 				result = sasl_getprop(conn, SASL_USERNAME,
1187*7c478bd9Sstevel@tonic-gate 						      (void **)&user);
1188*7c478bd9Sstevel@tonic-gate 				if (result != SASL_OK)
1189*7c478bd9Sstevel@tonic-gate 				{
1190*7c478bd9Sstevel@tonic-gate 					user = "";
1191*7c478bd9Sstevel@tonic-gate 					macdefine(&BlankEnvelope.e_macro,
1192*7c478bd9Sstevel@tonic-gate 						  A_PERM,
1193*7c478bd9Sstevel@tonic-gate 						  macid("{auth_authen}"), NULL);
1194*7c478bd9Sstevel@tonic-gate 				}
1195*7c478bd9Sstevel@tonic-gate 				else
1196*7c478bd9Sstevel@tonic-gate 				{
1197*7c478bd9Sstevel@tonic-gate 					macdefine(&BlankEnvelope.e_macro,
1198*7c478bd9Sstevel@tonic-gate 						  A_TEMP,
1199*7c478bd9Sstevel@tonic-gate 						  macid("{auth_authen}"),
1200*7c478bd9Sstevel@tonic-gate 						  xtextify(user, "<>\")"));
1201*7c478bd9Sstevel@tonic-gate 				}
1202*7c478bd9Sstevel@tonic-gate 
1203*7c478bd9Sstevel@tonic-gate # if 0
1204*7c478bd9Sstevel@tonic-gate 				/* get realm? */
1205*7c478bd9Sstevel@tonic-gate 				sasl_getprop(conn, SASL_REALM, (void **) &data);
1206*7c478bd9Sstevel@tonic-gate # endif /* 0 */
1207*7c478bd9Sstevel@tonic-gate 
1208*7c478bd9Sstevel@tonic-gate 				/* get security strength (features) */
1209*7c478bd9Sstevel@tonic-gate 				result = sasl_getprop(conn, SASL_SSF,
1210*7c478bd9Sstevel@tonic-gate 						      (void **) &ssf);
1211*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1212*7c478bd9Sstevel@tonic-gate 				if (result != SASL_OK)
1213*7c478bd9Sstevel@tonic-gate 				{
1214*7c478bd9Sstevel@tonic-gate 					macdefine(&BlankEnvelope.e_macro,
1215*7c478bd9Sstevel@tonic-gate 						  A_PERM,
1216*7c478bd9Sstevel@tonic-gate 						  macid("{auth_ssf}"), "0");
1217*7c478bd9Sstevel@tonic-gate 					ssf = NULL;
1218*7c478bd9Sstevel@tonic-gate 				}
1219*7c478bd9Sstevel@tonic-gate 				else
1220*7c478bd9Sstevel@tonic-gate 				{
1221*7c478bd9Sstevel@tonic-gate 					char pbuf[8];
1222*7c478bd9Sstevel@tonic-gate 
1223*7c478bd9Sstevel@tonic-gate 					(void) sm_snprintf(pbuf, sizeof pbuf,
1224*7c478bd9Sstevel@tonic-gate 							   "%u", *ssf);
1225*7c478bd9Sstevel@tonic-gate 					macdefine(&BlankEnvelope.e_macro,
1226*7c478bd9Sstevel@tonic-gate 						  A_TEMP,
1227*7c478bd9Sstevel@tonic-gate 						  macid("{auth_ssf}"), pbuf);
1228*7c478bd9Sstevel@tonic-gate 					if (tTd(95, 8))
1229*7c478bd9Sstevel@tonic-gate 						sm_dprintf("AUTH auth_ssf: %u\n",
1230*7c478bd9Sstevel@tonic-gate 							   *ssf);
1231*7c478bd9Sstevel@tonic-gate 				}
1232*7c478bd9Sstevel@tonic-gate 
1233*7c478bd9Sstevel@tonic-gate 				/*
1234*7c478bd9Sstevel@tonic-gate 				**  Only switch to encrypted connection
1235*7c478bd9Sstevel@tonic-gate 				**  if a security layer has been negotiated
1236*7c478bd9Sstevel@tonic-gate 				*/
1237*7c478bd9Sstevel@tonic-gate 
1238*7c478bd9Sstevel@tonic-gate 				if (ssf != NULL && *ssf > 0)
1239*7c478bd9Sstevel@tonic-gate 				{
1240*7c478bd9Sstevel@tonic-gate 					/*
1241*7c478bd9Sstevel@tonic-gate 					**  Convert I/O layer to use SASL.
1242*7c478bd9Sstevel@tonic-gate 					**  If the call fails, the connection
1243*7c478bd9Sstevel@tonic-gate 					**  is aborted.
1244*7c478bd9Sstevel@tonic-gate 					*/
1245*7c478bd9Sstevel@tonic-gate 
1246*7c478bd9Sstevel@tonic-gate 					if (sfdcsasl(&InChannel, &OutChannel,
1247*7c478bd9Sstevel@tonic-gate 						     conn) == 0)
1248*7c478bd9Sstevel@tonic-gate 					{
1249*7c478bd9Sstevel@tonic-gate 						/* restart dialogue */
1250*7c478bd9Sstevel@tonic-gate 						n_helo = 0;
1251*7c478bd9Sstevel@tonic-gate # if PIPELINING
1252*7c478bd9Sstevel@tonic-gate 						(void) sm_io_autoflush(InChannel,
1253*7c478bd9Sstevel@tonic-gate 								       OutChannel);
1254*7c478bd9Sstevel@tonic-gate # endif /* PIPELINING */
1255*7c478bd9Sstevel@tonic-gate 					}
1256*7c478bd9Sstevel@tonic-gate 					else
1257*7c478bd9Sstevel@tonic-gate 						syserr("503 5.3.3 SASL TLS failed");
1258*7c478bd9Sstevel@tonic-gate 				}
1259*7c478bd9Sstevel@tonic-gate 
1260*7c478bd9Sstevel@tonic-gate 				/* NULL pointer ok since it's our function */
1261*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 8)
1262*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, NOQID,
1263*7c478bd9Sstevel@tonic-gate 						  "AUTH=server, relay=%s, authid=%.128s, mech=%.16s, bits=%d",
1264*7c478bd9Sstevel@tonic-gate 						  CurSmtpClient,
1265*7c478bd9Sstevel@tonic-gate 						  shortenstring(user, 128),
1266*7c478bd9Sstevel@tonic-gate 						  auth_type, *ssf);
1267*7c478bd9Sstevel@tonic-gate 			}
1268*7c478bd9Sstevel@tonic-gate 			else if (result == SASL_CONTINUE)
1269*7c478bd9Sstevel@tonic-gate 			{
1270*7c478bd9Sstevel@tonic-gate 				len = ENC64LEN(outlen);
1271*7c478bd9Sstevel@tonic-gate 				out2 = xalloc(len);
1272*7c478bd9Sstevel@tonic-gate 				result = sasl_encode64(out, outlen, out2, len,
1273*7c478bd9Sstevel@tonic-gate 						       &out2len);
1274*7c478bd9Sstevel@tonic-gate 				if (result != SASL_OK)
1275*7c478bd9Sstevel@tonic-gate 				{
1276*7c478bd9Sstevel@tonic-gate 					/* correct code? XXX */
1277*7c478bd9Sstevel@tonic-gate 					/* 454 Temp. authentication failure */
1278*7c478bd9Sstevel@tonic-gate 					message("454 4.5.4 Internal error: unable to encode64");
1279*7c478bd9Sstevel@tonic-gate 					if (LogLevel > 5)
1280*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
1281*7c478bd9Sstevel@tonic-gate 							  "AUTH encode64 error [%d for \"%s\"]",
1282*7c478bd9Sstevel@tonic-gate 							  result, out);
1283*7c478bd9Sstevel@tonic-gate 					/* start over? */
1284*7c478bd9Sstevel@tonic-gate 					authenticating = SASL_NOT_AUTH;
1285*7c478bd9Sstevel@tonic-gate 				}
1286*7c478bd9Sstevel@tonic-gate 				else
1287*7c478bd9Sstevel@tonic-gate 				{
1288*7c478bd9Sstevel@tonic-gate 					message("334 %s", out2);
1289*7c478bd9Sstevel@tonic-gate 					if (tTd(95, 2))
1290*7c478bd9Sstevel@tonic-gate 						sm_dprintf("AUTH continue: msg='%s' len=%u\n",
1291*7c478bd9Sstevel@tonic-gate 							   out2, out2len);
1292*7c478bd9Sstevel@tonic-gate 				}
1293*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
1294*7c478bd9Sstevel@tonic-gate 				sm_free(out2);
1295*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1296*7c478bd9Sstevel@tonic-gate 			}
1297*7c478bd9Sstevel@tonic-gate 			else
1298*7c478bd9Sstevel@tonic-gate 			{
1299*7c478bd9Sstevel@tonic-gate 				/* not SASL_OK or SASL_CONT */
1300*7c478bd9Sstevel@tonic-gate 				message("535 5.7.0 authentication failed");
1301*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 9)
1302*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_WARNING, e->e_id,
1303*7c478bd9Sstevel@tonic-gate 						  "AUTH failure (%s): %s (%d) %s",
1304*7c478bd9Sstevel@tonic-gate 						  auth_type,
1305*7c478bd9Sstevel@tonic-gate 						  sasl_errstring(result, NULL,
1306*7c478bd9Sstevel@tonic-gate 								 NULL),
1307*7c478bd9Sstevel@tonic-gate 						  result,
1308*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
1309*7c478bd9Sstevel@tonic-gate 						  sasl_errdetail(conn));
1310*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
1311*7c478bd9Sstevel@tonic-gate 						  errstr == NULL ? "" : errstr);
1312*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1313*7c478bd9Sstevel@tonic-gate 				RESET_SASLCONN;
1314*7c478bd9Sstevel@tonic-gate 				authenticating = SASL_NOT_AUTH;
1315*7c478bd9Sstevel@tonic-gate 			}
1316*7c478bd9Sstevel@tonic-gate 		}
1317*7c478bd9Sstevel@tonic-gate 		else
1318*7c478bd9Sstevel@tonic-gate 		{
1319*7c478bd9Sstevel@tonic-gate 			/* don't want to do any of this if authenticating */
1320*7c478bd9Sstevel@tonic-gate #endif /* SASL */
1321*7c478bd9Sstevel@tonic-gate 
1322*7c478bd9Sstevel@tonic-gate 		/* echo command to transcript */
1323*7c478bd9Sstevel@tonic-gate 		if (e->e_xfp != NULL)
1324*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
1325*7c478bd9Sstevel@tonic-gate 					     "<<< %s\n", inp);
1326*7c478bd9Sstevel@tonic-gate 
1327*7c478bd9Sstevel@tonic-gate 		if (LogLevel > 14)
1328*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "<-- %s", inp);
1329*7c478bd9Sstevel@tonic-gate 
1330*7c478bd9Sstevel@tonic-gate 		/* break off command */
1331*7c478bd9Sstevel@tonic-gate 		for (p = inp; isascii(*p) && isspace(*p); p++)
1332*7c478bd9Sstevel@tonic-gate 			continue;
1333*7c478bd9Sstevel@tonic-gate 		cmd = cmdbuf;
1334*7c478bd9Sstevel@tonic-gate 		while (*p != '\0' &&
1335*7c478bd9Sstevel@tonic-gate 		       !(isascii(*p) && isspace(*p)) &&
1336*7c478bd9Sstevel@tonic-gate 		       cmd < &cmdbuf[sizeof cmdbuf - 2])
1337*7c478bd9Sstevel@tonic-gate 			*cmd++ = *p++;
1338*7c478bd9Sstevel@tonic-gate 		*cmd = '\0';
1339*7c478bd9Sstevel@tonic-gate 
1340*7c478bd9Sstevel@tonic-gate 		/* throw away leading whitespace */
1341*7c478bd9Sstevel@tonic-gate 		SKIP_SPACE(p);
1342*7c478bd9Sstevel@tonic-gate 
1343*7c478bd9Sstevel@tonic-gate 		/* decode command */
1344*7c478bd9Sstevel@tonic-gate 		for (c = CmdTab; c->cmd_name != NULL; c++)
1345*7c478bd9Sstevel@tonic-gate 		{
1346*7c478bd9Sstevel@tonic-gate 			if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0)
1347*7c478bd9Sstevel@tonic-gate 				break;
1348*7c478bd9Sstevel@tonic-gate 		}
1349*7c478bd9Sstevel@tonic-gate 
1350*7c478bd9Sstevel@tonic-gate 		/* reset errors */
1351*7c478bd9Sstevel@tonic-gate 		errno = 0;
1352*7c478bd9Sstevel@tonic-gate 
1353*7c478bd9Sstevel@tonic-gate 		/* check whether a "non-null" command has been used */
1354*7c478bd9Sstevel@tonic-gate 		switch (c->cmd_code)
1355*7c478bd9Sstevel@tonic-gate 		{
1356*7c478bd9Sstevel@tonic-gate #if SASL
1357*7c478bd9Sstevel@tonic-gate 		  case CMDAUTH:
1358*7c478bd9Sstevel@tonic-gate 			/* avoid information leak; take first two words? */
1359*7c478bd9Sstevel@tonic-gate 			q = "AUTH";
1360*7c478bd9Sstevel@tonic-gate 			break;
1361*7c478bd9Sstevel@tonic-gate #endif /* SASL */
1362*7c478bd9Sstevel@tonic-gate 
1363*7c478bd9Sstevel@tonic-gate 		  case CMDMAIL:
1364*7c478bd9Sstevel@tonic-gate 		  case CMDEXPN:
1365*7c478bd9Sstevel@tonic-gate 		  case CMDVRFY:
1366*7c478bd9Sstevel@tonic-gate 		  case CMDETRN:
1367*7c478bd9Sstevel@tonic-gate 			lognullconnection = false;
1368*7c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
1369*7c478bd9Sstevel@tonic-gate 		  default:
1370*7c478bd9Sstevel@tonic-gate 			q = inp;
1371*7c478bd9Sstevel@tonic-gate 			break;
1372*7c478bd9Sstevel@tonic-gate 		}
1373*7c478bd9Sstevel@tonic-gate 
1374*7c478bd9Sstevel@tonic-gate 		if (e->e_id == NULL)
1375*7c478bd9Sstevel@tonic-gate 			sm_setproctitle(true, e, "%s: %.80s",
1376*7c478bd9Sstevel@tonic-gate 					CurSmtpClient, q);
1377*7c478bd9Sstevel@tonic-gate 		else
1378*7c478bd9Sstevel@tonic-gate 			sm_setproctitle(true, e, "%s %s: %.80s",
1379*7c478bd9Sstevel@tonic-gate 					qid_printname(e),
1380*7c478bd9Sstevel@tonic-gate 					CurSmtpClient, q);
1381*7c478bd9Sstevel@tonic-gate 
1382*7c478bd9Sstevel@tonic-gate 		/*
1383*7c478bd9Sstevel@tonic-gate 		**  Process command.
1384*7c478bd9Sstevel@tonic-gate 		**
1385*7c478bd9Sstevel@tonic-gate 		**	If we are running as a null server, return 550
1386*7c478bd9Sstevel@tonic-gate 		**	to almost everything.
1387*7c478bd9Sstevel@tonic-gate 		*/
1388*7c478bd9Sstevel@tonic-gate 
1389*7c478bd9Sstevel@tonic-gate 		if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
1390*7c478bd9Sstevel@tonic-gate 		{
1391*7c478bd9Sstevel@tonic-gate 			switch (c->cmd_code)
1392*7c478bd9Sstevel@tonic-gate 			{
1393*7c478bd9Sstevel@tonic-gate 			  case CMDQUIT:
1394*7c478bd9Sstevel@tonic-gate 			  case CMDHELO:
1395*7c478bd9Sstevel@tonic-gate 			  case CMDEHLO:
1396*7c478bd9Sstevel@tonic-gate 			  case CMDNOOP:
1397*7c478bd9Sstevel@tonic-gate 			  case CMDRSET:
1398*7c478bd9Sstevel@tonic-gate 			  case CMDERROR:
1399*7c478bd9Sstevel@tonic-gate 				/* process normally */
1400*7c478bd9Sstevel@tonic-gate 				break;
1401*7c478bd9Sstevel@tonic-gate 
1402*7c478bd9Sstevel@tonic-gate 			  case CMDETRN:
1403*7c478bd9Sstevel@tonic-gate 				if (bitnset(D_ETRNONLY, d_flags) &&
1404*7c478bd9Sstevel@tonic-gate 				    nullserver == NULL)
1405*7c478bd9Sstevel@tonic-gate 					break;
1406*7c478bd9Sstevel@tonic-gate 				DELAY_CONN("ETRN");
1407*7c478bd9Sstevel@tonic-gate 				/* FALLTHROUGH */
1408*7c478bd9Sstevel@tonic-gate 
1409*7c478bd9Sstevel@tonic-gate 			  default:
1410*7c478bd9Sstevel@tonic-gate #if MAXBADCOMMANDS > 0
1411*7c478bd9Sstevel@tonic-gate 				/* theoretically this could overflow */
1412*7c478bd9Sstevel@tonic-gate 				if (nullserver != NULL &&
1413*7c478bd9Sstevel@tonic-gate 				    ++n_badcmds > MAXBADCOMMANDS)
1414*7c478bd9Sstevel@tonic-gate 				{
1415*7c478bd9Sstevel@tonic-gate 					message("421 4.7.0 %s Too many bad commands; closing connection",
1416*7c478bd9Sstevel@tonic-gate 						MyHostName);
1417*7c478bd9Sstevel@tonic-gate 
1418*7c478bd9Sstevel@tonic-gate 					/* arrange to ignore send list */
1419*7c478bd9Sstevel@tonic-gate 					e->e_sendqueue = NULL;
1420*7c478bd9Sstevel@tonic-gate 					goto doquit;
1421*7c478bd9Sstevel@tonic-gate 				}
1422*7c478bd9Sstevel@tonic-gate #endif /* MAXBADCOMMANDS > 0 */
1423*7c478bd9Sstevel@tonic-gate 				if (nullserver != NULL)
1424*7c478bd9Sstevel@tonic-gate 				{
1425*7c478bd9Sstevel@tonic-gate 					if (ISSMTPREPLY(nullserver))
1426*7c478bd9Sstevel@tonic-gate 						usrerr(nullserver);
1427*7c478bd9Sstevel@tonic-gate 					else
1428*7c478bd9Sstevel@tonic-gate 						usrerr("550 5.0.0 %s",
1429*7c478bd9Sstevel@tonic-gate 						       nullserver);
1430*7c478bd9Sstevel@tonic-gate 				}
1431*7c478bd9Sstevel@tonic-gate 				else
1432*7c478bd9Sstevel@tonic-gate 					usrerr("452 4.4.5 Insufficient disk space; try again later");
1433*7c478bd9Sstevel@tonic-gate 				continue;
1434*7c478bd9Sstevel@tonic-gate 			}
1435*7c478bd9Sstevel@tonic-gate 		}
1436*7c478bd9Sstevel@tonic-gate 
1437*7c478bd9Sstevel@tonic-gate 		switch (c->cmd_code)
1438*7c478bd9Sstevel@tonic-gate 		{
1439*7c478bd9Sstevel@tonic-gate #if SASL
1440*7c478bd9Sstevel@tonic-gate 		  case CMDAUTH: /* sasl */
1441*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("AUTH");
1442*7c478bd9Sstevel@tonic-gate 			if (!sasl_ok || n_mechs <= 0)
1443*7c478bd9Sstevel@tonic-gate 			{
1444*7c478bd9Sstevel@tonic-gate 				message("503 5.3.3 AUTH not available");
1445*7c478bd9Sstevel@tonic-gate 				break;
1446*7c478bd9Sstevel@tonic-gate 			}
1447*7c478bd9Sstevel@tonic-gate 			if (authenticating == SASL_IS_AUTH)
1448*7c478bd9Sstevel@tonic-gate 			{
1449*7c478bd9Sstevel@tonic-gate 				message("503 5.5.0 Already Authenticated");
1450*7c478bd9Sstevel@tonic-gate 				break;
1451*7c478bd9Sstevel@tonic-gate 			}
1452*7c478bd9Sstevel@tonic-gate 			if (smtp.sm_gotmail)
1453*7c478bd9Sstevel@tonic-gate 			{
1454*7c478bd9Sstevel@tonic-gate 				message("503 5.5.0 AUTH not permitted during a mail transaction");
1455*7c478bd9Sstevel@tonic-gate 				break;
1456*7c478bd9Sstevel@tonic-gate 			}
1457*7c478bd9Sstevel@tonic-gate 			if (tempfail)
1458*7c478bd9Sstevel@tonic-gate 			{
1459*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 9)
1460*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
1461*7c478bd9Sstevel@tonic-gate 						  "SMTP AUTH command (%.100s) from %s tempfailed (due to previous checks)",
1462*7c478bd9Sstevel@tonic-gate 						  p, CurSmtpClient);
1463*7c478bd9Sstevel@tonic-gate 				usrerr("454 4.3.0 Please try again later");
1464*7c478bd9Sstevel@tonic-gate 				break;
1465*7c478bd9Sstevel@tonic-gate 			}
1466*7c478bd9Sstevel@tonic-gate 
1467*7c478bd9Sstevel@tonic-gate 			ismore = false;
1468*7c478bd9Sstevel@tonic-gate 
1469*7c478bd9Sstevel@tonic-gate 			/* crude way to avoid crack attempts */
1470*7c478bd9Sstevel@tonic-gate 			STOP_IF_ATTACK(checksmtpattack(&n_auth, n_mechs + 1,
1471*7c478bd9Sstevel@tonic-gate 							true, "AUTH", e));
1472*7c478bd9Sstevel@tonic-gate 
1473*7c478bd9Sstevel@tonic-gate 			/* make sure mechanism (p) is a valid string */
1474*7c478bd9Sstevel@tonic-gate 			for (q = p; *q != '\0' && isascii(*q); q++)
1475*7c478bd9Sstevel@tonic-gate 			{
1476*7c478bd9Sstevel@tonic-gate 				if (isspace(*q))
1477*7c478bd9Sstevel@tonic-gate 				{
1478*7c478bd9Sstevel@tonic-gate 					*q = '\0';
1479*7c478bd9Sstevel@tonic-gate 					while (*++q != '\0' &&
1480*7c478bd9Sstevel@tonic-gate 					       isascii(*q) && isspace(*q))
1481*7c478bd9Sstevel@tonic-gate 						continue;
1482*7c478bd9Sstevel@tonic-gate 					*(q - 1) = '\0';
1483*7c478bd9Sstevel@tonic-gate 					ismore = (*q != '\0');
1484*7c478bd9Sstevel@tonic-gate 					break;
1485*7c478bd9Sstevel@tonic-gate 				}
1486*7c478bd9Sstevel@tonic-gate 			}
1487*7c478bd9Sstevel@tonic-gate 
1488*7c478bd9Sstevel@tonic-gate 			if (*p == '\0')
1489*7c478bd9Sstevel@tonic-gate 			{
1490*7c478bd9Sstevel@tonic-gate 				message("501 5.5.2 AUTH mechanism must be specified");
1491*7c478bd9Sstevel@tonic-gate 				break;
1492*7c478bd9Sstevel@tonic-gate 			}
1493*7c478bd9Sstevel@tonic-gate 
1494*7c478bd9Sstevel@tonic-gate 			/* check whether mechanism is available */
1495*7c478bd9Sstevel@tonic-gate 			if (iteminlist(p, mechlist, " ") == NULL)
1496*7c478bd9Sstevel@tonic-gate 			{
1497*7c478bd9Sstevel@tonic-gate 				message("504 5.3.3 AUTH mechanism %.32s not available",
1498*7c478bd9Sstevel@tonic-gate 					p);
1499*7c478bd9Sstevel@tonic-gate 				break;
1500*7c478bd9Sstevel@tonic-gate 			}
1501*7c478bd9Sstevel@tonic-gate 
1502*7c478bd9Sstevel@tonic-gate 			if (ismore)
1503*7c478bd9Sstevel@tonic-gate 			{
1504*7c478bd9Sstevel@tonic-gate 				/* could this be shorter? XXX */
1505*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
1506*7c478bd9Sstevel@tonic-gate 				in = xalloc(strlen(q) + 1);
1507*7c478bd9Sstevel@tonic-gate 				result = sasl_decode64(q, strlen(q), in,
1508*7c478bd9Sstevel@tonic-gate 						       strlen(q), &inlen);
1509*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
1510*7c478bd9Sstevel@tonic-gate 				in = sm_rpool_malloc(e->e_rpool, strlen(q));
1511*7c478bd9Sstevel@tonic-gate 				result = sasl_decode64(q, strlen(q), in,
1512*7c478bd9Sstevel@tonic-gate 						       &inlen);
1513*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1514*7c478bd9Sstevel@tonic-gate 				if (result != SASL_OK)
1515*7c478bd9Sstevel@tonic-gate 				{
1516*7c478bd9Sstevel@tonic-gate 					message("501 5.5.4 cannot BASE64 decode '%s'",
1517*7c478bd9Sstevel@tonic-gate 						q);
1518*7c478bd9Sstevel@tonic-gate 					if (LogLevel > 5)
1519*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_WARNING, e->e_id,
1520*7c478bd9Sstevel@tonic-gate 							  "AUTH decode64 error [%d for \"%s\"]",
1521*7c478bd9Sstevel@tonic-gate 							  result, q);
1522*7c478bd9Sstevel@tonic-gate 					/* start over? */
1523*7c478bd9Sstevel@tonic-gate 					authenticating = SASL_NOT_AUTH;
1524*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
1525*7c478bd9Sstevel@tonic-gate 					sm_free(in);
1526*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1527*7c478bd9Sstevel@tonic-gate 					in = NULL;
1528*7c478bd9Sstevel@tonic-gate 					inlen = 0;
1529*7c478bd9Sstevel@tonic-gate 					break;
1530*7c478bd9Sstevel@tonic-gate 				}
1531*7c478bd9Sstevel@tonic-gate 			}
1532*7c478bd9Sstevel@tonic-gate 			else
1533*7c478bd9Sstevel@tonic-gate 			{
1534*7c478bd9Sstevel@tonic-gate 				in = NULL;
1535*7c478bd9Sstevel@tonic-gate 				inlen = 0;
1536*7c478bd9Sstevel@tonic-gate 			}
1537*7c478bd9Sstevel@tonic-gate 
1538*7c478bd9Sstevel@tonic-gate 			/* see if that auth type exists */
1539*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
1540*7c478bd9Sstevel@tonic-gate 			result = sasl_server_start(conn, p, in, inlen,
1541*7c478bd9Sstevel@tonic-gate 						   &out, &outlen);
1542*7c478bd9Sstevel@tonic-gate 			if (in != NULL)
1543*7c478bd9Sstevel@tonic-gate 				sm_free(in);
1544*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
1545*7c478bd9Sstevel@tonic-gate 			result = sasl_server_start(conn, p, in, inlen,
1546*7c478bd9Sstevel@tonic-gate 						   &out, &outlen, &errstr);
1547*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1548*7c478bd9Sstevel@tonic-gate 
1549*7c478bd9Sstevel@tonic-gate 			if (result != SASL_OK && result != SASL_CONTINUE)
1550*7c478bd9Sstevel@tonic-gate 			{
1551*7c478bd9Sstevel@tonic-gate 				message("535 5.7.0 authentication failed");
1552*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 9)
1553*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_ERR, e->e_id,
1554*7c478bd9Sstevel@tonic-gate 						  "AUTH failure (%s): %s (%d) %s",
1555*7c478bd9Sstevel@tonic-gate 						  p,
1556*7c478bd9Sstevel@tonic-gate 						  sasl_errstring(result, NULL,
1557*7c478bd9Sstevel@tonic-gate 								 NULL),
1558*7c478bd9Sstevel@tonic-gate 						  result,
1559*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
1560*7c478bd9Sstevel@tonic-gate 						  sasl_errdetail(conn));
1561*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
1562*7c478bd9Sstevel@tonic-gate 						  errstr);
1563*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1564*7c478bd9Sstevel@tonic-gate 				RESET_SASLCONN;
1565*7c478bd9Sstevel@tonic-gate 				break;
1566*7c478bd9Sstevel@tonic-gate 			}
1567*7c478bd9Sstevel@tonic-gate 			auth_type = newstr(p);
1568*7c478bd9Sstevel@tonic-gate 
1569*7c478bd9Sstevel@tonic-gate 			if (result == SASL_OK)
1570*7c478bd9Sstevel@tonic-gate 			{
1571*7c478bd9Sstevel@tonic-gate 				/* ugly, but same code */
1572*7c478bd9Sstevel@tonic-gate 				goto authenticated;
1573*7c478bd9Sstevel@tonic-gate 				/* authenticated by the initial response */
1574*7c478bd9Sstevel@tonic-gate 			}
1575*7c478bd9Sstevel@tonic-gate 
1576*7c478bd9Sstevel@tonic-gate 			/* len is at least 2 */
1577*7c478bd9Sstevel@tonic-gate 			len = ENC64LEN(outlen);
1578*7c478bd9Sstevel@tonic-gate 			out2 = xalloc(len);
1579*7c478bd9Sstevel@tonic-gate 			result = sasl_encode64(out, outlen, out2, len,
1580*7c478bd9Sstevel@tonic-gate 					       &out2len);
1581*7c478bd9Sstevel@tonic-gate 
1582*7c478bd9Sstevel@tonic-gate 			if (result != SASL_OK)
1583*7c478bd9Sstevel@tonic-gate 			{
1584*7c478bd9Sstevel@tonic-gate 				message("454 4.5.4 Temporary authentication failure");
1585*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 5)
1586*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_WARNING, e->e_id,
1587*7c478bd9Sstevel@tonic-gate 						  "AUTH encode64 error [%d for \"%s\"]",
1588*7c478bd9Sstevel@tonic-gate 						  result, out);
1589*7c478bd9Sstevel@tonic-gate 
1590*7c478bd9Sstevel@tonic-gate 				/* start over? */
1591*7c478bd9Sstevel@tonic-gate 				authenticating = SASL_NOT_AUTH;
1592*7c478bd9Sstevel@tonic-gate 				RESET_SASLCONN;
1593*7c478bd9Sstevel@tonic-gate 			}
1594*7c478bd9Sstevel@tonic-gate 			else
1595*7c478bd9Sstevel@tonic-gate 			{
1596*7c478bd9Sstevel@tonic-gate 				message("334 %s", out2);
1597*7c478bd9Sstevel@tonic-gate 				authenticating = SASL_PROC_AUTH;
1598*7c478bd9Sstevel@tonic-gate 			}
1599*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
1600*7c478bd9Sstevel@tonic-gate 			sm_free(out2);
1601*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
1602*7c478bd9Sstevel@tonic-gate 			break;
1603*7c478bd9Sstevel@tonic-gate #endif /* SASL */
1604*7c478bd9Sstevel@tonic-gate 
1605*7c478bd9Sstevel@tonic-gate #if STARTTLS
1606*7c478bd9Sstevel@tonic-gate 		  case CMDSTLS: /* starttls */
1607*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("STARTTLS");
1608*7c478bd9Sstevel@tonic-gate 			if (*p != '\0')
1609*7c478bd9Sstevel@tonic-gate 			{
1610*7c478bd9Sstevel@tonic-gate 				message("501 5.5.2 Syntax error (no parameters allowed)");
1611*7c478bd9Sstevel@tonic-gate 				break;
1612*7c478bd9Sstevel@tonic-gate 			}
1613*7c478bd9Sstevel@tonic-gate 			if (!bitset(SRV_OFFER_TLS, features))
1614*7c478bd9Sstevel@tonic-gate 			{
1615*7c478bd9Sstevel@tonic-gate 				message("503 5.5.0 TLS not available");
1616*7c478bd9Sstevel@tonic-gate 				break;
1617*7c478bd9Sstevel@tonic-gate 			}
1618*7c478bd9Sstevel@tonic-gate 			if (!tls_ok_srv)
1619*7c478bd9Sstevel@tonic-gate 			{
1620*7c478bd9Sstevel@tonic-gate 				message("454 4.3.3 TLS not available after start");
1621*7c478bd9Sstevel@tonic-gate 				break;
1622*7c478bd9Sstevel@tonic-gate 			}
1623*7c478bd9Sstevel@tonic-gate 			if (smtp.sm_gotmail)
1624*7c478bd9Sstevel@tonic-gate 			{
1625*7c478bd9Sstevel@tonic-gate 				message("503 5.5.0 TLS not permitted during a mail transaction");
1626*7c478bd9Sstevel@tonic-gate 				break;
1627*7c478bd9Sstevel@tonic-gate 			}
1628*7c478bd9Sstevel@tonic-gate 			if (tempfail)
1629*7c478bd9Sstevel@tonic-gate 			{
1630*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 9)
1631*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
1632*7c478bd9Sstevel@tonic-gate 						  "SMTP STARTTLS command (%.100s) from %s tempfailed (due to previous checks)",
1633*7c478bd9Sstevel@tonic-gate 						  p, CurSmtpClient);
1634*7c478bd9Sstevel@tonic-gate 				usrerr("454 4.7.0 Please try again later");
1635*7c478bd9Sstevel@tonic-gate 				break;
1636*7c478bd9Sstevel@tonic-gate 			}
1637*7c478bd9Sstevel@tonic-gate   starttls:
1638*7c478bd9Sstevel@tonic-gate # if TLS_NO_RSA
1639*7c478bd9Sstevel@tonic-gate 			/*
1640*7c478bd9Sstevel@tonic-gate 			**  XXX do we need a temp key ?
1641*7c478bd9Sstevel@tonic-gate 			*/
1642*7c478bd9Sstevel@tonic-gate # else /* TLS_NO_RSA */
1643*7c478bd9Sstevel@tonic-gate # endif /* TLS_NO_RSA */
1644*7c478bd9Sstevel@tonic-gate 
1645*7c478bd9Sstevel@tonic-gate # if TLS_VRFY_PER_CTX
1646*7c478bd9Sstevel@tonic-gate 			/*
1647*7c478bd9Sstevel@tonic-gate 			**  Note: this sets the verification globally
1648*7c478bd9Sstevel@tonic-gate 			**  (per SSL_CTX)
1649*7c478bd9Sstevel@tonic-gate 			**  it's ok since it applies only to one transaction
1650*7c478bd9Sstevel@tonic-gate 			*/
1651*7c478bd9Sstevel@tonic-gate 
1652*7c478bd9Sstevel@tonic-gate 			TLS_VERIFY_CLIENT();
1653*7c478bd9Sstevel@tonic-gate # endif /* TLS_VRFY_PER_CTX */
1654*7c478bd9Sstevel@tonic-gate 
1655*7c478bd9Sstevel@tonic-gate 			if (srv_ssl != NULL)
1656*7c478bd9Sstevel@tonic-gate 				SSL_clear(srv_ssl);
1657*7c478bd9Sstevel@tonic-gate 			else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
1658*7c478bd9Sstevel@tonic-gate 			{
1659*7c478bd9Sstevel@tonic-gate 				message("454 4.3.3 TLS not available: error generating SSL handle");
1660*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 8)
1661*7c478bd9Sstevel@tonic-gate 					tlslogerr("server");
1662*7c478bd9Sstevel@tonic-gate 				goto tls_done;
1663*7c478bd9Sstevel@tonic-gate 			}
1664*7c478bd9Sstevel@tonic-gate 
1665*7c478bd9Sstevel@tonic-gate # if !TLS_VRFY_PER_CTX
1666*7c478bd9Sstevel@tonic-gate 			/*
1667*7c478bd9Sstevel@tonic-gate 			**  this could be used if it were possible to set
1668*7c478bd9Sstevel@tonic-gate 			**  verification per SSL (connection)
1669*7c478bd9Sstevel@tonic-gate 			**  not just per SSL_CTX (global)
1670*7c478bd9Sstevel@tonic-gate 			*/
1671*7c478bd9Sstevel@tonic-gate 
1672*7c478bd9Sstevel@tonic-gate 			TLS_VERIFY_CLIENT();
1673*7c478bd9Sstevel@tonic-gate # endif /* !TLS_VRFY_PER_CTX */
1674*7c478bd9Sstevel@tonic-gate 
1675*7c478bd9Sstevel@tonic-gate 			rfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
1676*7c478bd9Sstevel@tonic-gate 			wfd = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
1677*7c478bd9Sstevel@tonic-gate 
1678*7c478bd9Sstevel@tonic-gate 			if (rfd < 0 || wfd < 0 ||
1679*7c478bd9Sstevel@tonic-gate 			    SSL_set_rfd(srv_ssl, rfd) <= 0 ||
1680*7c478bd9Sstevel@tonic-gate 			    SSL_set_wfd(srv_ssl, wfd) <= 0)
1681*7c478bd9Sstevel@tonic-gate 			{
1682*7c478bd9Sstevel@tonic-gate 				message("454 4.3.3 TLS not available: error set fd");
1683*7c478bd9Sstevel@tonic-gate 				SSL_free(srv_ssl);
1684*7c478bd9Sstevel@tonic-gate 				srv_ssl = NULL;
1685*7c478bd9Sstevel@tonic-gate 				goto tls_done;
1686*7c478bd9Sstevel@tonic-gate 			}
1687*7c478bd9Sstevel@tonic-gate 			if (!smtps)
1688*7c478bd9Sstevel@tonic-gate 				message("220 2.0.0 Ready to start TLS");
1689*7c478bd9Sstevel@tonic-gate # if PIPELINING
1690*7c478bd9Sstevel@tonic-gate 			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
1691*7c478bd9Sstevel@tonic-gate # endif /* PIPELINING */
1692*7c478bd9Sstevel@tonic-gate 
1693*7c478bd9Sstevel@tonic-gate 			SSL_set_accept_state(srv_ssl);
1694*7c478bd9Sstevel@tonic-gate 
1695*7c478bd9Sstevel@tonic-gate #  define SSL_ACC(s)	SSL_accept(s)
1696*7c478bd9Sstevel@tonic-gate 
1697*7c478bd9Sstevel@tonic-gate 			tlsstart = curtime();
1698*7c478bd9Sstevel@tonic-gate 			fdfl = fcntl(rfd, F_GETFL);
1699*7c478bd9Sstevel@tonic-gate 			if (fdfl != -1)
1700*7c478bd9Sstevel@tonic-gate 				fcntl(rfd, F_SETFL, fdfl|O_NONBLOCK);
1701*7c478bd9Sstevel@tonic-gate   ssl_retry:
1702*7c478bd9Sstevel@tonic-gate 			if ((r = SSL_ACC(srv_ssl)) <= 0)
1703*7c478bd9Sstevel@tonic-gate 			{
1704*7c478bd9Sstevel@tonic-gate 				int i;
1705*7c478bd9Sstevel@tonic-gate 				bool timedout;
1706*7c478bd9Sstevel@tonic-gate 				time_t left;
1707*7c478bd9Sstevel@tonic-gate 				time_t now = curtime();
1708*7c478bd9Sstevel@tonic-gate 				struct timeval tv;
1709*7c478bd9Sstevel@tonic-gate 
1710*7c478bd9Sstevel@tonic-gate 				/* what to do in this case? */
1711*7c478bd9Sstevel@tonic-gate 				i = SSL_get_error(srv_ssl, r);
1712*7c478bd9Sstevel@tonic-gate 
1713*7c478bd9Sstevel@tonic-gate 				/*
1714*7c478bd9Sstevel@tonic-gate 				**  For SSL_ERROR_WANT_{READ,WRITE}:
1715*7c478bd9Sstevel@tonic-gate 				**  There is no SSL record available yet
1716*7c478bd9Sstevel@tonic-gate 				**  or there is only a partial SSL record
1717*7c478bd9Sstevel@tonic-gate 				**  removed from the network (socket) buffer
1718*7c478bd9Sstevel@tonic-gate 				**  into the SSL buffer. The SSL_accept will
1719*7c478bd9Sstevel@tonic-gate 				**  only succeed when a full SSL record is
1720*7c478bd9Sstevel@tonic-gate 				**  available (assuming a "real" error
1721*7c478bd9Sstevel@tonic-gate 				**  doesn't happen). To handle when a "real"
1722*7c478bd9Sstevel@tonic-gate 				**  error does happen the select is set for
1723*7c478bd9Sstevel@tonic-gate 				**  exceptions too.
1724*7c478bd9Sstevel@tonic-gate 				**  The connection may be re-negotiated
1725*7c478bd9Sstevel@tonic-gate 				**  during this time so both read and write
1726*7c478bd9Sstevel@tonic-gate 				**  "want errors" need to be handled.
1727*7c478bd9Sstevel@tonic-gate 				**  A select() exception loops back so that
1728*7c478bd9Sstevel@tonic-gate 				**  a proper SSL error message can be gotten.
1729*7c478bd9Sstevel@tonic-gate 				*/
1730*7c478bd9Sstevel@tonic-gate 
1731*7c478bd9Sstevel@tonic-gate 				left = TimeOuts.to_starttls - (now - tlsstart);
1732*7c478bd9Sstevel@tonic-gate 				timedout = left <= 0;
1733*7c478bd9Sstevel@tonic-gate 				if (!timedout)
1734*7c478bd9Sstevel@tonic-gate 				{
1735*7c478bd9Sstevel@tonic-gate 					tv.tv_sec = left;
1736*7c478bd9Sstevel@tonic-gate 					tv.tv_usec = 0;
1737*7c478bd9Sstevel@tonic-gate 				}
1738*7c478bd9Sstevel@tonic-gate 
1739*7c478bd9Sstevel@tonic-gate 				if (!timedout && FD_SETSIZE > 0 &&
1740*7c478bd9Sstevel@tonic-gate 				    (rfd >= FD_SETSIZE ||
1741*7c478bd9Sstevel@tonic-gate 				     (i == SSL_ERROR_WANT_WRITE &&
1742*7c478bd9Sstevel@tonic-gate 				      wfd >= FD_SETSIZE)))
1743*7c478bd9Sstevel@tonic-gate 				{
1744*7c478bd9Sstevel@tonic-gate 					if (LogLevel > 5)
1745*7c478bd9Sstevel@tonic-gate 					{
1746*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_ERR, NOQID,
1747*7c478bd9Sstevel@tonic-gate 							  "STARTTLS=server, error: fd %d/%d too large",
1748*7c478bd9Sstevel@tonic-gate 							  rfd, wfd);
1749*7c478bd9Sstevel@tonic-gate 						if (LogLevel > 8)
1750*7c478bd9Sstevel@tonic-gate 							tlslogerr("server");
1751*7c478bd9Sstevel@tonic-gate 					}
1752*7c478bd9Sstevel@tonic-gate 					goto tlsfail;
1753*7c478bd9Sstevel@tonic-gate 				}
1754*7c478bd9Sstevel@tonic-gate 
1755*7c478bd9Sstevel@tonic-gate 				/* XXX what about SSL_pending() ? */
1756*7c478bd9Sstevel@tonic-gate 				if (!timedout && i == SSL_ERROR_WANT_READ)
1757*7c478bd9Sstevel@tonic-gate 				{
1758*7c478bd9Sstevel@tonic-gate 					fd_set ssl_maskr, ssl_maskx;
1759*7c478bd9Sstevel@tonic-gate 
1760*7c478bd9Sstevel@tonic-gate 					FD_ZERO(&ssl_maskr);
1761*7c478bd9Sstevel@tonic-gate 					FD_SET(rfd, &ssl_maskr);
1762*7c478bd9Sstevel@tonic-gate 					FD_ZERO(&ssl_maskx);
1763*7c478bd9Sstevel@tonic-gate 					FD_SET(rfd, &ssl_maskx);
1764*7c478bd9Sstevel@tonic-gate 					if (select(rfd + 1, &ssl_maskr, NULL,
1765*7c478bd9Sstevel@tonic-gate 						   &ssl_maskx, &tv) > 0)
1766*7c478bd9Sstevel@tonic-gate 						goto ssl_retry;
1767*7c478bd9Sstevel@tonic-gate 				}
1768*7c478bd9Sstevel@tonic-gate 				if (!timedout && i == SSL_ERROR_WANT_WRITE)
1769*7c478bd9Sstevel@tonic-gate 				{
1770*7c478bd9Sstevel@tonic-gate 					fd_set ssl_maskw, ssl_maskx;
1771*7c478bd9Sstevel@tonic-gate 
1772*7c478bd9Sstevel@tonic-gate 					FD_ZERO(&ssl_maskw);
1773*7c478bd9Sstevel@tonic-gate 					FD_SET(wfd, &ssl_maskw);
1774*7c478bd9Sstevel@tonic-gate 					FD_ZERO(&ssl_maskx);
1775*7c478bd9Sstevel@tonic-gate 					FD_SET(rfd, &ssl_maskx);
1776*7c478bd9Sstevel@tonic-gate 					if (select(wfd + 1, NULL, &ssl_maskw,
1777*7c478bd9Sstevel@tonic-gate 						   &ssl_maskx, &tv) > 0)
1778*7c478bd9Sstevel@tonic-gate 						goto ssl_retry;
1779*7c478bd9Sstevel@tonic-gate 				}
1780*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 5)
1781*7c478bd9Sstevel@tonic-gate 				{
1782*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_WARNING, NOQID,
1783*7c478bd9Sstevel@tonic-gate 						  "STARTTLS=server, error: accept failed=%d, SSL_error=%d, timedout=%d, errno=%d",
1784*7c478bd9Sstevel@tonic-gate 						  r, i, (int) timedout, errno);
1785*7c478bd9Sstevel@tonic-gate 					if (LogLevel > 8)
1786*7c478bd9Sstevel@tonic-gate 						tlslogerr("server");
1787*7c478bd9Sstevel@tonic-gate 				}
1788*7c478bd9Sstevel@tonic-gate tlsfail:
1789*7c478bd9Sstevel@tonic-gate 				tls_ok_srv = false;
1790*7c478bd9Sstevel@tonic-gate 				SSL_free(srv_ssl);
1791*7c478bd9Sstevel@tonic-gate 				srv_ssl = NULL;
1792*7c478bd9Sstevel@tonic-gate 
1793*7c478bd9Sstevel@tonic-gate 				/*
1794*7c478bd9Sstevel@tonic-gate 				**  according to the next draft of
1795*7c478bd9Sstevel@tonic-gate 				**  RFC 2487 the connection should be dropped
1796*7c478bd9Sstevel@tonic-gate 				*/
1797*7c478bd9Sstevel@tonic-gate 
1798*7c478bd9Sstevel@tonic-gate 				/* arrange to ignore any current send list */
1799*7c478bd9Sstevel@tonic-gate 				e->e_sendqueue = NULL;
1800*7c478bd9Sstevel@tonic-gate 				goto doquit;
1801*7c478bd9Sstevel@tonic-gate 			}
1802*7c478bd9Sstevel@tonic-gate 
1803*7c478bd9Sstevel@tonic-gate 			if (fdfl != -1)
1804*7c478bd9Sstevel@tonic-gate 				fcntl(rfd, F_SETFL, fdfl);
1805*7c478bd9Sstevel@tonic-gate 
1806*7c478bd9Sstevel@tonic-gate 			/* ignore return code for now, it's in {verify} */
1807*7c478bd9Sstevel@tonic-gate 			(void) tls_get_info(srv_ssl, true,
1808*7c478bd9Sstevel@tonic-gate 					    CurSmtpClient,
1809*7c478bd9Sstevel@tonic-gate 					    &BlankEnvelope.e_macro,
1810*7c478bd9Sstevel@tonic-gate 					    bitset(SRV_VRFY_CLT, features));
1811*7c478bd9Sstevel@tonic-gate 
1812*7c478bd9Sstevel@tonic-gate 			/*
1813*7c478bd9Sstevel@tonic-gate 			**  call Stls_client to find out whether
1814*7c478bd9Sstevel@tonic-gate 			**  to accept the connection from the client
1815*7c478bd9Sstevel@tonic-gate 			*/
1816*7c478bd9Sstevel@tonic-gate 
1817*7c478bd9Sstevel@tonic-gate 			saveQuickAbort = QuickAbort;
1818*7c478bd9Sstevel@tonic-gate 			saveSuprErrs = SuprErrs;
1819*7c478bd9Sstevel@tonic-gate 			SuprErrs = true;
1820*7c478bd9Sstevel@tonic-gate 			QuickAbort = false;
1821*7c478bd9Sstevel@tonic-gate 			if (rscheck("tls_client",
1822*7c478bd9Sstevel@tonic-gate 				     macvalue(macid("{verify}"), e),
1823*7c478bd9Sstevel@tonic-gate 				     "STARTTLS", e,
1824*7c478bd9Sstevel@tonic-gate 				     RSF_RMCOMM|RSF_COUNT,
1825*7c478bd9Sstevel@tonic-gate 				     5, NULL, NOQID) != EX_OK ||
1826*7c478bd9Sstevel@tonic-gate 			    Errors > 0)
1827*7c478bd9Sstevel@tonic-gate 			{
1828*7c478bd9Sstevel@tonic-gate 				extern char MsgBuf[];
1829*7c478bd9Sstevel@tonic-gate 
1830*7c478bd9Sstevel@tonic-gate 				if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
1831*7c478bd9Sstevel@tonic-gate 					nullserver = newstr(MsgBuf);
1832*7c478bd9Sstevel@tonic-gate 				else
1833*7c478bd9Sstevel@tonic-gate 					nullserver = "503 5.7.0 Authentication required.";
1834*7c478bd9Sstevel@tonic-gate 			}
1835*7c478bd9Sstevel@tonic-gate 			QuickAbort = saveQuickAbort;
1836*7c478bd9Sstevel@tonic-gate 			SuprErrs = saveSuprErrs;
1837*7c478bd9Sstevel@tonic-gate 
1838*7c478bd9Sstevel@tonic-gate 			tls_ok_srv = false;	/* don't offer STARTTLS again */
1839*7c478bd9Sstevel@tonic-gate 			n_helo = 0;
1840*7c478bd9Sstevel@tonic-gate # if SASL
1841*7c478bd9Sstevel@tonic-gate 			if (sasl_ok)
1842*7c478bd9Sstevel@tonic-gate 			{
1843*7c478bd9Sstevel@tonic-gate 				int cipher_bits;
1844*7c478bd9Sstevel@tonic-gate 				bool verified;
1845*7c478bd9Sstevel@tonic-gate 				char *s, *v, *c;
1846*7c478bd9Sstevel@tonic-gate 
1847*7c478bd9Sstevel@tonic-gate 				s = macvalue(macid("{cipher_bits}"), e);
1848*7c478bd9Sstevel@tonic-gate 				v = macvalue(macid("{verify}"), e);
1849*7c478bd9Sstevel@tonic-gate 				c = macvalue(macid("{cert_subject}"), e);
1850*7c478bd9Sstevel@tonic-gate 				verified = (v != NULL && strcmp(v, "OK") == 0);
1851*7c478bd9Sstevel@tonic-gate 				if (s != NULL && (cipher_bits = atoi(s)) > 0)
1852*7c478bd9Sstevel@tonic-gate 				{
1853*7c478bd9Sstevel@tonic-gate #  if SASL >= 20000
1854*7c478bd9Sstevel@tonic-gate 					ext_ssf = cipher_bits;
1855*7c478bd9Sstevel@tonic-gate 					auth_id = verified ? c : NULL;
1856*7c478bd9Sstevel@tonic-gate 					sasl_ok = ((sasl_setprop(conn,
1857*7c478bd9Sstevel@tonic-gate 							SASL_SSF_EXTERNAL,
1858*7c478bd9Sstevel@tonic-gate 							&ext_ssf) == SASL_OK) &&
1859*7c478bd9Sstevel@tonic-gate 						   (sasl_setprop(conn,
1860*7c478bd9Sstevel@tonic-gate 							SASL_AUTH_EXTERNAL,
1861*7c478bd9Sstevel@tonic-gate 							auth_id) == SASL_OK));
1862*7c478bd9Sstevel@tonic-gate #  else /* SASL >= 20000 */
1863*7c478bd9Sstevel@tonic-gate 					ext_ssf.ssf = cipher_bits;
1864*7c478bd9Sstevel@tonic-gate 					ext_ssf.auth_id = verified ? c : NULL;
1865*7c478bd9Sstevel@tonic-gate 					sasl_ok = sasl_setprop(conn,
1866*7c478bd9Sstevel@tonic-gate 							SASL_SSF_EXTERNAL,
1867*7c478bd9Sstevel@tonic-gate 							&ext_ssf) == SASL_OK;
1868*7c478bd9Sstevel@tonic-gate #  endif /* SASL >= 20000 */
1869*7c478bd9Sstevel@tonic-gate 					mechlist = NULL;
1870*7c478bd9Sstevel@tonic-gate 					if (sasl_ok)
1871*7c478bd9Sstevel@tonic-gate 						n_mechs = saslmechs(conn,
1872*7c478bd9Sstevel@tonic-gate 								    &mechlist);
1873*7c478bd9Sstevel@tonic-gate 				}
1874*7c478bd9Sstevel@tonic-gate 			}
1875*7c478bd9Sstevel@tonic-gate # endif /* SASL */
1876*7c478bd9Sstevel@tonic-gate 
1877*7c478bd9Sstevel@tonic-gate 			/* switch to secure connection */
1878*7c478bd9Sstevel@tonic-gate 			if (sfdctls(&InChannel, &OutChannel, srv_ssl) == 0)
1879*7c478bd9Sstevel@tonic-gate 			{
1880*7c478bd9Sstevel@tonic-gate 				tls_active = true;
1881*7c478bd9Sstevel@tonic-gate # if PIPELINING
1882*7c478bd9Sstevel@tonic-gate 				(void) sm_io_autoflush(InChannel, OutChannel);
1883*7c478bd9Sstevel@tonic-gate # endif /* PIPELINING */
1884*7c478bd9Sstevel@tonic-gate 			}
1885*7c478bd9Sstevel@tonic-gate 			else
1886*7c478bd9Sstevel@tonic-gate 			{
1887*7c478bd9Sstevel@tonic-gate 				/*
1888*7c478bd9Sstevel@tonic-gate 				**  XXX this is an internal error
1889*7c478bd9Sstevel@tonic-gate 				**  how to deal with it?
1890*7c478bd9Sstevel@tonic-gate 				**  we can't generate an error message
1891*7c478bd9Sstevel@tonic-gate 				**  since the other side switched to an
1892*7c478bd9Sstevel@tonic-gate 				**  encrypted layer, but we could not...
1893*7c478bd9Sstevel@tonic-gate 				**  just "hang up"?
1894*7c478bd9Sstevel@tonic-gate 				*/
1895*7c478bd9Sstevel@tonic-gate 
1896*7c478bd9Sstevel@tonic-gate 				nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
1897*7c478bd9Sstevel@tonic-gate 				syserr("STARTTLS: can't switch to encrypted layer");
1898*7c478bd9Sstevel@tonic-gate 			}
1899*7c478bd9Sstevel@tonic-gate 		  tls_done:
1900*7c478bd9Sstevel@tonic-gate 			if (smtps)
1901*7c478bd9Sstevel@tonic-gate 			{
1902*7c478bd9Sstevel@tonic-gate 				if (tls_active)
1903*7c478bd9Sstevel@tonic-gate 					goto greeting;
1904*7c478bd9Sstevel@tonic-gate 				else
1905*7c478bd9Sstevel@tonic-gate 					goto doquit;
1906*7c478bd9Sstevel@tonic-gate 			}
1907*7c478bd9Sstevel@tonic-gate 			break;
1908*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
1909*7c478bd9Sstevel@tonic-gate 
1910*7c478bd9Sstevel@tonic-gate 		  case CMDHELO:		/* hello -- introduce yourself */
1911*7c478bd9Sstevel@tonic-gate 		  case CMDEHLO:		/* extended hello */
1912*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("EHLO");
1913*7c478bd9Sstevel@tonic-gate 			if (c->cmd_code == CMDEHLO)
1914*7c478bd9Sstevel@tonic-gate 			{
1915*7c478bd9Sstevel@tonic-gate 				protocol = "ESMTP";
1916*7c478bd9Sstevel@tonic-gate 				SmtpPhase = "server EHLO";
1917*7c478bd9Sstevel@tonic-gate 			}
1918*7c478bd9Sstevel@tonic-gate 			else
1919*7c478bd9Sstevel@tonic-gate 			{
1920*7c478bd9Sstevel@tonic-gate 				protocol = "SMTP";
1921*7c478bd9Sstevel@tonic-gate 				SmtpPhase = "server HELO";
1922*7c478bd9Sstevel@tonic-gate 			}
1923*7c478bd9Sstevel@tonic-gate 
1924*7c478bd9Sstevel@tonic-gate 			/* avoid denial-of-service */
1925*7c478bd9Sstevel@tonic-gate 			STOP_IF_ATTACK(checksmtpattack(&n_helo, MAXHELOCOMMANDS,
1926*7c478bd9Sstevel@tonic-gate 							true, "HELO/EHLO", e));
1927*7c478bd9Sstevel@tonic-gate 
1928*7c478bd9Sstevel@tonic-gate #if 0
1929*7c478bd9Sstevel@tonic-gate 			/* RFC2821 4.1.4 allows duplicate HELO/EHLO */
1930*7c478bd9Sstevel@tonic-gate 			/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
1931*7c478bd9Sstevel@tonic-gate 			if (gothello)
1932*7c478bd9Sstevel@tonic-gate 			{
1933*7c478bd9Sstevel@tonic-gate 				usrerr("503 %s Duplicate HELO/EHLO",
1934*7c478bd9Sstevel@tonic-gate 				       MyHostName);
1935*7c478bd9Sstevel@tonic-gate 				break;
1936*7c478bd9Sstevel@tonic-gate 			}
1937*7c478bd9Sstevel@tonic-gate #endif /* 0 */
1938*7c478bd9Sstevel@tonic-gate 
1939*7c478bd9Sstevel@tonic-gate 			/* check for valid domain name (re 1123 5.2.5) */
1940*7c478bd9Sstevel@tonic-gate 			if (*p == '\0' && !AllowBogusHELO)
1941*7c478bd9Sstevel@tonic-gate 			{
1942*7c478bd9Sstevel@tonic-gate 				usrerr("501 %s requires domain address",
1943*7c478bd9Sstevel@tonic-gate 					cmdbuf);
1944*7c478bd9Sstevel@tonic-gate 				break;
1945*7c478bd9Sstevel@tonic-gate 			}
1946*7c478bd9Sstevel@tonic-gate 
1947*7c478bd9Sstevel@tonic-gate 			/* check for long domain name (hides Received: info) */
1948*7c478bd9Sstevel@tonic-gate 			if (strlen(p) > MAXNAME)
1949*7c478bd9Sstevel@tonic-gate 			{
1950*7c478bd9Sstevel@tonic-gate 				usrerr("501 Invalid domain name");
1951*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 9)
1952*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, CurEnv->e_id,
1953*7c478bd9Sstevel@tonic-gate 						  "invalid domain name (too long) from %s",
1954*7c478bd9Sstevel@tonic-gate 						  CurSmtpClient);
1955*7c478bd9Sstevel@tonic-gate 				break;
1956*7c478bd9Sstevel@tonic-gate 			}
1957*7c478bd9Sstevel@tonic-gate 
1958*7c478bd9Sstevel@tonic-gate 			ok = true;
1959*7c478bd9Sstevel@tonic-gate 			for (q = p; *q != '\0'; q++)
1960*7c478bd9Sstevel@tonic-gate 			{
1961*7c478bd9Sstevel@tonic-gate 				if (!isascii(*q))
1962*7c478bd9Sstevel@tonic-gate 					break;
1963*7c478bd9Sstevel@tonic-gate 				if (isalnum(*q))
1964*7c478bd9Sstevel@tonic-gate 					continue;
1965*7c478bd9Sstevel@tonic-gate 				if (isspace(*q))
1966*7c478bd9Sstevel@tonic-gate 				{
1967*7c478bd9Sstevel@tonic-gate 					*q = '\0';
1968*7c478bd9Sstevel@tonic-gate 
1969*7c478bd9Sstevel@tonic-gate 					/* only complain if strict check */
1970*7c478bd9Sstevel@tonic-gate 					ok = AllowBogusHELO;
1971*7c478bd9Sstevel@tonic-gate 
1972*7c478bd9Sstevel@tonic-gate 					/* allow trailing whitespace */
1973*7c478bd9Sstevel@tonic-gate 					while (!ok && *++q != '\0' &&
1974*7c478bd9Sstevel@tonic-gate 					       isspace(*q))
1975*7c478bd9Sstevel@tonic-gate 						;
1976*7c478bd9Sstevel@tonic-gate 					if (*q == '\0')
1977*7c478bd9Sstevel@tonic-gate 						ok = true;
1978*7c478bd9Sstevel@tonic-gate 					break;
1979*7c478bd9Sstevel@tonic-gate 				}
1980*7c478bd9Sstevel@tonic-gate 				if (strchr("[].-_#:", *q) == NULL)
1981*7c478bd9Sstevel@tonic-gate 					break;
1982*7c478bd9Sstevel@tonic-gate 			}
1983*7c478bd9Sstevel@tonic-gate 
1984*7c478bd9Sstevel@tonic-gate 			if (*q == '\0' && ok)
1985*7c478bd9Sstevel@tonic-gate 			{
1986*7c478bd9Sstevel@tonic-gate 				q = "pleased to meet you";
1987*7c478bd9Sstevel@tonic-gate 				sendinghost = sm_strdup_x(p);
1988*7c478bd9Sstevel@tonic-gate 			}
1989*7c478bd9Sstevel@tonic-gate 			else if (!AllowBogusHELO)
1990*7c478bd9Sstevel@tonic-gate 			{
1991*7c478bd9Sstevel@tonic-gate 				usrerr("501 Invalid domain name");
1992*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 9)
1993*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, CurEnv->e_id,
1994*7c478bd9Sstevel@tonic-gate 						  "invalid domain name (%s) from %.100s",
1995*7c478bd9Sstevel@tonic-gate 						  p, CurSmtpClient);
1996*7c478bd9Sstevel@tonic-gate 				break;
1997*7c478bd9Sstevel@tonic-gate 			}
1998*7c478bd9Sstevel@tonic-gate 			else
1999*7c478bd9Sstevel@tonic-gate 			{
2000*7c478bd9Sstevel@tonic-gate 				q = "accepting invalid domain name";
2001*7c478bd9Sstevel@tonic-gate 			}
2002*7c478bd9Sstevel@tonic-gate 
2003*7c478bd9Sstevel@tonic-gate 			if (gothello)
2004*7c478bd9Sstevel@tonic-gate 			{
2005*7c478bd9Sstevel@tonic-gate 				CLEAR_STATE(cmdbuf);
2006*7c478bd9Sstevel@tonic-gate 			}
2007*7c478bd9Sstevel@tonic-gate 
2008*7c478bd9Sstevel@tonic-gate #if MILTER
2009*7c478bd9Sstevel@tonic-gate 			if (smtp.sm_milterlist && smtp.sm_milterize &&
2010*7c478bd9Sstevel@tonic-gate 			    !bitset(EF_DISCARD, e->e_flags))
2011*7c478bd9Sstevel@tonic-gate 			{
2012*7c478bd9Sstevel@tonic-gate 				char state;
2013*7c478bd9Sstevel@tonic-gate 				char *response;
2014*7c478bd9Sstevel@tonic-gate 
2015*7c478bd9Sstevel@tonic-gate 				response = milter_helo(p, e, &state);
2016*7c478bd9Sstevel@tonic-gate 				switch (state)
2017*7c478bd9Sstevel@tonic-gate 				{
2018*7c478bd9Sstevel@tonic-gate 				  case SMFIR_REPLYCODE:
2019*7c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 3)
2020*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_INFO, e->e_id,
2021*7c478bd9Sstevel@tonic-gate 							  "Milter: helo=%s, reject=%s",
2022*7c478bd9Sstevel@tonic-gate 							  p, response);
2023*7c478bd9Sstevel@tonic-gate 					nullserver = newstr(response);
2024*7c478bd9Sstevel@tonic-gate 					smtp.sm_milterize = false;
2025*7c478bd9Sstevel@tonic-gate 					break;
2026*7c478bd9Sstevel@tonic-gate 
2027*7c478bd9Sstevel@tonic-gate 				  case SMFIR_REJECT:
2028*7c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 3)
2029*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_INFO, e->e_id,
2030*7c478bd9Sstevel@tonic-gate 							  "Milter: helo=%s, reject=Command rejected",
2031*7c478bd9Sstevel@tonic-gate 							  p);
2032*7c478bd9Sstevel@tonic-gate 					nullserver = "Command rejected";
2033*7c478bd9Sstevel@tonic-gate 					smtp.sm_milterize = false;
2034*7c478bd9Sstevel@tonic-gate 					break;
2035*7c478bd9Sstevel@tonic-gate 
2036*7c478bd9Sstevel@tonic-gate 				  case SMFIR_TEMPFAIL:
2037*7c478bd9Sstevel@tonic-gate 					if (MilterLogLevel > 3)
2038*7c478bd9Sstevel@tonic-gate 						sm_syslog(LOG_INFO, e->e_id,
2039*7c478bd9Sstevel@tonic-gate 							  "Milter: helo=%s, reject=%s",
2040*7c478bd9Sstevel@tonic-gate 							  p, MSG_TEMPFAIL);
2041*7c478bd9Sstevel@tonic-gate 					tempfail = true;
2042*7c478bd9Sstevel@tonic-gate 					smtp.sm_milterize = false;
2043*7c478bd9Sstevel@tonic-gate 					break;
2044*7c478bd9Sstevel@tonic-gate 				}
2045*7c478bd9Sstevel@tonic-gate 				if (response != NULL)
2046*7c478bd9Sstevel@tonic-gate 					sm_free(response);
2047*7c478bd9Sstevel@tonic-gate 
2048*7c478bd9Sstevel@tonic-gate 				/*
2049*7c478bd9Sstevel@tonic-gate 				**  If quarantining by a connect/ehlo action,
2050*7c478bd9Sstevel@tonic-gate 				**  save between messages
2051*7c478bd9Sstevel@tonic-gate 				*/
2052*7c478bd9Sstevel@tonic-gate 
2053*7c478bd9Sstevel@tonic-gate 				if (smtp.sm_quarmsg == NULL &&
2054*7c478bd9Sstevel@tonic-gate 				    e->e_quarmsg != NULL)
2055*7c478bd9Sstevel@tonic-gate 					smtp.sm_quarmsg = newstr(e->e_quarmsg);
2056*7c478bd9Sstevel@tonic-gate 			}
2057*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
2058*7c478bd9Sstevel@tonic-gate 			gothello = true;
2059*7c478bd9Sstevel@tonic-gate 
2060*7c478bd9Sstevel@tonic-gate 			/* print HELO response message */
2061*7c478bd9Sstevel@tonic-gate 			if (c->cmd_code != CMDEHLO)
2062*7c478bd9Sstevel@tonic-gate 			{
2063*7c478bd9Sstevel@tonic-gate 				message("250 %s Hello %s, %s",
2064*7c478bd9Sstevel@tonic-gate 					MyHostName, CurSmtpClient, q);
2065*7c478bd9Sstevel@tonic-gate 				break;
2066*7c478bd9Sstevel@tonic-gate 			}
2067*7c478bd9Sstevel@tonic-gate 
2068*7c478bd9Sstevel@tonic-gate 			message("250-%s Hello %s, %s",
2069*7c478bd9Sstevel@tonic-gate 				MyHostName, CurSmtpClient, q);
2070*7c478bd9Sstevel@tonic-gate 
2071*7c478bd9Sstevel@tonic-gate 			/* offer ENHSC even for nullserver */
2072*7c478bd9Sstevel@tonic-gate 			if (nullserver != NULL)
2073*7c478bd9Sstevel@tonic-gate 			{
2074*7c478bd9Sstevel@tonic-gate 				message("250 ENHANCEDSTATUSCODES");
2075*7c478bd9Sstevel@tonic-gate 				break;
2076*7c478bd9Sstevel@tonic-gate 			}
2077*7c478bd9Sstevel@tonic-gate 
2078*7c478bd9Sstevel@tonic-gate 			/*
2079*7c478bd9Sstevel@tonic-gate 			**  print EHLO features list
2080*7c478bd9Sstevel@tonic-gate 			**
2081*7c478bd9Sstevel@tonic-gate 			**  Note: If you change this list,
2082*7c478bd9Sstevel@tonic-gate 			**	  remember to update 'helpfile'
2083*7c478bd9Sstevel@tonic-gate 			*/
2084*7c478bd9Sstevel@tonic-gate 
2085*7c478bd9Sstevel@tonic-gate 			message("250-ENHANCEDSTATUSCODES");
2086*7c478bd9Sstevel@tonic-gate #if PIPELINING
2087*7c478bd9Sstevel@tonic-gate 			if (bitset(SRV_OFFER_PIPE, features))
2088*7c478bd9Sstevel@tonic-gate 				message("250-PIPELINING");
2089*7c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
2090*7c478bd9Sstevel@tonic-gate 			if (bitset(SRV_OFFER_EXPN, features))
2091*7c478bd9Sstevel@tonic-gate 			{
2092*7c478bd9Sstevel@tonic-gate 				message("250-EXPN");
2093*7c478bd9Sstevel@tonic-gate 				if (bitset(SRV_OFFER_VERB, features))
2094*7c478bd9Sstevel@tonic-gate 					message("250-VERB");
2095*7c478bd9Sstevel@tonic-gate 			}
2096*7c478bd9Sstevel@tonic-gate #if MIME8TO7
2097*7c478bd9Sstevel@tonic-gate 			message("250-8BITMIME");
2098*7c478bd9Sstevel@tonic-gate #endif /* MIME8TO7 */
2099*7c478bd9Sstevel@tonic-gate 			if (MaxMessageSize > 0)
2100*7c478bd9Sstevel@tonic-gate 				message("250-SIZE %ld", MaxMessageSize);
2101*7c478bd9Sstevel@tonic-gate 			else
2102*7c478bd9Sstevel@tonic-gate 				message("250-SIZE");
2103*7c478bd9Sstevel@tonic-gate #if DSN
2104*7c478bd9Sstevel@tonic-gate 			if (SendMIMEErrors && bitset(SRV_OFFER_DSN, features))
2105*7c478bd9Sstevel@tonic-gate 				message("250-DSN");
2106*7c478bd9Sstevel@tonic-gate #endif /* DSN */
2107*7c478bd9Sstevel@tonic-gate 			if (bitset(SRV_OFFER_ETRN, features))
2108*7c478bd9Sstevel@tonic-gate 				message("250-ETRN");
2109*7c478bd9Sstevel@tonic-gate #if SASL
2110*7c478bd9Sstevel@tonic-gate 			if (sasl_ok && mechlist != NULL && *mechlist != '\0')
2111*7c478bd9Sstevel@tonic-gate 				message("250-AUTH %s", mechlist);
2112*7c478bd9Sstevel@tonic-gate #endif /* SASL */
2113*7c478bd9Sstevel@tonic-gate #if STARTTLS
2114*7c478bd9Sstevel@tonic-gate 			if (tls_ok_srv &&
2115*7c478bd9Sstevel@tonic-gate 			    bitset(SRV_OFFER_TLS, features))
2116*7c478bd9Sstevel@tonic-gate 				message("250-STARTTLS");
2117*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
2118*7c478bd9Sstevel@tonic-gate 			if (DeliverByMin > 0)
2119*7c478bd9Sstevel@tonic-gate 				message("250-DELIVERBY %ld",
2120*7c478bd9Sstevel@tonic-gate 					(long) DeliverByMin);
2121*7c478bd9Sstevel@tonic-gate 			else if (DeliverByMin == 0)
2122*7c478bd9Sstevel@tonic-gate 				message("250-DELIVERBY");
2123*7c478bd9Sstevel@tonic-gate 
2124*7c478bd9Sstevel@tonic-gate 			/* < 0: no deliver-by */
2125*7c478bd9Sstevel@tonic-gate 
2126*7c478bd9Sstevel@tonic-gate 			message("250 HELP");
2127*7c478bd9Sstevel@tonic-gate 			break;
2128*7c478bd9Sstevel@tonic-gate 
2129*7c478bd9Sstevel@tonic-gate 		  case CMDMAIL:		/* mail -- designate sender */
2130*7c478bd9Sstevel@tonic-gate 			SmtpPhase = "server MAIL";
2131*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("MAIL");
2132*7c478bd9Sstevel@tonic-gate 
2133*7c478bd9Sstevel@tonic-gate 			/* check for validity of this command */
2134*7c478bd9Sstevel@tonic-gate 			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
2135*7c478bd9Sstevel@tonic-gate 			{
2136*7c478bd9Sstevel@tonic-gate 				usrerr("503 5.0.0 Polite people say HELO first");
2137*7c478bd9Sstevel@tonic-gate 				break;
2138*7c478bd9Sstevel@tonic-gate 			}
2139*7c478bd9Sstevel@tonic-gate 			if (smtp.sm_gotmail)
2140*7c478bd9Sstevel@tonic-gate 			{
2141*7c478bd9Sstevel@tonic-gate 				usrerr("503 5.5.0 Sender already specified");
2142*7c478bd9Sstevel@tonic-gate 				break;
2143*7c478bd9Sstevel@tonic-gate 			}
2144*7c478bd9Sstevel@tonic-gate #if SASL
2145*7c478bd9Sstevel@tonic-gate 			if (bitset(SRV_REQ_AUTH, features) &&
2146*7c478bd9Sstevel@tonic-gate 			    authenticating != SASL_IS_AUTH)
2147*7c478bd9Sstevel@tonic-gate 			{
2148*7c478bd9Sstevel@tonic-gate 				usrerr("530 5.7.0 Authentication required");
2149*7c478bd9Sstevel@tonic-gate 				break;
2150*7c478bd9Sstevel@tonic-gate 			}
2151*7c478bd9Sstevel@tonic-gate #endif /* SASL */
2152*7c478bd9Sstevel@tonic-gate 
2153*7c478bd9Sstevel@tonic-gate 			p = skipword(p, "from");
2154*7c478bd9Sstevel@tonic-gate 			if (p == NULL)
2155*7c478bd9Sstevel@tonic-gate 				break;
2156*7c478bd9Sstevel@tonic-gate 			if (tempfail)
2157*7c478bd9Sstevel@tonic-gate 			{
2158*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 9)
2159*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
2160*7c478bd9Sstevel@tonic-gate 						  "SMTP MAIL command (%.100s) from %s tempfailed (due to previous checks)",
2161*7c478bd9Sstevel@tonic-gate 						  p, CurSmtpClient);
2162*7c478bd9Sstevel@tonic-gate 				usrerr(MSG_TEMPFAIL);
2163*7c478bd9Sstevel@tonic-gate 				break;
2164*7c478bd9Sstevel@tonic-gate 			}
2165*7c478bd9Sstevel@tonic-gate 
2166*7c478bd9Sstevel@tonic-gate 			/* make sure we know who the sending host is */
2167*7c478bd9Sstevel@tonic-gate 			if (sendinghost == NULL)
2168*7c478bd9Sstevel@tonic-gate 				sendinghost = peerhostname;
2169*7c478bd9Sstevel@tonic-gate 
2170*7c478bd9Sstevel@tonic-gate 
2171*7c478bd9Sstevel@tonic-gate #if SM_HEAP_CHECK
2172*7c478bd9Sstevel@tonic-gate 			if (sm_debug_active(&DebugLeakSmtp, 1))
2173*7c478bd9Sstevel@tonic-gate 			{
2174*7c478bd9Sstevel@tonic-gate 				sm_heap_newgroup();
2175*7c478bd9Sstevel@tonic-gate 				sm_dprintf("smtp() heap group #%d\n",
2176*7c478bd9Sstevel@tonic-gate 					sm_heap_group());
2177*7c478bd9Sstevel@tonic-gate 			}
2178*7c478bd9Sstevel@tonic-gate #endif /* SM_HEAP_CHECK */
2179*7c478bd9Sstevel@tonic-gate 
2180*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2181*7c478bd9Sstevel@tonic-gate 				goto undo_no_pm;
2182*7c478bd9Sstevel@tonic-gate 			if (!gothello)
2183*7c478bd9Sstevel@tonic-gate 			{
2184*7c478bd9Sstevel@tonic-gate 				auth_warning(e, "%s didn't use HELO protocol",
2185*7c478bd9Sstevel@tonic-gate 					     CurSmtpClient);
2186*7c478bd9Sstevel@tonic-gate 			}
2187*7c478bd9Sstevel@tonic-gate #ifdef PICKY_HELO_CHECK
2188*7c478bd9Sstevel@tonic-gate 			if (sm_strcasecmp(sendinghost, peerhostname) != 0 &&
2189*7c478bd9Sstevel@tonic-gate 			    (sm_strcasecmp(peerhostname, "localhost") != 0 ||
2190*7c478bd9Sstevel@tonic-gate 			     sm_strcasecmp(sendinghost, MyHostName) != 0))
2191*7c478bd9Sstevel@tonic-gate 			{
2192*7c478bd9Sstevel@tonic-gate 				auth_warning(e, "Host %s claimed to be %s",
2193*7c478bd9Sstevel@tonic-gate 					     CurSmtpClient, sendinghost);
2194*7c478bd9Sstevel@tonic-gate 			}
2195*7c478bd9Sstevel@tonic-gate #endif /* PICKY_HELO_CHECK */
2196*7c478bd9Sstevel@tonic-gate 
2197*7c478bd9Sstevel@tonic-gate 			if (protocol == NULL)
2198*7c478bd9Sstevel@tonic-gate 				protocol = "SMTP";
2199*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, 'r', protocol);
2200*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, 's', sendinghost);
2201*7c478bd9Sstevel@tonic-gate 
2202*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2203*7c478bd9Sstevel@tonic-gate 				goto undo_no_pm;
2204*7c478bd9Sstevel@tonic-gate 			smtp.sm_nrcpts = 0;
2205*7c478bd9Sstevel@tonic-gate 			n_badrcpts = 0;
2206*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{ntries}"), "0");
2207*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{nrcpts}"), "0");
2208*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM, macid("{nbadrcpts}"),
2209*7c478bd9Sstevel@tonic-gate 				"0");
2210*7c478bd9Sstevel@tonic-gate 			e->e_flags |= EF_CLRQUEUE;
2211*7c478bd9Sstevel@tonic-gate 			sm_setproctitle(true, e, "%s %s: %.80s",
2212*7c478bd9Sstevel@tonic-gate 					qid_printname(e),
2213*7c478bd9Sstevel@tonic-gate 					CurSmtpClient, inp);
2214*7c478bd9Sstevel@tonic-gate 
2215*7c478bd9Sstevel@tonic-gate 			/* do the processing */
2216*7c478bd9Sstevel@tonic-gate 		    SM_TRY
2217*7c478bd9Sstevel@tonic-gate 		    {
2218*7c478bd9Sstevel@tonic-gate 			extern char *FullName;
2219*7c478bd9Sstevel@tonic-gate 
2220*7c478bd9Sstevel@tonic-gate 			QuickAbort = true;
2221*7c478bd9Sstevel@tonic-gate 			SM_FREE_CLR(FullName);
2222*7c478bd9Sstevel@tonic-gate 
2223*7c478bd9Sstevel@tonic-gate 			/* must parse sender first */
2224*7c478bd9Sstevel@tonic-gate 			delimptr = NULL;
2225*7c478bd9Sstevel@tonic-gate 			setsender(p, e, &delimptr, ' ', false);
2226*7c478bd9Sstevel@tonic-gate 			if (delimptr != NULL && *delimptr != '\0')
2227*7c478bd9Sstevel@tonic-gate 				*delimptr++ = '\0';
2228*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2229*7c478bd9Sstevel@tonic-gate 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2230*7c478bd9Sstevel@tonic-gate 
2231*7c478bd9Sstevel@tonic-gate 			/* Successfully set e_from, allow logging */
2232*7c478bd9Sstevel@tonic-gate 			e->e_flags |= EF_LOGSENDER;
2233*7c478bd9Sstevel@tonic-gate 
2234*7c478bd9Sstevel@tonic-gate 			/* put resulting triple from parseaddr() into macros */
2235*7c478bd9Sstevel@tonic-gate 			if (e->e_from.q_mailer != NULL)
2236*7c478bd9Sstevel@tonic-gate 				 macdefine(&e->e_macro, A_PERM,
2237*7c478bd9Sstevel@tonic-gate 					macid("{mail_mailer}"),
2238*7c478bd9Sstevel@tonic-gate 					e->e_from.q_mailer->m_name);
2239*7c478bd9Sstevel@tonic-gate 			else
2240*7c478bd9Sstevel@tonic-gate 				 macdefine(&e->e_macro, A_PERM,
2241*7c478bd9Sstevel@tonic-gate 					macid("{mail_mailer}"), NULL);
2242*7c478bd9Sstevel@tonic-gate 			if (e->e_from.q_host != NULL)
2243*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
2244*7c478bd9Sstevel@tonic-gate 					macid("{mail_host}"),
2245*7c478bd9Sstevel@tonic-gate 					e->e_from.q_host);
2246*7c478bd9Sstevel@tonic-gate 			else
2247*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
2248*7c478bd9Sstevel@tonic-gate 					macid("{mail_host}"), "localhost");
2249*7c478bd9Sstevel@tonic-gate 			if (e->e_from.q_user != NULL)
2250*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
2251*7c478bd9Sstevel@tonic-gate 					macid("{mail_addr}"),
2252*7c478bd9Sstevel@tonic-gate 					e->e_from.q_user);
2253*7c478bd9Sstevel@tonic-gate 			else
2254*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
2255*7c478bd9Sstevel@tonic-gate 					macid("{mail_addr}"), NULL);
2256*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2257*7c478bd9Sstevel@tonic-gate 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2258*7c478bd9Sstevel@tonic-gate 
2259*7c478bd9Sstevel@tonic-gate 			/* check for possible spoofing */
2260*7c478bd9Sstevel@tonic-gate 			if (RealUid != 0 && OpMode == MD_SMTP &&
2261*7c478bd9Sstevel@tonic-gate 			    !wordinclass(RealUserName, 't') &&
2262*7c478bd9Sstevel@tonic-gate 			    (!bitnset(M_LOCALMAILER,
2263*7c478bd9Sstevel@tonic-gate 				      e->e_from.q_mailer->m_flags) ||
2264*7c478bd9Sstevel@tonic-gate 			     strcmp(e->e_from.q_user, RealUserName) != 0))
2265*7c478bd9Sstevel@tonic-gate 			{
2266*7c478bd9Sstevel@tonic-gate 				auth_warning(e, "%s owned process doing -bs",
2267*7c478bd9Sstevel@tonic-gate 					RealUserName);
2268*7c478bd9Sstevel@tonic-gate 			}
2269*7c478bd9Sstevel@tonic-gate 
2270*7c478bd9Sstevel@tonic-gate 			/* reset to default value */
2271*7c478bd9Sstevel@tonic-gate 			SevenBitInput = save_sevenbitinput;
2272*7c478bd9Sstevel@tonic-gate 
2273*7c478bd9Sstevel@tonic-gate 			/* now parse ESMTP arguments */
2274*7c478bd9Sstevel@tonic-gate 			e->e_msgsize = 0;
2275*7c478bd9Sstevel@tonic-gate 			addr = p;
2276*7c478bd9Sstevel@tonic-gate 			argno = 0;
2277*7c478bd9Sstevel@tonic-gate 			args[argno++] = p;
2278*7c478bd9Sstevel@tonic-gate 			p = delimptr;
2279*7c478bd9Sstevel@tonic-gate 			while (p != NULL && *p != '\0')
2280*7c478bd9Sstevel@tonic-gate 			{
2281*7c478bd9Sstevel@tonic-gate 				char *kp;
2282*7c478bd9Sstevel@tonic-gate 				char *vp = NULL;
2283*7c478bd9Sstevel@tonic-gate 				char *equal = NULL;
2284*7c478bd9Sstevel@tonic-gate 
2285*7c478bd9Sstevel@tonic-gate 				/* locate the beginning of the keyword */
2286*7c478bd9Sstevel@tonic-gate 				SKIP_SPACE(p);
2287*7c478bd9Sstevel@tonic-gate 				if (*p == '\0')
2288*7c478bd9Sstevel@tonic-gate 					break;
2289*7c478bd9Sstevel@tonic-gate 				kp = p;
2290*7c478bd9Sstevel@tonic-gate 
2291*7c478bd9Sstevel@tonic-gate 				/* skip to the value portion */
2292*7c478bd9Sstevel@tonic-gate 				while ((isascii(*p) && isalnum(*p)) || *p == '-')
2293*7c478bd9Sstevel@tonic-gate 					p++;
2294*7c478bd9Sstevel@tonic-gate 				if (*p == '=')
2295*7c478bd9Sstevel@tonic-gate 				{
2296*7c478bd9Sstevel@tonic-gate 					equal = p;
2297*7c478bd9Sstevel@tonic-gate 					*p++ = '\0';
2298*7c478bd9Sstevel@tonic-gate 					vp = p;
2299*7c478bd9Sstevel@tonic-gate 
2300*7c478bd9Sstevel@tonic-gate 					/* skip to the end of the value */
2301*7c478bd9Sstevel@tonic-gate 					while (*p != '\0' && *p != ' ' &&
2302*7c478bd9Sstevel@tonic-gate 					       !(isascii(*p) && iscntrl(*p)) &&
2303*7c478bd9Sstevel@tonic-gate 					       *p != '=')
2304*7c478bd9Sstevel@tonic-gate 						p++;
2305*7c478bd9Sstevel@tonic-gate 				}
2306*7c478bd9Sstevel@tonic-gate 
2307*7c478bd9Sstevel@tonic-gate 				if (*p != '\0')
2308*7c478bd9Sstevel@tonic-gate 					*p++ = '\0';
2309*7c478bd9Sstevel@tonic-gate 
2310*7c478bd9Sstevel@tonic-gate 				if (tTd(19, 1))
2311*7c478bd9Sstevel@tonic-gate 					sm_dprintf("MAIL: got arg %s=\"%s\"\n", kp,
2312*7c478bd9Sstevel@tonic-gate 						vp == NULL ? "<null>" : vp);
2313*7c478bd9Sstevel@tonic-gate 
2314*7c478bd9Sstevel@tonic-gate 				mail_esmtp_args(kp, vp, e);
2315*7c478bd9Sstevel@tonic-gate 				if (equal != NULL)
2316*7c478bd9Sstevel@tonic-gate 					*equal = '=';
2317*7c478bd9Sstevel@tonic-gate 				args[argno++] = kp;
2318*7c478bd9Sstevel@tonic-gate 				if (argno >= MAXSMTPARGS - 1)
2319*7c478bd9Sstevel@tonic-gate 					usrerr("501 5.5.4 Too many parameters");
2320*7c478bd9Sstevel@tonic-gate 				if (Errors > 0)
2321*7c478bd9Sstevel@tonic-gate 					sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2322*7c478bd9Sstevel@tonic-gate 			}
2323*7c478bd9Sstevel@tonic-gate 			args[argno] = NULL;
2324*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2325*7c478bd9Sstevel@tonic-gate 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2326*7c478bd9Sstevel@tonic-gate 
2327*7c478bd9Sstevel@tonic-gate #if SASL
2328*7c478bd9Sstevel@tonic-gate # if _FFR_AUTH_PASSING
2329*7c478bd9Sstevel@tonic-gate 			/* set the default AUTH= if the sender didn't */
2330*7c478bd9Sstevel@tonic-gate 			if (e->e_auth_param == NULL)
2331*7c478bd9Sstevel@tonic-gate 			{
2332*7c478bd9Sstevel@tonic-gate 				/* XXX only do this for an MSA? */
2333*7c478bd9Sstevel@tonic-gate 				e->e_auth_param = macvalue(macid("{auth_authen}"),
2334*7c478bd9Sstevel@tonic-gate 							   e);
2335*7c478bd9Sstevel@tonic-gate 				if (e->e_auth_param == NULL)
2336*7c478bd9Sstevel@tonic-gate 					e->e_auth_param = "<>";
2337*7c478bd9Sstevel@tonic-gate 
2338*7c478bd9Sstevel@tonic-gate 				/*
2339*7c478bd9Sstevel@tonic-gate 				**  XXX should we invoke Strust_auth now?
2340*7c478bd9Sstevel@tonic-gate 				**  authorizing as the client that just
2341*7c478bd9Sstevel@tonic-gate 				**  authenticated, so we'll trust implicitly
2342*7c478bd9Sstevel@tonic-gate 				*/
2343*7c478bd9Sstevel@tonic-gate 			}
2344*7c478bd9Sstevel@tonic-gate # endif /* _FFR_AUTH_PASSING */
2345*7c478bd9Sstevel@tonic-gate #endif /* SASL */
2346*7c478bd9Sstevel@tonic-gate 
2347*7c478bd9Sstevel@tonic-gate 			/* do config file checking of the sender */
2348*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
2349*7c478bd9Sstevel@tonic-gate 				macid("{addr_type}"), "e s");
2350*7c478bd9Sstevel@tonic-gate #if _FFR_MAIL_MACRO
2351*7c478bd9Sstevel@tonic-gate 			/* make the "real" sender address available */
2352*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_TEMP, macid("{mail_from}"),
2353*7c478bd9Sstevel@tonic-gate 				  e->e_from.q_paddr);
2354*7c478bd9Sstevel@tonic-gate #endif /* _FFR_MAIL_MACRO */
2355*7c478bd9Sstevel@tonic-gate 			if (rscheck("check_mail", addr,
2356*7c478bd9Sstevel@tonic-gate 				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
2357*7c478bd9Sstevel@tonic-gate 				    NULL, e->e_id) != EX_OK ||
2358*7c478bd9Sstevel@tonic-gate 			    Errors > 0)
2359*7c478bd9Sstevel@tonic-gate 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2360*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
2361*7c478bd9Sstevel@tonic-gate 				  macid("{addr_type}"), NULL);
2362*7c478bd9Sstevel@tonic-gate 
2363*7c478bd9Sstevel@tonic-gate 			if (MaxMessageSize > 0 &&
2364*7c478bd9Sstevel@tonic-gate 			    (e->e_msgsize > MaxMessageSize ||
2365*7c478bd9Sstevel@tonic-gate 			     e->e_msgsize < 0))
2366*7c478bd9Sstevel@tonic-gate 			{
2367*7c478bd9Sstevel@tonic-gate 				usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
2368*7c478bd9Sstevel@tonic-gate 					MaxMessageSize);
2369*7c478bd9Sstevel@tonic-gate 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2370*7c478bd9Sstevel@tonic-gate 			}
2371*7c478bd9Sstevel@tonic-gate 
2372*7c478bd9Sstevel@tonic-gate 			/*
2373*7c478bd9Sstevel@tonic-gate 			**  XXX always check whether there is at least one fs
2374*7c478bd9Sstevel@tonic-gate 			**  with enough space?
2375*7c478bd9Sstevel@tonic-gate 			**  However, this may not help much: the queue group
2376*7c478bd9Sstevel@tonic-gate 			**  selection may later on select a FS that hasn't
2377*7c478bd9Sstevel@tonic-gate 			**  enough space.
2378*7c478bd9Sstevel@tonic-gate 			*/
2379*7c478bd9Sstevel@tonic-gate 
2380*7c478bd9Sstevel@tonic-gate 			if ((NumFileSys == 1 || NumQueue == 1) &&
2381*7c478bd9Sstevel@tonic-gate 			    !enoughdiskspace(e->e_msgsize, e)
2382*7c478bd9Sstevel@tonic-gate #if _FFR_ANY_FREE_FS
2383*7c478bd9Sstevel@tonic-gate 			    && !filesys_free(e->e_msgsize)
2384*7c478bd9Sstevel@tonic-gate #endif /* _FFR_ANY_FREE_FS */
2385*7c478bd9Sstevel@tonic-gate 			   )
2386*7c478bd9Sstevel@tonic-gate 			{
2387*7c478bd9Sstevel@tonic-gate 				/*
2388*7c478bd9Sstevel@tonic-gate 				**  We perform this test again when the
2389*7c478bd9Sstevel@tonic-gate 				**  queue directory is selected, in collect.
2390*7c478bd9Sstevel@tonic-gate 				*/
2391*7c478bd9Sstevel@tonic-gate 
2392*7c478bd9Sstevel@tonic-gate 				usrerr("452 4.4.5 Insufficient disk space; try again later");
2393*7c478bd9Sstevel@tonic-gate 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2394*7c478bd9Sstevel@tonic-gate 			}
2395*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2396*7c478bd9Sstevel@tonic-gate 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2397*7c478bd9Sstevel@tonic-gate 
2398*7c478bd9Sstevel@tonic-gate 			LogUsrErrs = true;
2399*7c478bd9Sstevel@tonic-gate #if MILTER
2400*7c478bd9Sstevel@tonic-gate 			if (smtp.sm_milterlist && smtp.sm_milterize &&
2401*7c478bd9Sstevel@tonic-gate 			    !bitset(EF_DISCARD, e->e_flags))
2402*7c478bd9Sstevel@tonic-gate 			{
2403*7c478bd9Sstevel@tonic-gate 				char state;
2404*7c478bd9Sstevel@tonic-gate 				char *response;
2405*7c478bd9Sstevel@tonic-gate 
2406*7c478bd9Sstevel@tonic-gate 				response = milter_envfrom(args, e, &state);
2407*7c478bd9Sstevel@tonic-gate 				MILTER_REPLY("from");
2408*7c478bd9Sstevel@tonic-gate 			}
2409*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
2410*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2411*7c478bd9Sstevel@tonic-gate 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2412*7c478bd9Sstevel@tonic-gate 
2413*7c478bd9Sstevel@tonic-gate 			message("250 2.1.0 Sender ok");
2414*7c478bd9Sstevel@tonic-gate 			smtp.sm_gotmail = true;
2415*7c478bd9Sstevel@tonic-gate 		    }
2416*7c478bd9Sstevel@tonic-gate 		    SM_EXCEPT(exc, "[!F]*")
2417*7c478bd9Sstevel@tonic-gate 		    {
2418*7c478bd9Sstevel@tonic-gate 			/*
2419*7c478bd9Sstevel@tonic-gate 			**  An error occurred while processing a MAIL command.
2420*7c478bd9Sstevel@tonic-gate 			**  Jump to the common error handling code.
2421*7c478bd9Sstevel@tonic-gate 			*/
2422*7c478bd9Sstevel@tonic-gate 
2423*7c478bd9Sstevel@tonic-gate 			sm_exc_free(exc);
2424*7c478bd9Sstevel@tonic-gate 			goto undo_no_pm;
2425*7c478bd9Sstevel@tonic-gate 		    }
2426*7c478bd9Sstevel@tonic-gate 		    SM_END_TRY
2427*7c478bd9Sstevel@tonic-gate 			break;
2428*7c478bd9Sstevel@tonic-gate 
2429*7c478bd9Sstevel@tonic-gate 		  undo_no_pm:
2430*7c478bd9Sstevel@tonic-gate 			e->e_flags &= ~EF_PM_NOTIFY;
2431*7c478bd9Sstevel@tonic-gate 		  undo:
2432*7c478bd9Sstevel@tonic-gate 			break;
2433*7c478bd9Sstevel@tonic-gate 
2434*7c478bd9Sstevel@tonic-gate 		  case CMDRCPT:		/* rcpt -- designate recipient */
2435*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("RCPT");
2436*7c478bd9Sstevel@tonic-gate 			if (BadRcptThrottle > 0 &&
2437*7c478bd9Sstevel@tonic-gate 			    n_badrcpts >= BadRcptThrottle)
2438*7c478bd9Sstevel@tonic-gate 			{
2439*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 5 &&
2440*7c478bd9Sstevel@tonic-gate 				    n_badrcpts == BadRcptThrottle)
2441*7c478bd9Sstevel@tonic-gate 				{
2442*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
2443*7c478bd9Sstevel@tonic-gate 						  "%s: Possible SMTP RCPT flood, throttling.",
2444*7c478bd9Sstevel@tonic-gate 						  CurSmtpClient);
2445*7c478bd9Sstevel@tonic-gate 
2446*7c478bd9Sstevel@tonic-gate 					/* To avoid duplicated message */
2447*7c478bd9Sstevel@tonic-gate 					n_badrcpts++;
2448*7c478bd9Sstevel@tonic-gate 				}
2449*7c478bd9Sstevel@tonic-gate 				NBADRCPTS;
2450*7c478bd9Sstevel@tonic-gate 
2451*7c478bd9Sstevel@tonic-gate 				/*
2452*7c478bd9Sstevel@tonic-gate 				**  Don't use exponential backoff for now.
2453*7c478bd9Sstevel@tonic-gate 				**  Some servers will open more connections
2454*7c478bd9Sstevel@tonic-gate 				**  and actually overload the receiver even
2455*7c478bd9Sstevel@tonic-gate 				**  more.
2456*7c478bd9Sstevel@tonic-gate 				*/
2457*7c478bd9Sstevel@tonic-gate 
2458*7c478bd9Sstevel@tonic-gate 				(void) sleep(1);
2459*7c478bd9Sstevel@tonic-gate 			}
2460*7c478bd9Sstevel@tonic-gate 			if (!smtp.sm_gotmail)
2461*7c478bd9Sstevel@tonic-gate 			{
2462*7c478bd9Sstevel@tonic-gate 				usrerr("503 5.0.0 Need MAIL before RCPT");
2463*7c478bd9Sstevel@tonic-gate 				break;
2464*7c478bd9Sstevel@tonic-gate 			}
2465*7c478bd9Sstevel@tonic-gate 			SmtpPhase = "server RCPT";
2466*7c478bd9Sstevel@tonic-gate 		    SM_TRY
2467*7c478bd9Sstevel@tonic-gate 		    {
2468*7c478bd9Sstevel@tonic-gate 			QuickAbort = true;
2469*7c478bd9Sstevel@tonic-gate 			LogUsrErrs = true;
2470*7c478bd9Sstevel@tonic-gate 
2471*7c478bd9Sstevel@tonic-gate 			/* limit flooding of our machine */
2472*7c478bd9Sstevel@tonic-gate 			if (MaxRcptPerMsg > 0 &&
2473*7c478bd9Sstevel@tonic-gate 			    smtp.sm_nrcpts >= MaxRcptPerMsg)
2474*7c478bd9Sstevel@tonic-gate 			{
2475*7c478bd9Sstevel@tonic-gate 				/* sleep(1); / * slow down? */
2476*7c478bd9Sstevel@tonic-gate 				usrerr("452 4.5.3 Too many recipients");
2477*7c478bd9Sstevel@tonic-gate 				goto rcpt_done;
2478*7c478bd9Sstevel@tonic-gate 			}
2479*7c478bd9Sstevel@tonic-gate 
2480*7c478bd9Sstevel@tonic-gate 			if (e->e_sendmode != SM_DELIVER)
2481*7c478bd9Sstevel@tonic-gate 				e->e_flags |= EF_VRFYONLY;
2482*7c478bd9Sstevel@tonic-gate 
2483*7c478bd9Sstevel@tonic-gate #if MILTER
2484*7c478bd9Sstevel@tonic-gate 			/*
2485*7c478bd9Sstevel@tonic-gate 			**  If the filter will be deleting recipients,
2486*7c478bd9Sstevel@tonic-gate 			**  don't expand them at RCPT time (in the call
2487*7c478bd9Sstevel@tonic-gate 			**  to recipient()).  If they are expanded, it
2488*7c478bd9Sstevel@tonic-gate 			**  is impossible for removefromlist() to figure
2489*7c478bd9Sstevel@tonic-gate 			**  out the expanded members of the original
2490*7c478bd9Sstevel@tonic-gate 			**  recipient and mark them as QS_DONTSEND.
2491*7c478bd9Sstevel@tonic-gate 			*/
2492*7c478bd9Sstevel@tonic-gate 
2493*7c478bd9Sstevel@tonic-gate 			if (milter_can_delrcpts())
2494*7c478bd9Sstevel@tonic-gate 				e->e_flags |= EF_VRFYONLY;
2495*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
2496*7c478bd9Sstevel@tonic-gate 
2497*7c478bd9Sstevel@tonic-gate 			p = skipword(p, "to");
2498*7c478bd9Sstevel@tonic-gate 			if (p == NULL)
2499*7c478bd9Sstevel@tonic-gate 				goto rcpt_done;
2500*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
2501*7c478bd9Sstevel@tonic-gate 				macid("{addr_type}"), "e r");
2502*7c478bd9Sstevel@tonic-gate 			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr,
2503*7c478bd9Sstevel@tonic-gate 				      e, true);
2504*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
2505*7c478bd9Sstevel@tonic-gate 				macid("{addr_type}"), NULL);
2506*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2507*7c478bd9Sstevel@tonic-gate 				goto rcpt_done;
2508*7c478bd9Sstevel@tonic-gate 			if (a == NULL)
2509*7c478bd9Sstevel@tonic-gate 			{
2510*7c478bd9Sstevel@tonic-gate 				usrerr("501 5.0.0 Missing recipient");
2511*7c478bd9Sstevel@tonic-gate 				goto rcpt_done;
2512*7c478bd9Sstevel@tonic-gate 			}
2513*7c478bd9Sstevel@tonic-gate 
2514*7c478bd9Sstevel@tonic-gate 			if (delimptr != NULL && *delimptr != '\0')
2515*7c478bd9Sstevel@tonic-gate 				*delimptr++ = '\0';
2516*7c478bd9Sstevel@tonic-gate 
2517*7c478bd9Sstevel@tonic-gate 			/* put resulting triple from parseaddr() into macros */
2518*7c478bd9Sstevel@tonic-gate 			if (a->q_mailer != NULL)
2519*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
2520*7c478bd9Sstevel@tonic-gate 					macid("{rcpt_mailer}"),
2521*7c478bd9Sstevel@tonic-gate 					a->q_mailer->m_name);
2522*7c478bd9Sstevel@tonic-gate 			else
2523*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
2524*7c478bd9Sstevel@tonic-gate 					macid("{rcpt_mailer}"), NULL);
2525*7c478bd9Sstevel@tonic-gate 			if (a->q_host != NULL)
2526*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
2527*7c478bd9Sstevel@tonic-gate 					macid("{rcpt_host}"), a->q_host);
2528*7c478bd9Sstevel@tonic-gate 			else
2529*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
2530*7c478bd9Sstevel@tonic-gate 					macid("{rcpt_host}"), "localhost");
2531*7c478bd9Sstevel@tonic-gate 			if (a->q_user != NULL)
2532*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
2533*7c478bd9Sstevel@tonic-gate 					macid("{rcpt_addr}"), a->q_user);
2534*7c478bd9Sstevel@tonic-gate 			else
2535*7c478bd9Sstevel@tonic-gate 				macdefine(&e->e_macro, A_PERM,
2536*7c478bd9Sstevel@tonic-gate 					macid("{rcpt_addr}"), NULL);
2537*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2538*7c478bd9Sstevel@tonic-gate 				goto rcpt_done;
2539*7c478bd9Sstevel@tonic-gate 
2540*7c478bd9Sstevel@tonic-gate 			/* now parse ESMTP arguments */
2541*7c478bd9Sstevel@tonic-gate 			addr = p;
2542*7c478bd9Sstevel@tonic-gate 			argno = 0;
2543*7c478bd9Sstevel@tonic-gate 			args[argno++] = p;
2544*7c478bd9Sstevel@tonic-gate 			p = delimptr;
2545*7c478bd9Sstevel@tonic-gate 			while (p != NULL && *p != '\0')
2546*7c478bd9Sstevel@tonic-gate 			{
2547*7c478bd9Sstevel@tonic-gate 				char *kp;
2548*7c478bd9Sstevel@tonic-gate 				char *vp = NULL;
2549*7c478bd9Sstevel@tonic-gate 				char *equal = NULL;
2550*7c478bd9Sstevel@tonic-gate 
2551*7c478bd9Sstevel@tonic-gate 				/* locate the beginning of the keyword */
2552*7c478bd9Sstevel@tonic-gate 				SKIP_SPACE(p);
2553*7c478bd9Sstevel@tonic-gate 				if (*p == '\0')
2554*7c478bd9Sstevel@tonic-gate 					break;
2555*7c478bd9Sstevel@tonic-gate 				kp = p;
2556*7c478bd9Sstevel@tonic-gate 
2557*7c478bd9Sstevel@tonic-gate 				/* skip to the value portion */
2558*7c478bd9Sstevel@tonic-gate 				while ((isascii(*p) && isalnum(*p)) || *p == '-')
2559*7c478bd9Sstevel@tonic-gate 					p++;
2560*7c478bd9Sstevel@tonic-gate 				if (*p == '=')
2561*7c478bd9Sstevel@tonic-gate 				{
2562*7c478bd9Sstevel@tonic-gate 					equal = p;
2563*7c478bd9Sstevel@tonic-gate 					*p++ = '\0';
2564*7c478bd9Sstevel@tonic-gate 					vp = p;
2565*7c478bd9Sstevel@tonic-gate 
2566*7c478bd9Sstevel@tonic-gate 					/* skip to the end of the value */
2567*7c478bd9Sstevel@tonic-gate 					while (*p != '\0' && *p != ' ' &&
2568*7c478bd9Sstevel@tonic-gate 					       !(isascii(*p) && iscntrl(*p)) &&
2569*7c478bd9Sstevel@tonic-gate 					       *p != '=')
2570*7c478bd9Sstevel@tonic-gate 						p++;
2571*7c478bd9Sstevel@tonic-gate 				}
2572*7c478bd9Sstevel@tonic-gate 
2573*7c478bd9Sstevel@tonic-gate 				if (*p != '\0')
2574*7c478bd9Sstevel@tonic-gate 					*p++ = '\0';
2575*7c478bd9Sstevel@tonic-gate 
2576*7c478bd9Sstevel@tonic-gate 				if (tTd(19, 1))
2577*7c478bd9Sstevel@tonic-gate 					sm_dprintf("RCPT: got arg %s=\"%s\"\n", kp,
2578*7c478bd9Sstevel@tonic-gate 						vp == NULL ? "<null>" : vp);
2579*7c478bd9Sstevel@tonic-gate 
2580*7c478bd9Sstevel@tonic-gate 				rcpt_esmtp_args(a, kp, vp, e);
2581*7c478bd9Sstevel@tonic-gate 				if (equal != NULL)
2582*7c478bd9Sstevel@tonic-gate 					*equal = '=';
2583*7c478bd9Sstevel@tonic-gate 				args[argno++] = kp;
2584*7c478bd9Sstevel@tonic-gate 				if (argno >= MAXSMTPARGS - 1)
2585*7c478bd9Sstevel@tonic-gate 					usrerr("501 5.5.4 Too many parameters");
2586*7c478bd9Sstevel@tonic-gate 				if (Errors > 0)
2587*7c478bd9Sstevel@tonic-gate 					break;
2588*7c478bd9Sstevel@tonic-gate 			}
2589*7c478bd9Sstevel@tonic-gate 			args[argno] = NULL;
2590*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2591*7c478bd9Sstevel@tonic-gate 				goto rcpt_done;
2592*7c478bd9Sstevel@tonic-gate 
2593*7c478bd9Sstevel@tonic-gate 			/* do config file checking of the recipient */
2594*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
2595*7c478bd9Sstevel@tonic-gate 				macid("{addr_type}"), "e r");
2596*7c478bd9Sstevel@tonic-gate 			if (rscheck("check_rcpt", addr,
2597*7c478bd9Sstevel@tonic-gate 				    NULL, e, RSF_RMCOMM|RSF_COUNT, 3,
2598*7c478bd9Sstevel@tonic-gate 				    NULL, e->e_id) != EX_OK ||
2599*7c478bd9Sstevel@tonic-gate 			    Errors > 0)
2600*7c478bd9Sstevel@tonic-gate 				goto rcpt_done;
2601*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
2602*7c478bd9Sstevel@tonic-gate 				macid("{addr_type}"), NULL);
2603*7c478bd9Sstevel@tonic-gate 
2604*7c478bd9Sstevel@tonic-gate 			/* If discarding, don't bother to verify user */
2605*7c478bd9Sstevel@tonic-gate 			if (bitset(EF_DISCARD, e->e_flags))
2606*7c478bd9Sstevel@tonic-gate 				a->q_state = QS_VERIFIED;
2607*7c478bd9Sstevel@tonic-gate 
2608*7c478bd9Sstevel@tonic-gate #if MILTER
2609*7c478bd9Sstevel@tonic-gate 			if (smtp.sm_milterlist && smtp.sm_milterize &&
2610*7c478bd9Sstevel@tonic-gate 			    !bitset(EF_DISCARD, e->e_flags))
2611*7c478bd9Sstevel@tonic-gate 			{
2612*7c478bd9Sstevel@tonic-gate 				char state;
2613*7c478bd9Sstevel@tonic-gate 				char *response;
2614*7c478bd9Sstevel@tonic-gate 
2615*7c478bd9Sstevel@tonic-gate 				response = milter_envrcpt(args, e, &state);
2616*7c478bd9Sstevel@tonic-gate 				MILTER_REPLY("to");
2617*7c478bd9Sstevel@tonic-gate 			}
2618*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
2619*7c478bd9Sstevel@tonic-gate 
2620*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
2621*7c478bd9Sstevel@tonic-gate 				macid("{rcpt_mailer}"), NULL);
2622*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
2623*7c478bd9Sstevel@tonic-gate 				macid("{rcpt_host}"), NULL);
2624*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
2625*7c478bd9Sstevel@tonic-gate 				macid("{rcpt_addr}"), NULL);
2626*7c478bd9Sstevel@tonic-gate 			macdefine(&e->e_macro, A_PERM,
2627*7c478bd9Sstevel@tonic-gate 				macid("{dsn_notify}"), NULL);
2628*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2629*7c478bd9Sstevel@tonic-gate 				goto rcpt_done;
2630*7c478bd9Sstevel@tonic-gate 
2631*7c478bd9Sstevel@tonic-gate 			/* save in recipient list after ESMTP mods */
2632*7c478bd9Sstevel@tonic-gate 			a = recipient(a, &e->e_sendqueue, 0, e);
2633*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2634*7c478bd9Sstevel@tonic-gate 				goto rcpt_done;
2635*7c478bd9Sstevel@tonic-gate 
2636*7c478bd9Sstevel@tonic-gate 			/* no errors during parsing, but might be a duplicate */
2637*7c478bd9Sstevel@tonic-gate 			e->e_to = a->q_paddr;
2638*7c478bd9Sstevel@tonic-gate 			if (!QS_IS_BADADDR(a->q_state))
2639*7c478bd9Sstevel@tonic-gate 			{
2640*7c478bd9Sstevel@tonic-gate 				if (smtp.sm_nrcpts == 0)
2641*7c478bd9Sstevel@tonic-gate 					initsys(e);
2642*7c478bd9Sstevel@tonic-gate 				message("250 2.1.5 Recipient ok%s",
2643*7c478bd9Sstevel@tonic-gate 					QS_IS_QUEUEUP(a->q_state) ?
2644*7c478bd9Sstevel@tonic-gate 						" (will queue)" : "");
2645*7c478bd9Sstevel@tonic-gate 				smtp.sm_nrcpts++;
2646*7c478bd9Sstevel@tonic-gate 			}
2647*7c478bd9Sstevel@tonic-gate 			else
2648*7c478bd9Sstevel@tonic-gate 			{
2649*7c478bd9Sstevel@tonic-gate 				/* punt -- should keep message in ADDRESS.... */
2650*7c478bd9Sstevel@tonic-gate 				usrerr("550 5.1.1 Addressee unknown");
2651*7c478bd9Sstevel@tonic-gate 			}
2652*7c478bd9Sstevel@tonic-gate 		    rcpt_done:
2653*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2654*7c478bd9Sstevel@tonic-gate 			{
2655*7c478bd9Sstevel@tonic-gate 				++n_badrcpts;
2656*7c478bd9Sstevel@tonic-gate 				NBADRCPTS;
2657*7c478bd9Sstevel@tonic-gate 			}
2658*7c478bd9Sstevel@tonic-gate 		    }
2659*7c478bd9Sstevel@tonic-gate 		    SM_EXCEPT(exc, "[!F]*")
2660*7c478bd9Sstevel@tonic-gate 		    {
2661*7c478bd9Sstevel@tonic-gate 			/* An exception occurred while processing RCPT */
2662*7c478bd9Sstevel@tonic-gate 			e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
2663*7c478bd9Sstevel@tonic-gate 			++n_badrcpts;
2664*7c478bd9Sstevel@tonic-gate 			NBADRCPTS;
2665*7c478bd9Sstevel@tonic-gate 		    }
2666*7c478bd9Sstevel@tonic-gate 		    SM_END_TRY
2667*7c478bd9Sstevel@tonic-gate 			break;
2668*7c478bd9Sstevel@tonic-gate 
2669*7c478bd9Sstevel@tonic-gate 		  case CMDDATA:		/* data -- text of mail */
2670*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("DATA");
2671*7c478bd9Sstevel@tonic-gate 			if (!smtp_data(&smtp, e))
2672*7c478bd9Sstevel@tonic-gate 				goto doquit;
2673*7c478bd9Sstevel@tonic-gate 			break;
2674*7c478bd9Sstevel@tonic-gate 
2675*7c478bd9Sstevel@tonic-gate 		  case CMDRSET:		/* rset -- reset state */
2676*7c478bd9Sstevel@tonic-gate 			if (tTd(94, 100))
2677*7c478bd9Sstevel@tonic-gate 				message("451 4.0.0 Test failure");
2678*7c478bd9Sstevel@tonic-gate 			else
2679*7c478bd9Sstevel@tonic-gate 				message("250 2.0.0 Reset state");
2680*7c478bd9Sstevel@tonic-gate 			CLEAR_STATE(cmdbuf);
2681*7c478bd9Sstevel@tonic-gate 			break;
2682*7c478bd9Sstevel@tonic-gate 
2683*7c478bd9Sstevel@tonic-gate 		  case CMDVRFY:		/* vrfy -- verify address */
2684*7c478bd9Sstevel@tonic-gate 		  case CMDEXPN:		/* expn -- expand address */
2685*7c478bd9Sstevel@tonic-gate 			vrfy = c->cmd_code == CMDVRFY;
2686*7c478bd9Sstevel@tonic-gate 			DELAY_CONN(vrfy ? "VRFY" : "EXPN");
2687*7c478bd9Sstevel@tonic-gate 			if (tempfail)
2688*7c478bd9Sstevel@tonic-gate 			{
2689*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 9)
2690*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
2691*7c478bd9Sstevel@tonic-gate 						  "SMTP %s command (%.100s) from %s tempfailed (due to previous checks)",
2692*7c478bd9Sstevel@tonic-gate 						  vrfy ? "VRFY" : "EXPN",
2693*7c478bd9Sstevel@tonic-gate 						  p, CurSmtpClient);
2694*7c478bd9Sstevel@tonic-gate 
2695*7c478bd9Sstevel@tonic-gate 				/* RFC 821 doesn't allow 4xy reply code */
2696*7c478bd9Sstevel@tonic-gate 				usrerr("550 5.7.1 Please try again later");
2697*7c478bd9Sstevel@tonic-gate 				break;
2698*7c478bd9Sstevel@tonic-gate 			}
2699*7c478bd9Sstevel@tonic-gate 			wt = checksmtpattack(&n_verifies, MAXVRFYCOMMANDS,
2700*7c478bd9Sstevel@tonic-gate 					     false, vrfy ? "VRFY" : "EXPN", e);
2701*7c478bd9Sstevel@tonic-gate 			STOP_IF_ATTACK(wt);
2702*7c478bd9Sstevel@tonic-gate 			previous = curtime();
2703*7c478bd9Sstevel@tonic-gate 			if ((vrfy && bitset(PRIV_NOVRFY, PrivacyFlags)) ||
2704*7c478bd9Sstevel@tonic-gate 			    (!vrfy && !bitset(SRV_OFFER_EXPN, features)))
2705*7c478bd9Sstevel@tonic-gate 			{
2706*7c478bd9Sstevel@tonic-gate 				if (vrfy)
2707*7c478bd9Sstevel@tonic-gate 					message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
2708*7c478bd9Sstevel@tonic-gate 				else
2709*7c478bd9Sstevel@tonic-gate 					message("502 5.7.0 Sorry, we do not allow this operation");
2710*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 5)
2711*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
2712*7c478bd9Sstevel@tonic-gate 						  "%s: %s [rejected]",
2713*7c478bd9Sstevel@tonic-gate 						  CurSmtpClient,
2714*7c478bd9Sstevel@tonic-gate 						  shortenstring(inp, MAXSHORTSTR));
2715*7c478bd9Sstevel@tonic-gate 				break;
2716*7c478bd9Sstevel@tonic-gate 			}
2717*7c478bd9Sstevel@tonic-gate 			else if (!gothello &&
2718*7c478bd9Sstevel@tonic-gate 				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
2719*7c478bd9Sstevel@tonic-gate 						PrivacyFlags))
2720*7c478bd9Sstevel@tonic-gate 			{
2721*7c478bd9Sstevel@tonic-gate 				usrerr("503 5.0.0 I demand that you introduce yourself first");
2722*7c478bd9Sstevel@tonic-gate 				break;
2723*7c478bd9Sstevel@tonic-gate 			}
2724*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2725*7c478bd9Sstevel@tonic-gate 				break;
2726*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 5)
2727*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id, "%s: %s",
2728*7c478bd9Sstevel@tonic-gate 					  CurSmtpClient,
2729*7c478bd9Sstevel@tonic-gate 					  shortenstring(inp, MAXSHORTSTR));
2730*7c478bd9Sstevel@tonic-gate 		    SM_TRY
2731*7c478bd9Sstevel@tonic-gate 		    {
2732*7c478bd9Sstevel@tonic-gate 			QuickAbort = true;
2733*7c478bd9Sstevel@tonic-gate 			vrfyqueue = NULL;
2734*7c478bd9Sstevel@tonic-gate 			if (vrfy)
2735*7c478bd9Sstevel@tonic-gate 				e->e_flags |= EF_VRFYONLY;
2736*7c478bd9Sstevel@tonic-gate 			while (*p != '\0' && isascii(*p) && isspace(*p))
2737*7c478bd9Sstevel@tonic-gate 				p++;
2738*7c478bd9Sstevel@tonic-gate 			if (*p == '\0')
2739*7c478bd9Sstevel@tonic-gate 			{
2740*7c478bd9Sstevel@tonic-gate 				usrerr("501 5.5.2 Argument required");
2741*7c478bd9Sstevel@tonic-gate 			}
2742*7c478bd9Sstevel@tonic-gate 			else
2743*7c478bd9Sstevel@tonic-gate 			{
2744*7c478bd9Sstevel@tonic-gate 				/* do config file checking of the address */
2745*7c478bd9Sstevel@tonic-gate 				if (rscheck(vrfy ? "check_vrfy" : "check_expn",
2746*7c478bd9Sstevel@tonic-gate 					    p, NULL, e, RSF_RMCOMM,
2747*7c478bd9Sstevel@tonic-gate 					    3, NULL, NOQID) != EX_OK ||
2748*7c478bd9Sstevel@tonic-gate 				    Errors > 0)
2749*7c478bd9Sstevel@tonic-gate 					sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2750*7c478bd9Sstevel@tonic-gate 				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
2751*7c478bd9Sstevel@tonic-gate 			}
2752*7c478bd9Sstevel@tonic-gate 			if (wt > 0)
2753*7c478bd9Sstevel@tonic-gate 			{
2754*7c478bd9Sstevel@tonic-gate 				time_t t;
2755*7c478bd9Sstevel@tonic-gate 
2756*7c478bd9Sstevel@tonic-gate 				t = wt - (curtime() - previous);
2757*7c478bd9Sstevel@tonic-gate 				if (t > 0)
2758*7c478bd9Sstevel@tonic-gate 					(void) sleep(t);
2759*7c478bd9Sstevel@tonic-gate 			}
2760*7c478bd9Sstevel@tonic-gate 			if (Errors > 0)
2761*7c478bd9Sstevel@tonic-gate 				sm_exc_raisenew_x(&EtypeQuickAbort, 1);
2762*7c478bd9Sstevel@tonic-gate 			if (vrfyqueue == NULL)
2763*7c478bd9Sstevel@tonic-gate 			{
2764*7c478bd9Sstevel@tonic-gate 				usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
2765*7c478bd9Sstevel@tonic-gate 			}
2766*7c478bd9Sstevel@tonic-gate 			while (vrfyqueue != NULL)
2767*7c478bd9Sstevel@tonic-gate 			{
2768*7c478bd9Sstevel@tonic-gate 				if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
2769*7c478bd9Sstevel@tonic-gate 				{
2770*7c478bd9Sstevel@tonic-gate 					vrfyqueue = vrfyqueue->q_next;
2771*7c478bd9Sstevel@tonic-gate 					continue;
2772*7c478bd9Sstevel@tonic-gate 				}
2773*7c478bd9Sstevel@tonic-gate 
2774*7c478bd9Sstevel@tonic-gate 				/* see if there is more in the vrfy list */
2775*7c478bd9Sstevel@tonic-gate 				a = vrfyqueue;
2776*7c478bd9Sstevel@tonic-gate 				while ((a = a->q_next) != NULL &&
2777*7c478bd9Sstevel@tonic-gate 				       (!QS_IS_UNDELIVERED(a->q_state)))
2778*7c478bd9Sstevel@tonic-gate 					continue;
2779*7c478bd9Sstevel@tonic-gate 				printvrfyaddr(vrfyqueue, a == NULL, vrfy);
2780*7c478bd9Sstevel@tonic-gate 				vrfyqueue = a;
2781*7c478bd9Sstevel@tonic-gate 			}
2782*7c478bd9Sstevel@tonic-gate 		    }
2783*7c478bd9Sstevel@tonic-gate 		    SM_EXCEPT(exc, "[!F]*")
2784*7c478bd9Sstevel@tonic-gate 		    {
2785*7c478bd9Sstevel@tonic-gate 			/*
2786*7c478bd9Sstevel@tonic-gate 			**  An exception occurred while processing VRFY/EXPN
2787*7c478bd9Sstevel@tonic-gate 			*/
2788*7c478bd9Sstevel@tonic-gate 
2789*7c478bd9Sstevel@tonic-gate 			sm_exc_free(exc);
2790*7c478bd9Sstevel@tonic-gate 			goto undo;
2791*7c478bd9Sstevel@tonic-gate 		    }
2792*7c478bd9Sstevel@tonic-gate 		    SM_END_TRY
2793*7c478bd9Sstevel@tonic-gate 			break;
2794*7c478bd9Sstevel@tonic-gate 
2795*7c478bd9Sstevel@tonic-gate 		  case CMDETRN:		/* etrn -- force queue flush */
2796*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("ETRN");
2797*7c478bd9Sstevel@tonic-gate 
2798*7c478bd9Sstevel@tonic-gate 			/* Don't leak queue information via debug flags */
2799*7c478bd9Sstevel@tonic-gate 			if (!bitset(SRV_OFFER_ETRN, features) || UseMSP ||
2800*7c478bd9Sstevel@tonic-gate 			    (RealUid != 0 && RealUid != TrustedUid &&
2801*7c478bd9Sstevel@tonic-gate 			     OpMode == MD_SMTP))
2802*7c478bd9Sstevel@tonic-gate 			{
2803*7c478bd9Sstevel@tonic-gate 				/* different message for MSA ? */
2804*7c478bd9Sstevel@tonic-gate 				message("502 5.7.0 Sorry, we do not allow this operation");
2805*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 5)
2806*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
2807*7c478bd9Sstevel@tonic-gate 						  "%s: %s [rejected]",
2808*7c478bd9Sstevel@tonic-gate 						  CurSmtpClient,
2809*7c478bd9Sstevel@tonic-gate 						  shortenstring(inp, MAXSHORTSTR));
2810*7c478bd9Sstevel@tonic-gate 				break;
2811*7c478bd9Sstevel@tonic-gate 			}
2812*7c478bd9Sstevel@tonic-gate 			if (tempfail)
2813*7c478bd9Sstevel@tonic-gate 			{
2814*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 9)
2815*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
2816*7c478bd9Sstevel@tonic-gate 						  "SMTP ETRN command (%.100s) from %s tempfailed (due to previous checks)",
2817*7c478bd9Sstevel@tonic-gate 						  p, CurSmtpClient);
2818*7c478bd9Sstevel@tonic-gate 				usrerr(MSG_TEMPFAIL);
2819*7c478bd9Sstevel@tonic-gate 				break;
2820*7c478bd9Sstevel@tonic-gate 			}
2821*7c478bd9Sstevel@tonic-gate 
2822*7c478bd9Sstevel@tonic-gate 			if (strlen(p) <= 0)
2823*7c478bd9Sstevel@tonic-gate 			{
2824*7c478bd9Sstevel@tonic-gate 				usrerr("500 5.5.2 Parameter required");
2825*7c478bd9Sstevel@tonic-gate 				break;
2826*7c478bd9Sstevel@tonic-gate 			}
2827*7c478bd9Sstevel@tonic-gate 
2828*7c478bd9Sstevel@tonic-gate 			/* crude way to avoid denial-of-service attacks */
2829*7c478bd9Sstevel@tonic-gate 			STOP_IF_ATTACK(checksmtpattack(&n_etrn, MAXETRNCOMMANDS,
2830*7c478bd9Sstevel@tonic-gate 							true, "ETRN", e));
2831*7c478bd9Sstevel@tonic-gate 
2832*7c478bd9Sstevel@tonic-gate 			/*
2833*7c478bd9Sstevel@tonic-gate 			**  Do config file checking of the parameter.
2834*7c478bd9Sstevel@tonic-gate 			**  Even though we have srv_features now, we still
2835*7c478bd9Sstevel@tonic-gate 			**  need this ruleset because the former is called
2836*7c478bd9Sstevel@tonic-gate 			**  when the connection has been established, while
2837*7c478bd9Sstevel@tonic-gate 			**  this ruleset is called when the command is
2838*7c478bd9Sstevel@tonic-gate 			**  actually issued and therefore has all information
2839*7c478bd9Sstevel@tonic-gate 			**  available to make a decision.
2840*7c478bd9Sstevel@tonic-gate 			*/
2841*7c478bd9Sstevel@tonic-gate 
2842*7c478bd9Sstevel@tonic-gate 			if (rscheck("check_etrn", p, NULL, e,
2843*7c478bd9Sstevel@tonic-gate 				    RSF_RMCOMM, 3, NULL, NOQID) != EX_OK ||
2844*7c478bd9Sstevel@tonic-gate 			    Errors > 0)
2845*7c478bd9Sstevel@tonic-gate 				break;
2846*7c478bd9Sstevel@tonic-gate 
2847*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 5)
2848*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
2849*7c478bd9Sstevel@tonic-gate 					  "%s: ETRN %s", CurSmtpClient,
2850*7c478bd9Sstevel@tonic-gate 					  shortenstring(p, MAXSHORTSTR));
2851*7c478bd9Sstevel@tonic-gate 
2852*7c478bd9Sstevel@tonic-gate 			id = p;
2853*7c478bd9Sstevel@tonic-gate 			if (*id == '#')
2854*7c478bd9Sstevel@tonic-gate 			{
2855*7c478bd9Sstevel@tonic-gate 				int i, qgrp;
2856*7c478bd9Sstevel@tonic-gate 
2857*7c478bd9Sstevel@tonic-gate 				id++;
2858*7c478bd9Sstevel@tonic-gate 				qgrp = name2qid(id);
2859*7c478bd9Sstevel@tonic-gate 				if (!ISVALIDQGRP(qgrp))
2860*7c478bd9Sstevel@tonic-gate 				{
2861*7c478bd9Sstevel@tonic-gate 					usrerr("459 4.5.4 Queue %s unknown",
2862*7c478bd9Sstevel@tonic-gate 					       id);
2863*7c478bd9Sstevel@tonic-gate 					break;
2864*7c478bd9Sstevel@tonic-gate 				}
2865*7c478bd9Sstevel@tonic-gate 				for (i = 0; i < NumQueue && Queue[i] != NULL;
2866*7c478bd9Sstevel@tonic-gate 				     i++)
2867*7c478bd9Sstevel@tonic-gate 					Queue[i]->qg_nextrun = (time_t) -1;
2868*7c478bd9Sstevel@tonic-gate 				Queue[qgrp]->qg_nextrun = 0;
2869*7c478bd9Sstevel@tonic-gate 				ok = run_work_group(Queue[qgrp]->qg_wgrp,
2870*7c478bd9Sstevel@tonic-gate 						    RWG_FORK|RWG_FORCE);
2871*7c478bd9Sstevel@tonic-gate 				if (ok && Errors == 0)
2872*7c478bd9Sstevel@tonic-gate 					message("250 2.0.0 Queuing for queue group %s started", id);
2873*7c478bd9Sstevel@tonic-gate 				break;
2874*7c478bd9Sstevel@tonic-gate 			}
2875*7c478bd9Sstevel@tonic-gate 
2876*7c478bd9Sstevel@tonic-gate 			if (*id == '@')
2877*7c478bd9Sstevel@tonic-gate 				id++;
2878*7c478bd9Sstevel@tonic-gate 			else
2879*7c478bd9Sstevel@tonic-gate 				*--id = '@';
2880*7c478bd9Sstevel@tonic-gate 
2881*7c478bd9Sstevel@tonic-gate 			new = (QUEUE_CHAR *) sm_malloc(sizeof(QUEUE_CHAR));
2882*7c478bd9Sstevel@tonic-gate 			if (new == NULL)
2883*7c478bd9Sstevel@tonic-gate 			{
2884*7c478bd9Sstevel@tonic-gate 				syserr("500 5.5.0 ETRN out of memory");
2885*7c478bd9Sstevel@tonic-gate 				break;
2886*7c478bd9Sstevel@tonic-gate 			}
2887*7c478bd9Sstevel@tonic-gate 			new->queue_match = id;
2888*7c478bd9Sstevel@tonic-gate 			new->queue_negate = false;
2889*7c478bd9Sstevel@tonic-gate 			new->queue_next = NULL;
2890*7c478bd9Sstevel@tonic-gate 			QueueLimitRecipient = new;
2891*7c478bd9Sstevel@tonic-gate 			ok = runqueue(true, false, false, true);
2892*7c478bd9Sstevel@tonic-gate 			sm_free(QueueLimitRecipient); /* XXX */
2893*7c478bd9Sstevel@tonic-gate 			QueueLimitRecipient = NULL;
2894*7c478bd9Sstevel@tonic-gate 			if (ok && Errors == 0)
2895*7c478bd9Sstevel@tonic-gate 				message("250 2.0.0 Queuing for node %s started", p);
2896*7c478bd9Sstevel@tonic-gate 			break;
2897*7c478bd9Sstevel@tonic-gate 
2898*7c478bd9Sstevel@tonic-gate 		  case CMDHELP:		/* help -- give user info */
2899*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("HELP");
2900*7c478bd9Sstevel@tonic-gate 			help(p, e);
2901*7c478bd9Sstevel@tonic-gate 			break;
2902*7c478bd9Sstevel@tonic-gate 
2903*7c478bd9Sstevel@tonic-gate 		  case CMDNOOP:		/* noop -- do nothing */
2904*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("NOOP");
2905*7c478bd9Sstevel@tonic-gate 			STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS,
2906*7c478bd9Sstevel@tonic-gate 							true, "NOOP", e));
2907*7c478bd9Sstevel@tonic-gate 			message("250 2.0.0 OK");
2908*7c478bd9Sstevel@tonic-gate 			break;
2909*7c478bd9Sstevel@tonic-gate 
2910*7c478bd9Sstevel@tonic-gate 		  case CMDQUIT:		/* quit -- leave mail */
2911*7c478bd9Sstevel@tonic-gate 			message("221 2.0.0 %s closing connection", MyHostName);
2912*7c478bd9Sstevel@tonic-gate #if PIPELINING
2913*7c478bd9Sstevel@tonic-gate 			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
2914*7c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
2915*7c478bd9Sstevel@tonic-gate 
2916*7c478bd9Sstevel@tonic-gate 			if (smtp.sm_nrcpts > 0)
2917*7c478bd9Sstevel@tonic-gate 				logundelrcpts(e, "aborted by sender", 9, false);
2918*7c478bd9Sstevel@tonic-gate 
2919*7c478bd9Sstevel@tonic-gate 			/* arrange to ignore any current send list */
2920*7c478bd9Sstevel@tonic-gate 			e->e_sendqueue = NULL;
2921*7c478bd9Sstevel@tonic-gate 
2922*7c478bd9Sstevel@tonic-gate #if STARTTLS
2923*7c478bd9Sstevel@tonic-gate 			/* shutdown TLS connection */
2924*7c478bd9Sstevel@tonic-gate 			if (tls_active)
2925*7c478bd9Sstevel@tonic-gate 			{
2926*7c478bd9Sstevel@tonic-gate 				(void) endtls(srv_ssl, "server");
2927*7c478bd9Sstevel@tonic-gate 				tls_active = false;
2928*7c478bd9Sstevel@tonic-gate 			}
2929*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
2930*7c478bd9Sstevel@tonic-gate #if SASL
2931*7c478bd9Sstevel@tonic-gate 			if (authenticating == SASL_IS_AUTH)
2932*7c478bd9Sstevel@tonic-gate 			{
2933*7c478bd9Sstevel@tonic-gate 				sasl_dispose(&conn);
2934*7c478bd9Sstevel@tonic-gate 				authenticating = SASL_NOT_AUTH;
2935*7c478bd9Sstevel@tonic-gate 				/* XXX sasl_done(); this is a child */
2936*7c478bd9Sstevel@tonic-gate 			}
2937*7c478bd9Sstevel@tonic-gate #endif /* SASL */
2938*7c478bd9Sstevel@tonic-gate 
2939*7c478bd9Sstevel@tonic-gate doquit:
2940*7c478bd9Sstevel@tonic-gate 			/* avoid future 050 messages */
2941*7c478bd9Sstevel@tonic-gate 			disconnect(1, e);
2942*7c478bd9Sstevel@tonic-gate 
2943*7c478bd9Sstevel@tonic-gate #if MILTER
2944*7c478bd9Sstevel@tonic-gate 			/* close out milter filters */
2945*7c478bd9Sstevel@tonic-gate 			milter_quit(e);
2946*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
2947*7c478bd9Sstevel@tonic-gate 
2948*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
2949*7c478bd9Sstevel@tonic-gate 				logsender(e, NULL);
2950*7c478bd9Sstevel@tonic-gate 			e->e_flags &= ~EF_LOGSENDER;
2951*7c478bd9Sstevel@tonic-gate 
2952*7c478bd9Sstevel@tonic-gate 			if (lognullconnection && LogLevel > 5 &&
2953*7c478bd9Sstevel@tonic-gate 			    nullserver == NULL)
2954*7c478bd9Sstevel@tonic-gate 			{
2955*7c478bd9Sstevel@tonic-gate 				char *d;
2956*7c478bd9Sstevel@tonic-gate 
2957*7c478bd9Sstevel@tonic-gate 				d = macvalue(macid("{daemon_name}"), e);
2958*7c478bd9Sstevel@tonic-gate 				if (d == NULL)
2959*7c478bd9Sstevel@tonic-gate 					d = "stdin";
2960*7c478bd9Sstevel@tonic-gate 
2961*7c478bd9Sstevel@tonic-gate 				/*
2962*7c478bd9Sstevel@tonic-gate 				**  even though this id is "bogus", it makes
2963*7c478bd9Sstevel@tonic-gate 				**  it simpler to "grep" related events, e.g.,
2964*7c478bd9Sstevel@tonic-gate 				**  timeouts for the same connection.
2965*7c478bd9Sstevel@tonic-gate 				*/
2966*7c478bd9Sstevel@tonic-gate 
2967*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
2968*7c478bd9Sstevel@tonic-gate 					  "%s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
2969*7c478bd9Sstevel@tonic-gate 					  CurSmtpClient, d);
2970*7c478bd9Sstevel@tonic-gate 			}
2971*7c478bd9Sstevel@tonic-gate 			if (tTd(93, 100))
2972*7c478bd9Sstevel@tonic-gate 			{
2973*7c478bd9Sstevel@tonic-gate 				/* return to handle next connection */
2974*7c478bd9Sstevel@tonic-gate 				return;
2975*7c478bd9Sstevel@tonic-gate 			}
2976*7c478bd9Sstevel@tonic-gate 			finis(true, true, ExitStat);
2977*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
2978*7c478bd9Sstevel@tonic-gate 
2979*7c478bd9Sstevel@tonic-gate 		  case CMDVERB:		/* set verbose mode */
2980*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("VERB");
2981*7c478bd9Sstevel@tonic-gate 			if (!bitset(SRV_OFFER_EXPN, features) ||
2982*7c478bd9Sstevel@tonic-gate 			    !bitset(SRV_OFFER_VERB, features))
2983*7c478bd9Sstevel@tonic-gate 			{
2984*7c478bd9Sstevel@tonic-gate 				/* this would give out the same info */
2985*7c478bd9Sstevel@tonic-gate 				message("502 5.7.0 Verbose unavailable");
2986*7c478bd9Sstevel@tonic-gate 				break;
2987*7c478bd9Sstevel@tonic-gate 			}
2988*7c478bd9Sstevel@tonic-gate 			STOP_IF_ATTACK(checksmtpattack(&n_noop, MAXNOOPCOMMANDS,
2989*7c478bd9Sstevel@tonic-gate 							true, "VERB", e));
2990*7c478bd9Sstevel@tonic-gate 			Verbose = 1;
2991*7c478bd9Sstevel@tonic-gate 			set_delivery_mode(SM_DELIVER, e);
2992*7c478bd9Sstevel@tonic-gate 			message("250 2.0.0 Verbose mode");
2993*7c478bd9Sstevel@tonic-gate 			break;
2994*7c478bd9Sstevel@tonic-gate 
2995*7c478bd9Sstevel@tonic-gate #if SMTPDEBUG
2996*7c478bd9Sstevel@tonic-gate 		  case CMDDBGQSHOW:	/* show queues */
2997*7c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
2998*7c478bd9Sstevel@tonic-gate 					     "Send Queue=");
2999*7c478bd9Sstevel@tonic-gate 			printaddr(smioout, e->e_sendqueue, true);
3000*7c478bd9Sstevel@tonic-gate 			break;
3001*7c478bd9Sstevel@tonic-gate 
3002*7c478bd9Sstevel@tonic-gate 		  case CMDDBGDEBUG:	/* set debug mode */
3003*7c478bd9Sstevel@tonic-gate 			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
3004*7c478bd9Sstevel@tonic-gate 			tTflag(p);
3005*7c478bd9Sstevel@tonic-gate 			message("200 2.0.0 Debug set");
3006*7c478bd9Sstevel@tonic-gate 			break;
3007*7c478bd9Sstevel@tonic-gate 
3008*7c478bd9Sstevel@tonic-gate #else /* SMTPDEBUG */
3009*7c478bd9Sstevel@tonic-gate 		  case CMDDBGQSHOW:	/* show queues */
3010*7c478bd9Sstevel@tonic-gate 		  case CMDDBGDEBUG:	/* set debug mode */
3011*7c478bd9Sstevel@tonic-gate #endif /* SMTPDEBUG */
3012*7c478bd9Sstevel@tonic-gate 		  case CMDLOGBOGUS:	/* bogus command */
3013*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("Bogus");
3014*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 0)
3015*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_CRIT, e->e_id,
3016*7c478bd9Sstevel@tonic-gate 					  "\"%s\" command from %s (%.100s)",
3017*7c478bd9Sstevel@tonic-gate 					  c->cmd_name, CurSmtpClient,
3018*7c478bd9Sstevel@tonic-gate 					  anynet_ntoa(&RealHostAddr));
3019*7c478bd9Sstevel@tonic-gate 			/* FALLTHROUGH */
3020*7c478bd9Sstevel@tonic-gate 
3021*7c478bd9Sstevel@tonic-gate 		  case CMDERROR:	/* unknown command */
3022*7c478bd9Sstevel@tonic-gate #if MAXBADCOMMANDS > 0
3023*7c478bd9Sstevel@tonic-gate 			if (++n_badcmds > MAXBADCOMMANDS)
3024*7c478bd9Sstevel@tonic-gate 			{
3025*7c478bd9Sstevel@tonic-gate   stopattack:
3026*7c478bd9Sstevel@tonic-gate 				message("421 4.7.0 %s Too many bad commands; closing connection",
3027*7c478bd9Sstevel@tonic-gate 					MyHostName);
3028*7c478bd9Sstevel@tonic-gate 
3029*7c478bd9Sstevel@tonic-gate 				/* arrange to ignore any current send list */
3030*7c478bd9Sstevel@tonic-gate 				e->e_sendqueue = NULL;
3031*7c478bd9Sstevel@tonic-gate 				goto doquit;
3032*7c478bd9Sstevel@tonic-gate 			}
3033*7c478bd9Sstevel@tonic-gate #endif /* MAXBADCOMMANDS > 0 */
3034*7c478bd9Sstevel@tonic-gate 
3035*7c478bd9Sstevel@tonic-gate #if MILTER && SMFI_VERSION > 2
3036*7c478bd9Sstevel@tonic-gate 			if (smtp.sm_milterlist && smtp.sm_milterize &&
3037*7c478bd9Sstevel@tonic-gate 			    !bitset(EF_DISCARD, e->e_flags))
3038*7c478bd9Sstevel@tonic-gate 			{
3039*7c478bd9Sstevel@tonic-gate 				char state;
3040*7c478bd9Sstevel@tonic-gate 				char *response;
3041*7c478bd9Sstevel@tonic-gate 
3042*7c478bd9Sstevel@tonic-gate 				if (MilterLogLevel > 9)
3043*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_INFO, e->e_id,
3044*7c478bd9Sstevel@tonic-gate 						"Sending \"%s\" to Milter", inp);
3045*7c478bd9Sstevel@tonic-gate 				response = milter_unknown(inp, e, &state);
3046*7c478bd9Sstevel@tonic-gate 				MILTER_REPLY("unknown");
3047*7c478bd9Sstevel@tonic-gate 				if (state == SMFIR_REPLYCODE ||
3048*7c478bd9Sstevel@tonic-gate 				    state == SMFIR_REJECT ||
3049*7c478bd9Sstevel@tonic-gate 				    state == SMFIR_TEMPFAIL)
3050*7c478bd9Sstevel@tonic-gate 				{
3051*7c478bd9Sstevel@tonic-gate 					/* MILTER_REPLY already gave an error */
3052*7c478bd9Sstevel@tonic-gate 					break;
3053*7c478bd9Sstevel@tonic-gate 				}
3054*7c478bd9Sstevel@tonic-gate 			}
3055*7c478bd9Sstevel@tonic-gate #endif /* MILTER && SMFI_VERSION > 2 */
3056*7c478bd9Sstevel@tonic-gate 
3057*7c478bd9Sstevel@tonic-gate 			usrerr("500 5.5.1 Command unrecognized: \"%s\"",
3058*7c478bd9Sstevel@tonic-gate 			       shortenstring(inp, MAXSHORTSTR));
3059*7c478bd9Sstevel@tonic-gate 			break;
3060*7c478bd9Sstevel@tonic-gate 
3061*7c478bd9Sstevel@tonic-gate 		  case CMDUNIMPL:
3062*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("Unimpl");
3063*7c478bd9Sstevel@tonic-gate 			usrerr("502 5.5.1 Command not implemented: \"%s\"",
3064*7c478bd9Sstevel@tonic-gate 			       shortenstring(inp, MAXSHORTSTR));
3065*7c478bd9Sstevel@tonic-gate 			break;
3066*7c478bd9Sstevel@tonic-gate 
3067*7c478bd9Sstevel@tonic-gate 		  default:
3068*7c478bd9Sstevel@tonic-gate 			DELAY_CONN("default");
3069*7c478bd9Sstevel@tonic-gate 			errno = 0;
3070*7c478bd9Sstevel@tonic-gate 			syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
3071*7c478bd9Sstevel@tonic-gate 			break;
3072*7c478bd9Sstevel@tonic-gate 		}
3073*7c478bd9Sstevel@tonic-gate #if SASL
3074*7c478bd9Sstevel@tonic-gate 		}
3075*7c478bd9Sstevel@tonic-gate #endif /* SASL */
3076*7c478bd9Sstevel@tonic-gate 	    }
3077*7c478bd9Sstevel@tonic-gate 	    SM_EXCEPT(exc, "[!F]*")
3078*7c478bd9Sstevel@tonic-gate 	    {
3079*7c478bd9Sstevel@tonic-gate 		/*
3080*7c478bd9Sstevel@tonic-gate 		**  The only possible exception is "E:mta.quickabort".
3081*7c478bd9Sstevel@tonic-gate 		**  There is nothing to do except fall through and loop.
3082*7c478bd9Sstevel@tonic-gate 		*/
3083*7c478bd9Sstevel@tonic-gate 	    }
3084*7c478bd9Sstevel@tonic-gate 	    SM_END_TRY
3085*7c478bd9Sstevel@tonic-gate 	}
3086*7c478bd9Sstevel@tonic-gate }
3087*7c478bd9Sstevel@tonic-gate /*
3088*7c478bd9Sstevel@tonic-gate **  SMTP_DATA -- implement the SMTP DATA command.
3089*7c478bd9Sstevel@tonic-gate **
3090*7c478bd9Sstevel@tonic-gate **	Parameters:
3091*7c478bd9Sstevel@tonic-gate **		smtp -- status of SMTP connection.
3092*7c478bd9Sstevel@tonic-gate **		e -- envelope.
3093*7c478bd9Sstevel@tonic-gate **
3094*7c478bd9Sstevel@tonic-gate **	Returns:
3095*7c478bd9Sstevel@tonic-gate **		true iff SMTP session can continue.
3096*7c478bd9Sstevel@tonic-gate **
3097*7c478bd9Sstevel@tonic-gate **	Side Effects:
3098*7c478bd9Sstevel@tonic-gate **		possibly sends message.
3099*7c478bd9Sstevel@tonic-gate */
3100*7c478bd9Sstevel@tonic-gate 
3101*7c478bd9Sstevel@tonic-gate static bool
3102*7c478bd9Sstevel@tonic-gate smtp_data(smtp, e)
3103*7c478bd9Sstevel@tonic-gate 	SMTP_T *smtp;
3104*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3105*7c478bd9Sstevel@tonic-gate {
3106*7c478bd9Sstevel@tonic-gate #if MILTER
3107*7c478bd9Sstevel@tonic-gate 	bool milteraccept;
3108*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
3109*7c478bd9Sstevel@tonic-gate 	bool aborting;
3110*7c478bd9Sstevel@tonic-gate 	bool doublequeue;
3111*7c478bd9Sstevel@tonic-gate 	ADDRESS *a;
3112*7c478bd9Sstevel@tonic-gate 	ENVELOPE *ee;
3113*7c478bd9Sstevel@tonic-gate 	char *id;
3114*7c478bd9Sstevel@tonic-gate 	char *oldid;
3115*7c478bd9Sstevel@tonic-gate 	char buf[32];
3116*7c478bd9Sstevel@tonic-gate 
3117*7c478bd9Sstevel@tonic-gate 	SmtpPhase = "server DATA";
3118*7c478bd9Sstevel@tonic-gate 	if (!smtp->sm_gotmail)
3119*7c478bd9Sstevel@tonic-gate 	{
3120*7c478bd9Sstevel@tonic-gate 		usrerr("503 5.0.0 Need MAIL command");
3121*7c478bd9Sstevel@tonic-gate 		return true;
3122*7c478bd9Sstevel@tonic-gate 	}
3123*7c478bd9Sstevel@tonic-gate 	else if (smtp->sm_nrcpts <= 0)
3124*7c478bd9Sstevel@tonic-gate 	{
3125*7c478bd9Sstevel@tonic-gate 		usrerr("503 5.0.0 Need RCPT (recipient)");
3126*7c478bd9Sstevel@tonic-gate 		return true;
3127*7c478bd9Sstevel@tonic-gate 	}
3128*7c478bd9Sstevel@tonic-gate 	(void) sm_snprintf(buf, sizeof buf, "%u", smtp->sm_nrcpts);
3129*7c478bd9Sstevel@tonic-gate 	if (rscheck("check_data", buf, NULL, e,
3130*7c478bd9Sstevel@tonic-gate 		    RSF_RMCOMM|RSF_UNSTRUCTURED|RSF_COUNT, 3, NULL,
3131*7c478bd9Sstevel@tonic-gate 		    e->e_id) != EX_OK)
3132*7c478bd9Sstevel@tonic-gate 		return true;
3133*7c478bd9Sstevel@tonic-gate 
3134*7c478bd9Sstevel@tonic-gate #if MILTER && SMFI_VERSION > 3
3135*7c478bd9Sstevel@tonic-gate 	if (smtp->sm_milterlist && smtp->sm_milterize &&
3136*7c478bd9Sstevel@tonic-gate 	    !bitset(EF_DISCARD, e->e_flags))
3137*7c478bd9Sstevel@tonic-gate 	{
3138*7c478bd9Sstevel@tonic-gate 		char state;
3139*7c478bd9Sstevel@tonic-gate 		char *response;
3140*7c478bd9Sstevel@tonic-gate 		int savelogusrerrs = LogUsrErrs;
3141*7c478bd9Sstevel@tonic-gate 
3142*7c478bd9Sstevel@tonic-gate 		response = milter_data_cmd(e, &state);
3143*7c478bd9Sstevel@tonic-gate 		switch (state)
3144*7c478bd9Sstevel@tonic-gate 		{
3145*7c478bd9Sstevel@tonic-gate 		  case SMFIR_REPLYCODE:
3146*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
3147*7c478bd9Sstevel@tonic-gate 			{
3148*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
3149*7c478bd9Sstevel@tonic-gate 					  "Milter: cmd=data, reject=%s",
3150*7c478bd9Sstevel@tonic-gate 					  response);
3151*7c478bd9Sstevel@tonic-gate 				LogUsrErrs = false;
3152*7c478bd9Sstevel@tonic-gate 			}
3153*7c478bd9Sstevel@tonic-gate 			usrerr(response);
3154*7c478bd9Sstevel@tonic-gate 			if (strncmp(response, "421 ", 4) == 0)
3155*7c478bd9Sstevel@tonic-gate 			{
3156*7c478bd9Sstevel@tonic-gate 				e->e_sendqueue = NULL;
3157*7c478bd9Sstevel@tonic-gate 				return false;
3158*7c478bd9Sstevel@tonic-gate 			}
3159*7c478bd9Sstevel@tonic-gate 			return true;
3160*7c478bd9Sstevel@tonic-gate 
3161*7c478bd9Sstevel@tonic-gate 		  case SMFIR_REJECT:
3162*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
3163*7c478bd9Sstevel@tonic-gate 			{
3164*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
3165*7c478bd9Sstevel@tonic-gate 					  "Milter: cmd=data, reject=550 5.7.1 Command rejected");
3166*7c478bd9Sstevel@tonic-gate 				LogUsrErrs = false;
3167*7c478bd9Sstevel@tonic-gate 			}
3168*7c478bd9Sstevel@tonic-gate 			usrerr("550 5.7.1 Command rejected");
3169*7c478bd9Sstevel@tonic-gate 			return true;
3170*7c478bd9Sstevel@tonic-gate 
3171*7c478bd9Sstevel@tonic-gate 		  case SMFIR_DISCARD:
3172*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
3173*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
3174*7c478bd9Sstevel@tonic-gate 					  "Milter: cmd=data, discard");
3175*7c478bd9Sstevel@tonic-gate 			e->e_flags |= EF_DISCARD;
3176*7c478bd9Sstevel@tonic-gate 			break;
3177*7c478bd9Sstevel@tonic-gate 
3178*7c478bd9Sstevel@tonic-gate 		  case SMFIR_TEMPFAIL:
3179*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
3180*7c478bd9Sstevel@tonic-gate 			{
3181*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
3182*7c478bd9Sstevel@tonic-gate 					  "Milter: cmd=data, reject=%s",
3183*7c478bd9Sstevel@tonic-gate 					  MSG_TEMPFAIL);
3184*7c478bd9Sstevel@tonic-gate 				LogUsrErrs = false;
3185*7c478bd9Sstevel@tonic-gate 			}
3186*7c478bd9Sstevel@tonic-gate 			usrerr(MSG_TEMPFAIL);
3187*7c478bd9Sstevel@tonic-gate 			return true;
3188*7c478bd9Sstevel@tonic-gate 		}
3189*7c478bd9Sstevel@tonic-gate 		LogUsrErrs = savelogusrerrs;
3190*7c478bd9Sstevel@tonic-gate 		if (response != NULL)
3191*7c478bd9Sstevel@tonic-gate 			sm_free(response); /* XXX */
3192*7c478bd9Sstevel@tonic-gate 	}
3193*7c478bd9Sstevel@tonic-gate #endif /* MILTER && SMFI_VERSION > 3 */
3194*7c478bd9Sstevel@tonic-gate 
3195*7c478bd9Sstevel@tonic-gate 	/* put back discard bit */
3196*7c478bd9Sstevel@tonic-gate 	if (smtp->sm_discard)
3197*7c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_DISCARD;
3198*7c478bd9Sstevel@tonic-gate 
3199*7c478bd9Sstevel@tonic-gate 	/* check to see if we need to re-expand aliases */
3200*7c478bd9Sstevel@tonic-gate 	/* also reset QS_BADADDR on already-diagnosted addrs */
3201*7c478bd9Sstevel@tonic-gate 	doublequeue = false;
3202*7c478bd9Sstevel@tonic-gate 	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
3203*7c478bd9Sstevel@tonic-gate 	{
3204*7c478bd9Sstevel@tonic-gate 		if (QS_IS_VERIFIED(a->q_state) &&
3205*7c478bd9Sstevel@tonic-gate 		    !bitset(EF_DISCARD, e->e_flags))
3206*7c478bd9Sstevel@tonic-gate 		{
3207*7c478bd9Sstevel@tonic-gate 			/* need to re-expand aliases */
3208*7c478bd9Sstevel@tonic-gate 			doublequeue = true;
3209*7c478bd9Sstevel@tonic-gate 		}
3210*7c478bd9Sstevel@tonic-gate 		if (QS_IS_BADADDR(a->q_state))
3211*7c478bd9Sstevel@tonic-gate 		{
3212*7c478bd9Sstevel@tonic-gate 			/* make this "go away" */
3213*7c478bd9Sstevel@tonic-gate 			a->q_state = QS_DONTSEND;
3214*7c478bd9Sstevel@tonic-gate 		}
3215*7c478bd9Sstevel@tonic-gate 	}
3216*7c478bd9Sstevel@tonic-gate 
3217*7c478bd9Sstevel@tonic-gate 	/* collect the text of the message */
3218*7c478bd9Sstevel@tonic-gate 	SmtpPhase = "collect";
3219*7c478bd9Sstevel@tonic-gate 	buffer_errors();
3220*7c478bd9Sstevel@tonic-gate 
3221*7c478bd9Sstevel@tonic-gate 	collect(InChannel, true, NULL, e, true);
3222*7c478bd9Sstevel@tonic-gate 
3223*7c478bd9Sstevel@tonic-gate 	/* redefine message size */
3224*7c478bd9Sstevel@tonic-gate 	(void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
3225*7c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
3226*7c478bd9Sstevel@tonic-gate 
3227*7c478bd9Sstevel@tonic-gate #if _FFR_CHECK_EOM
3228*7c478bd9Sstevel@tonic-gate 	/* rscheck() will set Errors or EF_DISCARD if it trips */
3229*7c478bd9Sstevel@tonic-gate 	(void) rscheck("check_eom", buf, NULL, e, RSF_UNSTRUCTURED|RSF_COUNT,
3230*7c478bd9Sstevel@tonic-gate 		       3, NULL, e->e_id);
3231*7c478bd9Sstevel@tonic-gate #endif /* _FFR_CHECK_EOM */
3232*7c478bd9Sstevel@tonic-gate 
3233*7c478bd9Sstevel@tonic-gate #if MILTER
3234*7c478bd9Sstevel@tonic-gate 	milteraccept = true;
3235*7c478bd9Sstevel@tonic-gate 	if (smtp->sm_milterlist && smtp->sm_milterize &&
3236*7c478bd9Sstevel@tonic-gate 	    Errors <= 0 &&
3237*7c478bd9Sstevel@tonic-gate 	    !bitset(EF_DISCARD, e->e_flags))
3238*7c478bd9Sstevel@tonic-gate 	{
3239*7c478bd9Sstevel@tonic-gate 		char state;
3240*7c478bd9Sstevel@tonic-gate 		char *response;
3241*7c478bd9Sstevel@tonic-gate 
3242*7c478bd9Sstevel@tonic-gate 		response = milter_data(e, &state);
3243*7c478bd9Sstevel@tonic-gate 		switch (state)
3244*7c478bd9Sstevel@tonic-gate 		{
3245*7c478bd9Sstevel@tonic-gate 		  case SMFIR_REPLYCODE:
3246*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
3247*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
3248*7c478bd9Sstevel@tonic-gate 					  "Milter: data, reject=%s",
3249*7c478bd9Sstevel@tonic-gate 					  response);
3250*7c478bd9Sstevel@tonic-gate 			milteraccept = false;
3251*7c478bd9Sstevel@tonic-gate 			usrerr(response);
3252*7c478bd9Sstevel@tonic-gate 			break;
3253*7c478bd9Sstevel@tonic-gate 
3254*7c478bd9Sstevel@tonic-gate 		  case SMFIR_REJECT:
3255*7c478bd9Sstevel@tonic-gate 			milteraccept = false;
3256*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
3257*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
3258*7c478bd9Sstevel@tonic-gate 					  "Milter: data, reject=554 5.7.1 Command rejected");
3259*7c478bd9Sstevel@tonic-gate 			usrerr("554 5.7.1 Command rejected");
3260*7c478bd9Sstevel@tonic-gate 			break;
3261*7c478bd9Sstevel@tonic-gate 
3262*7c478bd9Sstevel@tonic-gate 		  case SMFIR_DISCARD:
3263*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
3264*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
3265*7c478bd9Sstevel@tonic-gate 					  "Milter: data, discard");
3266*7c478bd9Sstevel@tonic-gate 			milteraccept = false;
3267*7c478bd9Sstevel@tonic-gate 			e->e_flags |= EF_DISCARD;
3268*7c478bd9Sstevel@tonic-gate 			break;
3269*7c478bd9Sstevel@tonic-gate 
3270*7c478bd9Sstevel@tonic-gate 		  case SMFIR_TEMPFAIL:
3271*7c478bd9Sstevel@tonic-gate 			if (MilterLogLevel > 3)
3272*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, e->e_id,
3273*7c478bd9Sstevel@tonic-gate 					  "Milter: data, reject=%s",
3274*7c478bd9Sstevel@tonic-gate 					  MSG_TEMPFAIL);
3275*7c478bd9Sstevel@tonic-gate 			milteraccept = false;
3276*7c478bd9Sstevel@tonic-gate 			usrerr(MSG_TEMPFAIL);
3277*7c478bd9Sstevel@tonic-gate 			break;
3278*7c478bd9Sstevel@tonic-gate 		}
3279*7c478bd9Sstevel@tonic-gate 		if (response != NULL)
3280*7c478bd9Sstevel@tonic-gate 			sm_free(response);
3281*7c478bd9Sstevel@tonic-gate 	}
3282*7c478bd9Sstevel@tonic-gate 
3283*7c478bd9Sstevel@tonic-gate 	/* Milter may have changed message size */
3284*7c478bd9Sstevel@tonic-gate 	(void) sm_snprintf(buf, sizeof buf, "%ld", e->e_msgsize);
3285*7c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), buf);
3286*7c478bd9Sstevel@tonic-gate 
3287*7c478bd9Sstevel@tonic-gate 	/* abort message filters that didn't get the body & log msg is OK */
3288*7c478bd9Sstevel@tonic-gate 	if (smtp->sm_milterlist && smtp->sm_milterize)
3289*7c478bd9Sstevel@tonic-gate 	{
3290*7c478bd9Sstevel@tonic-gate 		milter_abort(e);
3291*7c478bd9Sstevel@tonic-gate 		if (milteraccept && MilterLogLevel > 9)
3292*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id, "Milter accept: message");
3293*7c478bd9Sstevel@tonic-gate 	}
3294*7c478bd9Sstevel@tonic-gate 
3295*7c478bd9Sstevel@tonic-gate 	/*
3296*7c478bd9Sstevel@tonic-gate 	**  If SuperSafe is SAFE_REALLY_POSTMILTER, and we don't have milter or
3297*7c478bd9Sstevel@tonic-gate 	**  milter accepted message, sync it now
3298*7c478bd9Sstevel@tonic-gate 	**
3299*7c478bd9Sstevel@tonic-gate 	**  XXX This is almost a copy of the code in collect(): put it into
3300*7c478bd9Sstevel@tonic-gate 	**	a function that is called from both places?
3301*7c478bd9Sstevel@tonic-gate 	*/
3302*7c478bd9Sstevel@tonic-gate 
3303*7c478bd9Sstevel@tonic-gate 	if (milteraccept && SuperSafe == SAFE_REALLY_POSTMILTER)
3304*7c478bd9Sstevel@tonic-gate 	{
3305*7c478bd9Sstevel@tonic-gate 		int afd;
3306*7c478bd9Sstevel@tonic-gate 		SM_FILE_T *volatile df;
3307*7c478bd9Sstevel@tonic-gate 		char *dfname;
3308*7c478bd9Sstevel@tonic-gate 
3309*7c478bd9Sstevel@tonic-gate 		df = e->e_dfp;
3310*7c478bd9Sstevel@tonic-gate 		dfname = queuename(e, DATAFL_LETTER);
3311*7c478bd9Sstevel@tonic-gate 		if (sm_io_setinfo(df, SM_BF_COMMIT, NULL) < 0
3312*7c478bd9Sstevel@tonic-gate 		    && errno != EINVAL)
3313*7c478bd9Sstevel@tonic-gate 		{
3314*7c478bd9Sstevel@tonic-gate 			int save_errno;
3315*7c478bd9Sstevel@tonic-gate 
3316*7c478bd9Sstevel@tonic-gate 			save_errno = errno;
3317*7c478bd9Sstevel@tonic-gate 			if (save_errno == EEXIST)
3318*7c478bd9Sstevel@tonic-gate 			{
3319*7c478bd9Sstevel@tonic-gate 				struct stat st;
3320*7c478bd9Sstevel@tonic-gate 				int dfd;
3321*7c478bd9Sstevel@tonic-gate 
3322*7c478bd9Sstevel@tonic-gate 				if (stat(dfname, &st) < 0)
3323*7c478bd9Sstevel@tonic-gate 					st.st_size = -1;
3324*7c478bd9Sstevel@tonic-gate 				errno = EEXIST;
3325*7c478bd9Sstevel@tonic-gate 				syserr("@collect: bfcommit(%s): already on disk, size=%ld",
3326*7c478bd9Sstevel@tonic-gate 				       dfname, (long) st.st_size);
3327*7c478bd9Sstevel@tonic-gate 				dfd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL);
3328*7c478bd9Sstevel@tonic-gate 				if (dfd >= 0)
3329*7c478bd9Sstevel@tonic-gate 					dumpfd(dfd, true, true);
3330*7c478bd9Sstevel@tonic-gate 			}
3331*7c478bd9Sstevel@tonic-gate 			errno = save_errno;
3332*7c478bd9Sstevel@tonic-gate 			dferror(df, "bfcommit", e);
3333*7c478bd9Sstevel@tonic-gate 			flush_errors(true);
3334*7c478bd9Sstevel@tonic-gate 			finis(save_errno != EEXIST, true, ExitStat);
3335*7c478bd9Sstevel@tonic-gate 		}
3336*7c478bd9Sstevel@tonic-gate 		else if ((afd = sm_io_getinfo(df, SM_IO_WHAT_FD, NULL)) < 0)
3337*7c478bd9Sstevel@tonic-gate 		{
3338*7c478bd9Sstevel@tonic-gate 			dferror(df, "sm_io_getinfo", e);
3339*7c478bd9Sstevel@tonic-gate 			flush_errors(true);
3340*7c478bd9Sstevel@tonic-gate 			finis(true, true, ExitStat);
3341*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3342*7c478bd9Sstevel@tonic-gate 		}
3343*7c478bd9Sstevel@tonic-gate 		else if (fsync(afd) < 0)
3344*7c478bd9Sstevel@tonic-gate 		{
3345*7c478bd9Sstevel@tonic-gate 			dferror(df, "fsync", e);
3346*7c478bd9Sstevel@tonic-gate 			flush_errors(true);
3347*7c478bd9Sstevel@tonic-gate 			finis(true, true, ExitStat);
3348*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3349*7c478bd9Sstevel@tonic-gate 		}
3350*7c478bd9Sstevel@tonic-gate 		else if (sm_io_close(df, SM_TIME_DEFAULT) < 0)
3351*7c478bd9Sstevel@tonic-gate 		{
3352*7c478bd9Sstevel@tonic-gate 			dferror(df, "sm_io_close", e);
3353*7c478bd9Sstevel@tonic-gate 			flush_errors(true);
3354*7c478bd9Sstevel@tonic-gate 			finis(true, true, ExitStat);
3355*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3356*7c478bd9Sstevel@tonic-gate 		}
3357*7c478bd9Sstevel@tonic-gate 
3358*7c478bd9Sstevel@tonic-gate 		/* Now reopen the df file */
3359*7c478bd9Sstevel@tonic-gate 		e->e_dfp = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, dfname,
3360*7c478bd9Sstevel@tonic-gate 					SM_IO_RDONLY, NULL);
3361*7c478bd9Sstevel@tonic-gate 		if (e->e_dfp == NULL)
3362*7c478bd9Sstevel@tonic-gate 		{
3363*7c478bd9Sstevel@tonic-gate 			/* we haven't acked receipt yet, so just chuck this */
3364*7c478bd9Sstevel@tonic-gate 			syserr("@Cannot reopen %s", dfname);
3365*7c478bd9Sstevel@tonic-gate 			finis(true, true, ExitStat);
3366*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3367*7c478bd9Sstevel@tonic-gate 		}
3368*7c478bd9Sstevel@tonic-gate 	}
3369*7c478bd9Sstevel@tonic-gate #endif /* MILTER */
3370*7c478bd9Sstevel@tonic-gate 
3371*7c478bd9Sstevel@tonic-gate 	/* Check if quarantining stats should be updated */
3372*7c478bd9Sstevel@tonic-gate 	if (e->e_quarmsg != NULL)
3373*7c478bd9Sstevel@tonic-gate 		markstats(e, NULL, STATS_QUARANTINE);
3374*7c478bd9Sstevel@tonic-gate 
3375*7c478bd9Sstevel@tonic-gate 	/*
3376*7c478bd9Sstevel@tonic-gate 	**  If a header/body check (header checks or milter)
3377*7c478bd9Sstevel@tonic-gate 	**  set EF_DISCARD, don't queueup the message --
3378*7c478bd9Sstevel@tonic-gate 	**  that would lose the EF_DISCARD bit and deliver
3379*7c478bd9Sstevel@tonic-gate 	**  the message.
3380*7c478bd9Sstevel@tonic-gate 	*/
3381*7c478bd9Sstevel@tonic-gate 
3382*7c478bd9Sstevel@tonic-gate 	if (bitset(EF_DISCARD, e->e_flags))
3383*7c478bd9Sstevel@tonic-gate 		doublequeue = false;
3384*7c478bd9Sstevel@tonic-gate 
3385*7c478bd9Sstevel@tonic-gate 	aborting = Errors > 0;
3386*7c478bd9Sstevel@tonic-gate 	if (!(aborting || bitset(EF_DISCARD, e->e_flags)) &&
3387*7c478bd9Sstevel@tonic-gate 	    (QueueMode == QM_QUARANTINE || e->e_quarmsg == NULL) &&
3388*7c478bd9Sstevel@tonic-gate 	    !split_by_recipient(e))
3389*7c478bd9Sstevel@tonic-gate 		aborting = bitset(EF_FATALERRS, e->e_flags);
3390*7c478bd9Sstevel@tonic-gate 
3391*7c478bd9Sstevel@tonic-gate 	if (aborting)
3392*7c478bd9Sstevel@tonic-gate 	{
3393*7c478bd9Sstevel@tonic-gate 		/* Log who the mail would have gone to */
3394*7c478bd9Sstevel@tonic-gate 		logundelrcpts(e, e->e_message, 8, false);
3395*7c478bd9Sstevel@tonic-gate 		flush_errors(true);
3396*7c478bd9Sstevel@tonic-gate 		buffer_errors();
3397*7c478bd9Sstevel@tonic-gate 		goto abortmessage;
3398*7c478bd9Sstevel@tonic-gate 	}
3399*7c478bd9Sstevel@tonic-gate 
3400*7c478bd9Sstevel@tonic-gate 	/* from now on, we have to operate silently */
3401*7c478bd9Sstevel@tonic-gate 	buffer_errors();
3402*7c478bd9Sstevel@tonic-gate 
3403*7c478bd9Sstevel@tonic-gate #if 0
3404*7c478bd9Sstevel@tonic-gate 	/*
3405*7c478bd9Sstevel@tonic-gate 	**  Clear message, it may contain an error from the SMTP dialogue.
3406*7c478bd9Sstevel@tonic-gate 	**  This error must not show up in the queue.
3407*7c478bd9Sstevel@tonic-gate 	**	Some error message should show up, e.g., alias database
3408*7c478bd9Sstevel@tonic-gate 	**	not available, but others shouldn't, e.g., from check_rcpt.
3409*7c478bd9Sstevel@tonic-gate 	*/
3410*7c478bd9Sstevel@tonic-gate 
3411*7c478bd9Sstevel@tonic-gate 	e->e_message = NULL;
3412*7c478bd9Sstevel@tonic-gate #endif /* 0 */
3413*7c478bd9Sstevel@tonic-gate 
3414*7c478bd9Sstevel@tonic-gate 	/*
3415*7c478bd9Sstevel@tonic-gate 	**  Arrange to send to everyone.
3416*7c478bd9Sstevel@tonic-gate 	**	If sending to multiple people, mail back
3417*7c478bd9Sstevel@tonic-gate 	**		errors rather than reporting directly.
3418*7c478bd9Sstevel@tonic-gate 	**	In any case, don't mail back errors for
3419*7c478bd9Sstevel@tonic-gate 	**		anything that has happened up to
3420*7c478bd9Sstevel@tonic-gate 	**		now (the other end will do this).
3421*7c478bd9Sstevel@tonic-gate 	**	Truncate our transcript -- the mail has gotten
3422*7c478bd9Sstevel@tonic-gate 	**		to us successfully, and if we have
3423*7c478bd9Sstevel@tonic-gate 	**		to mail this back, it will be easier
3424*7c478bd9Sstevel@tonic-gate 	**		on the reader.
3425*7c478bd9Sstevel@tonic-gate 	**	Then send to everyone.
3426*7c478bd9Sstevel@tonic-gate 	**	Finally give a reply code.  If an error has
3427*7c478bd9Sstevel@tonic-gate 	**		already been given, don't mail a
3428*7c478bd9Sstevel@tonic-gate 	**		message back.
3429*7c478bd9Sstevel@tonic-gate 	**	We goose error returns by clearing error bit.
3430*7c478bd9Sstevel@tonic-gate 	*/
3431*7c478bd9Sstevel@tonic-gate 
3432*7c478bd9Sstevel@tonic-gate 	SmtpPhase = "delivery";
3433*7c478bd9Sstevel@tonic-gate 	(void) sm_io_setinfo(e->e_xfp, SM_BF_TRUNCATE, NULL);
3434*7c478bd9Sstevel@tonic-gate 	id = e->e_id;
3435*7c478bd9Sstevel@tonic-gate 
3436*7c478bd9Sstevel@tonic-gate #if NAMED_BIND
3437*7c478bd9Sstevel@tonic-gate 	_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
3438*7c478bd9Sstevel@tonic-gate 	_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
3439*7c478bd9Sstevel@tonic-gate #endif /* NAMED_BIND */
3440*7c478bd9Sstevel@tonic-gate 
3441*7c478bd9Sstevel@tonic-gate 	for (ee = e; ee != NULL; ee = ee->e_sibling)
3442*7c478bd9Sstevel@tonic-gate 	{
3443*7c478bd9Sstevel@tonic-gate 		/* make sure we actually do delivery */
3444*7c478bd9Sstevel@tonic-gate 		ee->e_flags &= ~EF_CLRQUEUE;
3445*7c478bd9Sstevel@tonic-gate 
3446*7c478bd9Sstevel@tonic-gate 		/* from now on, operate silently */
3447*7c478bd9Sstevel@tonic-gate 		ee->e_errormode = EM_MAIL;
3448*7c478bd9Sstevel@tonic-gate 
3449*7c478bd9Sstevel@tonic-gate 		if (doublequeue)
3450*7c478bd9Sstevel@tonic-gate 		{
3451*7c478bd9Sstevel@tonic-gate 			/* make sure it is in the queue */
3452*7c478bd9Sstevel@tonic-gate 			queueup(ee, false, true);
3453*7c478bd9Sstevel@tonic-gate 		}
3454*7c478bd9Sstevel@tonic-gate 		else
3455*7c478bd9Sstevel@tonic-gate 		{
3456*7c478bd9Sstevel@tonic-gate 			/* send to all recipients */
3457*7c478bd9Sstevel@tonic-gate 			sendall(ee, SM_DEFAULT);
3458*7c478bd9Sstevel@tonic-gate 		}
3459*7c478bd9Sstevel@tonic-gate 		ee->e_to = NULL;
3460*7c478bd9Sstevel@tonic-gate 	}
3461*7c478bd9Sstevel@tonic-gate 
3462*7c478bd9Sstevel@tonic-gate 	/* put back id for SMTP logging in putoutmsg() */
3463*7c478bd9Sstevel@tonic-gate 	oldid = CurEnv->e_id;
3464*7c478bd9Sstevel@tonic-gate 	CurEnv->e_id = id;
3465*7c478bd9Sstevel@tonic-gate 
3466*7c478bd9Sstevel@tonic-gate 	/* issue success message */
3467*7c478bd9Sstevel@tonic-gate 	message("250 2.0.0 %s Message accepted for delivery", id);
3468*7c478bd9Sstevel@tonic-gate 	CurEnv->e_id = oldid;
3469*7c478bd9Sstevel@tonic-gate 
3470*7c478bd9Sstevel@tonic-gate 	/* if we just queued, poke it */
3471*7c478bd9Sstevel@tonic-gate 	if (doublequeue)
3472*7c478bd9Sstevel@tonic-gate 	{
3473*7c478bd9Sstevel@tonic-gate 		bool anything_to_send = false;
3474*7c478bd9Sstevel@tonic-gate 
3475*7c478bd9Sstevel@tonic-gate 		sm_getla();
3476*7c478bd9Sstevel@tonic-gate 		for (ee = e; ee != NULL; ee = ee->e_sibling)
3477*7c478bd9Sstevel@tonic-gate 		{
3478*7c478bd9Sstevel@tonic-gate 			if (WILL_BE_QUEUED(ee->e_sendmode))
3479*7c478bd9Sstevel@tonic-gate 				continue;
3480*7c478bd9Sstevel@tonic-gate 			if (shouldqueue(ee->e_msgpriority, ee->e_ctime))
3481*7c478bd9Sstevel@tonic-gate 			{
3482*7c478bd9Sstevel@tonic-gate 				ee->e_sendmode = SM_QUEUE;
3483*7c478bd9Sstevel@tonic-gate 				continue;
3484*7c478bd9Sstevel@tonic-gate 			}
3485*7c478bd9Sstevel@tonic-gate 			else if (QueueMode != QM_QUARANTINE &&
3486*7c478bd9Sstevel@tonic-gate 				 ee->e_quarmsg != NULL)
3487*7c478bd9Sstevel@tonic-gate 			{
3488*7c478bd9Sstevel@tonic-gate 				ee->e_sendmode = SM_QUEUE;
3489*7c478bd9Sstevel@tonic-gate 				continue;
3490*7c478bd9Sstevel@tonic-gate 			}
3491*7c478bd9Sstevel@tonic-gate 			anything_to_send = true;
3492*7c478bd9Sstevel@tonic-gate 
3493*7c478bd9Sstevel@tonic-gate 			/* close all the queue files */
3494*7c478bd9Sstevel@tonic-gate 			closexscript(ee);
3495*7c478bd9Sstevel@tonic-gate 			if (ee->e_dfp != NULL)
3496*7c478bd9Sstevel@tonic-gate 			{
3497*7c478bd9Sstevel@tonic-gate 				(void) sm_io_close(ee->e_dfp, SM_TIME_DEFAULT);
3498*7c478bd9Sstevel@tonic-gate 				ee->e_dfp = NULL;
3499*7c478bd9Sstevel@tonic-gate 			}
3500*7c478bd9Sstevel@tonic-gate 			unlockqueue(ee);
3501*7c478bd9Sstevel@tonic-gate 		}
3502*7c478bd9Sstevel@tonic-gate 		if (anything_to_send)
3503*7c478bd9Sstevel@tonic-gate 		{
3504*7c478bd9Sstevel@tonic-gate #if PIPELINING
3505*7c478bd9Sstevel@tonic-gate 			/*
3506*7c478bd9Sstevel@tonic-gate 			**  XXX if we don't do this, we get 250 twice
3507*7c478bd9Sstevel@tonic-gate 			**	because it is also flushed in the child.
3508*7c478bd9Sstevel@tonic-gate 			*/
3509*7c478bd9Sstevel@tonic-gate 
3510*7c478bd9Sstevel@tonic-gate 			(void) sm_io_flush(OutChannel, SM_TIME_DEFAULT);
3511*7c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
3512*7c478bd9Sstevel@tonic-gate 			(void) doworklist(e, true, true);
3513*7c478bd9Sstevel@tonic-gate 		}
3514*7c478bd9Sstevel@tonic-gate 	}
3515*7c478bd9Sstevel@tonic-gate 
3516*7c478bd9Sstevel@tonic-gate   abortmessage:
3517*7c478bd9Sstevel@tonic-gate 	if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
3518*7c478bd9Sstevel@tonic-gate 		logsender(e, NULL);
3519*7c478bd9Sstevel@tonic-gate 	e->e_flags &= ~EF_LOGSENDER;
3520*7c478bd9Sstevel@tonic-gate 
3521*7c478bd9Sstevel@tonic-gate 	/* clean up a bit */
3522*7c478bd9Sstevel@tonic-gate 	smtp->sm_gotmail = false;
3523*7c478bd9Sstevel@tonic-gate 
3524*7c478bd9Sstevel@tonic-gate 	/*
3525*7c478bd9Sstevel@tonic-gate 	**  Call dropenvelope if and only if the envelope is *not*
3526*7c478bd9Sstevel@tonic-gate 	**  being processed by the child process forked by doworklist().
3527*7c478bd9Sstevel@tonic-gate 	*/
3528*7c478bd9Sstevel@tonic-gate 
3529*7c478bd9Sstevel@tonic-gate 	if (aborting || bitset(EF_DISCARD, e->e_flags))
3530*7c478bd9Sstevel@tonic-gate 		dropenvelope(e, true, false);
3531*7c478bd9Sstevel@tonic-gate 	else
3532*7c478bd9Sstevel@tonic-gate 	{
3533*7c478bd9Sstevel@tonic-gate 		for (ee = e; ee != NULL; ee = ee->e_sibling)
3534*7c478bd9Sstevel@tonic-gate 		{
3535*7c478bd9Sstevel@tonic-gate 			if (!doublequeue &&
3536*7c478bd9Sstevel@tonic-gate 			    QueueMode != QM_QUARANTINE &&
3537*7c478bd9Sstevel@tonic-gate 			    ee->e_quarmsg != NULL)
3538*7c478bd9Sstevel@tonic-gate 			{
3539*7c478bd9Sstevel@tonic-gate 				dropenvelope(ee, true, false);
3540*7c478bd9Sstevel@tonic-gate 				continue;
3541*7c478bd9Sstevel@tonic-gate 			}
3542*7c478bd9Sstevel@tonic-gate 			if (WILL_BE_QUEUED(ee->e_sendmode))
3543*7c478bd9Sstevel@tonic-gate 				dropenvelope(ee, true, false);
3544*7c478bd9Sstevel@tonic-gate 		}
3545*7c478bd9Sstevel@tonic-gate 	}
3546*7c478bd9Sstevel@tonic-gate 	sm_rpool_free(e->e_rpool);
3547*7c478bd9Sstevel@tonic-gate 
3548*7c478bd9Sstevel@tonic-gate 	/*
3549*7c478bd9Sstevel@tonic-gate 	**  At this point, e == &MainEnvelope, but if we did splitting,
3550*7c478bd9Sstevel@tonic-gate 	**  then CurEnv may point to an envelope structure that was just
3551*7c478bd9Sstevel@tonic-gate 	**  freed with the rpool.  So reset CurEnv *before* calling
3552*7c478bd9Sstevel@tonic-gate 	**  newenvelope.
3553*7c478bd9Sstevel@tonic-gate 	*/
3554*7c478bd9Sstevel@tonic-gate 
3555*7c478bd9Sstevel@tonic-gate 	CurEnv = e;
3556*7c478bd9Sstevel@tonic-gate 	newenvelope(e, e, sm_rpool_new_x(NULL));
3557*7c478bd9Sstevel@tonic-gate 	e->e_flags = BlankEnvelope.e_flags;
3558*7c478bd9Sstevel@tonic-gate 
3559*7c478bd9Sstevel@tonic-gate 	/* restore connection quarantining */
3560*7c478bd9Sstevel@tonic-gate 	if (smtp->sm_quarmsg == NULL)
3561*7c478bd9Sstevel@tonic-gate 	{
3562*7c478bd9Sstevel@tonic-gate 		e->e_quarmsg = NULL;
3563*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM, macid("{quarantine}"), "");
3564*7c478bd9Sstevel@tonic-gate 	}
3565*7c478bd9Sstevel@tonic-gate 	else
3566*7c478bd9Sstevel@tonic-gate 	{
3567*7c478bd9Sstevel@tonic-gate 		e->e_quarmsg = sm_rpool_strdup_x(e->e_rpool, smtp->sm_quarmsg);
3568*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM,
3569*7c478bd9Sstevel@tonic-gate 			  macid("{quarantine}"), e->e_quarmsg);
3570*7c478bd9Sstevel@tonic-gate 	}
3571*7c478bd9Sstevel@tonic-gate 	return true;
3572*7c478bd9Sstevel@tonic-gate }
3573*7c478bd9Sstevel@tonic-gate /*
3574*7c478bd9Sstevel@tonic-gate **  LOGUNDELRCPTS -- log undelivered (or all) recipients.
3575*7c478bd9Sstevel@tonic-gate **
3576*7c478bd9Sstevel@tonic-gate **	Parameters:
3577*7c478bd9Sstevel@tonic-gate **		e -- envelope.
3578*7c478bd9Sstevel@tonic-gate **		msg -- message for Stat=
3579*7c478bd9Sstevel@tonic-gate **		level -- log level.
3580*7c478bd9Sstevel@tonic-gate **		all -- log all recipients.
3581*7c478bd9Sstevel@tonic-gate **
3582*7c478bd9Sstevel@tonic-gate **	Returns:
3583*7c478bd9Sstevel@tonic-gate **		none.
3584*7c478bd9Sstevel@tonic-gate **
3585*7c478bd9Sstevel@tonic-gate **	Side Effects:
3586*7c478bd9Sstevel@tonic-gate **		logs undelivered (or all) recipients
3587*7c478bd9Sstevel@tonic-gate */
3588*7c478bd9Sstevel@tonic-gate 
3589*7c478bd9Sstevel@tonic-gate void
3590*7c478bd9Sstevel@tonic-gate logundelrcpts(e, msg, level, all)
3591*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3592*7c478bd9Sstevel@tonic-gate 	char *msg;
3593*7c478bd9Sstevel@tonic-gate 	int level;
3594*7c478bd9Sstevel@tonic-gate 	bool all;
3595*7c478bd9Sstevel@tonic-gate {
3596*7c478bd9Sstevel@tonic-gate 	ADDRESS *a;
3597*7c478bd9Sstevel@tonic-gate 
3598*7c478bd9Sstevel@tonic-gate 	if (LogLevel <= level || msg == NULL || *msg == '\0')
3599*7c478bd9Sstevel@tonic-gate 		return;
3600*7c478bd9Sstevel@tonic-gate 
3601*7c478bd9Sstevel@tonic-gate 	/* Clear $h so relay= doesn't get mislogged by logdelivery() */
3602*7c478bd9Sstevel@tonic-gate 	macdefine(&e->e_macro, A_PERM, 'h', NULL);
3603*7c478bd9Sstevel@tonic-gate 
3604*7c478bd9Sstevel@tonic-gate 	/* Log who the mail would have gone to */
3605*7c478bd9Sstevel@tonic-gate 	for (a = e->e_sendqueue; a != NULL; a = a->q_next)
3606*7c478bd9Sstevel@tonic-gate 	{
3607*7c478bd9Sstevel@tonic-gate 		if (!QS_IS_UNDELIVERED(a->q_state) && !all)
3608*7c478bd9Sstevel@tonic-gate 			continue;
3609*7c478bd9Sstevel@tonic-gate 		e->e_to = a->q_paddr;
3610*7c478bd9Sstevel@tonic-gate 		logdelivery(NULL, NULL, a->q_status, msg, NULL,
3611*7c478bd9Sstevel@tonic-gate 			    (time_t) 0, e);
3612*7c478bd9Sstevel@tonic-gate 	}
3613*7c478bd9Sstevel@tonic-gate 	e->e_to = NULL;
3614*7c478bd9Sstevel@tonic-gate }
3615*7c478bd9Sstevel@tonic-gate /*
3616*7c478bd9Sstevel@tonic-gate **  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
3617*7c478bd9Sstevel@tonic-gate **
3618*7c478bd9Sstevel@tonic-gate **	Parameters:
3619*7c478bd9Sstevel@tonic-gate **		pcounter -- pointer to a counter for this command.
3620*7c478bd9Sstevel@tonic-gate **		maxcount -- maximum value for this counter before we
3621*7c478bd9Sstevel@tonic-gate **			slow down.
3622*7c478bd9Sstevel@tonic-gate **		waitnow -- sleep now (in this routine)?
3623*7c478bd9Sstevel@tonic-gate **		cname -- command name for logging.
3624*7c478bd9Sstevel@tonic-gate **		e -- the current envelope.
3625*7c478bd9Sstevel@tonic-gate **
3626*7c478bd9Sstevel@tonic-gate **	Returns:
3627*7c478bd9Sstevel@tonic-gate **		time to wait,
3628*7c478bd9Sstevel@tonic-gate **		STOP_ATTACK if twice as many commands as allowed and
3629*7c478bd9Sstevel@tonic-gate **			MaxChildren > 0.
3630*7c478bd9Sstevel@tonic-gate **
3631*7c478bd9Sstevel@tonic-gate **	Side Effects:
3632*7c478bd9Sstevel@tonic-gate **		Slows down if we seem to be under attack.
3633*7c478bd9Sstevel@tonic-gate */
3634*7c478bd9Sstevel@tonic-gate 
3635*7c478bd9Sstevel@tonic-gate static time_t
3636*7c478bd9Sstevel@tonic-gate checksmtpattack(pcounter, maxcount, waitnow, cname, e)
3637*7c478bd9Sstevel@tonic-gate 	volatile unsigned int *pcounter;
3638*7c478bd9Sstevel@tonic-gate 	unsigned int maxcount;
3639*7c478bd9Sstevel@tonic-gate 	bool waitnow;
3640*7c478bd9Sstevel@tonic-gate 	char *cname;
3641*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3642*7c478bd9Sstevel@tonic-gate {
3643*7c478bd9Sstevel@tonic-gate 	if (maxcount <= 0)	/* no limit */
3644*7c478bd9Sstevel@tonic-gate 		return (time_t) 0;
3645*7c478bd9Sstevel@tonic-gate 
3646*7c478bd9Sstevel@tonic-gate 	if (++(*pcounter) >= maxcount)
3647*7c478bd9Sstevel@tonic-gate 	{
3648*7c478bd9Sstevel@tonic-gate 		unsigned int shift;
3649*7c478bd9Sstevel@tonic-gate 		time_t s;
3650*7c478bd9Sstevel@tonic-gate 
3651*7c478bd9Sstevel@tonic-gate 		if (*pcounter == maxcount && LogLevel > 5)
3652*7c478bd9Sstevel@tonic-gate 		{
3653*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, e->e_id,
3654*7c478bd9Sstevel@tonic-gate 				  "%s: possible SMTP attack: command=%.40s, count=%u",
3655*7c478bd9Sstevel@tonic-gate 				  CurSmtpClient, cname, *pcounter);
3656*7c478bd9Sstevel@tonic-gate 		}
3657*7c478bd9Sstevel@tonic-gate 		shift = *pcounter - maxcount;
3658*7c478bd9Sstevel@tonic-gate 		s = 1 << shift;
3659*7c478bd9Sstevel@tonic-gate 		if (shift > MAXSHIFT || s >= MAXTIMEOUT || s <= 0)
3660*7c478bd9Sstevel@tonic-gate 			s = MAXTIMEOUT;
3661*7c478bd9Sstevel@tonic-gate 
3662*7c478bd9Sstevel@tonic-gate #define IS_ATTACK(s)	((MaxChildren > 0 && *pcounter >= maxcount * 2)	\
3663*7c478bd9Sstevel@tonic-gate 				? STOP_ATTACK : (time_t) s)
3664*7c478bd9Sstevel@tonic-gate 
3665*7c478bd9Sstevel@tonic-gate 		/* sleep at least 1 second before returning */
3666*7c478bd9Sstevel@tonic-gate 		(void) sleep(*pcounter / maxcount);
3667*7c478bd9Sstevel@tonic-gate 		s -= *pcounter / maxcount;
3668*7c478bd9Sstevel@tonic-gate 		if (s >= MAXTIMEOUT || s < 0)
3669*7c478bd9Sstevel@tonic-gate 			s = MAXTIMEOUT;
3670*7c478bd9Sstevel@tonic-gate 		if (waitnow && s > 0)
3671*7c478bd9Sstevel@tonic-gate 		{
3672*7c478bd9Sstevel@tonic-gate 			(void) sleep(s);
3673*7c478bd9Sstevel@tonic-gate 			return IS_ATTACK(0);
3674*7c478bd9Sstevel@tonic-gate 		}
3675*7c478bd9Sstevel@tonic-gate 		return IS_ATTACK(s);
3676*7c478bd9Sstevel@tonic-gate 	}
3677*7c478bd9Sstevel@tonic-gate 	return (time_t) 0;
3678*7c478bd9Sstevel@tonic-gate }
3679*7c478bd9Sstevel@tonic-gate /*
3680*7c478bd9Sstevel@tonic-gate **  SETUP_SMTPD_IO -- setup I/O fd correctly for the SMTP server
3681*7c478bd9Sstevel@tonic-gate **
3682*7c478bd9Sstevel@tonic-gate **	Parameters:
3683*7c478bd9Sstevel@tonic-gate **		none.
3684*7c478bd9Sstevel@tonic-gate **
3685*7c478bd9Sstevel@tonic-gate **	Returns:
3686*7c478bd9Sstevel@tonic-gate **		nothing.
3687*7c478bd9Sstevel@tonic-gate **
3688*7c478bd9Sstevel@tonic-gate **	Side Effects:
3689*7c478bd9Sstevel@tonic-gate **		may change I/O fd.
3690*7c478bd9Sstevel@tonic-gate */
3691*7c478bd9Sstevel@tonic-gate 
3692*7c478bd9Sstevel@tonic-gate static void
3693*7c478bd9Sstevel@tonic-gate setup_smtpd_io()
3694*7c478bd9Sstevel@tonic-gate {
3695*7c478bd9Sstevel@tonic-gate 	int inchfd, outchfd, outfd;
3696*7c478bd9Sstevel@tonic-gate 
3697*7c478bd9Sstevel@tonic-gate 	inchfd = sm_io_getinfo(InChannel, SM_IO_WHAT_FD, NULL);
3698*7c478bd9Sstevel@tonic-gate 	outchfd  = sm_io_getinfo(OutChannel, SM_IO_WHAT_FD, NULL);
3699*7c478bd9Sstevel@tonic-gate 	outfd = sm_io_getinfo(smioout, SM_IO_WHAT_FD, NULL);
3700*7c478bd9Sstevel@tonic-gate 	if (outchfd != outfd)
3701*7c478bd9Sstevel@tonic-gate 	{
3702*7c478bd9Sstevel@tonic-gate 		/* arrange for debugging output to go to remote host */
3703*7c478bd9Sstevel@tonic-gate 		(void) dup2(outchfd, outfd);
3704*7c478bd9Sstevel@tonic-gate 	}
3705*7c478bd9Sstevel@tonic-gate 
3706*7c478bd9Sstevel@tonic-gate 	/*
3707*7c478bd9Sstevel@tonic-gate 	**  if InChannel and OutChannel are stdin/stdout
3708*7c478bd9Sstevel@tonic-gate 	**  and connected to ttys
3709*7c478bd9Sstevel@tonic-gate 	**  and fcntl(STDIN, F_SETFL, O_NONBLOCKING) also changes STDOUT,
3710*7c478bd9Sstevel@tonic-gate 	**  then "chain" them together.
3711*7c478bd9Sstevel@tonic-gate 	*/
3712*7c478bd9Sstevel@tonic-gate 
3713*7c478bd9Sstevel@tonic-gate 	if (inchfd == STDIN_FILENO && outchfd == STDOUT_FILENO &&
3714*7c478bd9Sstevel@tonic-gate 	    isatty(inchfd) && isatty(outchfd))
3715*7c478bd9Sstevel@tonic-gate 	{
3716*7c478bd9Sstevel@tonic-gate 		int inmode, outmode;
3717*7c478bd9Sstevel@tonic-gate 
3718*7c478bd9Sstevel@tonic-gate 		inmode = fcntl(inchfd, F_GETFL, 0);
3719*7c478bd9Sstevel@tonic-gate 		if (inmode == -1)
3720*7c478bd9Sstevel@tonic-gate 		{
3721*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 11)
3722*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
3723*7c478bd9Sstevel@tonic-gate 					"fcntl(inchfd, F_GETFL) failed: %s",
3724*7c478bd9Sstevel@tonic-gate 					sm_errstring(errno));
3725*7c478bd9Sstevel@tonic-gate 			return;
3726*7c478bd9Sstevel@tonic-gate 		}
3727*7c478bd9Sstevel@tonic-gate 		outmode = fcntl(outchfd, F_GETFL, 0);
3728*7c478bd9Sstevel@tonic-gate 		if (outmode == -1)
3729*7c478bd9Sstevel@tonic-gate 		{
3730*7c478bd9Sstevel@tonic-gate 			if (LogLevel > 11)
3731*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
3732*7c478bd9Sstevel@tonic-gate 					"fcntl(outchfd, F_GETFL) failed: %s",
3733*7c478bd9Sstevel@tonic-gate 					sm_errstring(errno));
3734*7c478bd9Sstevel@tonic-gate 			return;
3735*7c478bd9Sstevel@tonic-gate 		}
3736*7c478bd9Sstevel@tonic-gate 		if (bitset(O_NONBLOCK, inmode) ||
3737*7c478bd9Sstevel@tonic-gate 		    bitset(O_NONBLOCK, outmode) ||
3738*7c478bd9Sstevel@tonic-gate 		    fcntl(inchfd, F_SETFL, inmode | O_NONBLOCK) == -1)
3739*7c478bd9Sstevel@tonic-gate 			return;
3740*7c478bd9Sstevel@tonic-gate 		outmode = fcntl(outchfd, F_GETFL, 0);
3741*7c478bd9Sstevel@tonic-gate 		if (outmode != -1 && bitset(O_NONBLOCK, outmode))
3742*7c478bd9Sstevel@tonic-gate 		{
3743*7c478bd9Sstevel@tonic-gate 			/* changing InChannel also changes OutChannel */
3744*7c478bd9Sstevel@tonic-gate 			sm_io_automode(OutChannel, InChannel);
3745*7c478bd9Sstevel@tonic-gate 			if (tTd(97, 4) && LogLevel > 9)
3746*7c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_INFO, NOQID,
3747*7c478bd9Sstevel@tonic-gate 					  "set automode for I (%d)/O (%d) in SMTP server",
3748*7c478bd9Sstevel@tonic-gate 					  inchfd, outchfd);
3749*7c478bd9Sstevel@tonic-gate 		}
3750*7c478bd9Sstevel@tonic-gate 
3751*7c478bd9Sstevel@tonic-gate 		/* undo change of inchfd */
3752*7c478bd9Sstevel@tonic-gate 		(void) fcntl(inchfd, F_SETFL, inmode);
3753*7c478bd9Sstevel@tonic-gate 	}
3754*7c478bd9Sstevel@tonic-gate }
3755*7c478bd9Sstevel@tonic-gate /*
3756*7c478bd9Sstevel@tonic-gate **  SKIPWORD -- skip a fixed word.
3757*7c478bd9Sstevel@tonic-gate **
3758*7c478bd9Sstevel@tonic-gate **	Parameters:
3759*7c478bd9Sstevel@tonic-gate **		p -- place to start looking.
3760*7c478bd9Sstevel@tonic-gate **		w -- word to skip.
3761*7c478bd9Sstevel@tonic-gate **
3762*7c478bd9Sstevel@tonic-gate **	Returns:
3763*7c478bd9Sstevel@tonic-gate **		p following w.
3764*7c478bd9Sstevel@tonic-gate **		NULL on error.
3765*7c478bd9Sstevel@tonic-gate **
3766*7c478bd9Sstevel@tonic-gate **	Side Effects:
3767*7c478bd9Sstevel@tonic-gate **		clobbers the p data area.
3768*7c478bd9Sstevel@tonic-gate */
3769*7c478bd9Sstevel@tonic-gate 
3770*7c478bd9Sstevel@tonic-gate static char *
3771*7c478bd9Sstevel@tonic-gate skipword(p, w)
3772*7c478bd9Sstevel@tonic-gate 	register char *volatile p;
3773*7c478bd9Sstevel@tonic-gate 	char *w;
3774*7c478bd9Sstevel@tonic-gate {
3775*7c478bd9Sstevel@tonic-gate 	register char *q;
3776*7c478bd9Sstevel@tonic-gate 	char *firstp = p;
3777*7c478bd9Sstevel@tonic-gate 
3778*7c478bd9Sstevel@tonic-gate 	/* find beginning of word */
3779*7c478bd9Sstevel@tonic-gate 	SKIP_SPACE(p);
3780*7c478bd9Sstevel@tonic-gate 	q = p;
3781*7c478bd9Sstevel@tonic-gate 
3782*7c478bd9Sstevel@tonic-gate 	/* find end of word */
3783*7c478bd9Sstevel@tonic-gate 	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
3784*7c478bd9Sstevel@tonic-gate 		p++;
3785*7c478bd9Sstevel@tonic-gate 	while (isascii(*p) && isspace(*p))
3786*7c478bd9Sstevel@tonic-gate 		*p++ = '\0';
3787*7c478bd9Sstevel@tonic-gate 	if (*p != ':')
3788*7c478bd9Sstevel@tonic-gate 	{
3789*7c478bd9Sstevel@tonic-gate 	  syntax:
3790*7c478bd9Sstevel@tonic-gate 		usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
3791*7c478bd9Sstevel@tonic-gate 			shortenstring(firstp, MAXSHORTSTR));
3792*7c478bd9Sstevel@tonic-gate 		return NULL;
3793*7c478bd9Sstevel@tonic-gate 	}
3794*7c478bd9Sstevel@tonic-gate 	*p++ = '\0';
3795*7c478bd9Sstevel@tonic-gate 	SKIP_SPACE(p);
3796*7c478bd9Sstevel@tonic-gate 
3797*7c478bd9Sstevel@tonic-gate 	if (*p == '\0')
3798*7c478bd9Sstevel@tonic-gate 		goto syntax;
3799*7c478bd9Sstevel@tonic-gate 
3800*7c478bd9Sstevel@tonic-gate 	/* see if the input word matches desired word */
3801*7c478bd9Sstevel@tonic-gate 	if (sm_strcasecmp(q, w))
3802*7c478bd9Sstevel@tonic-gate 		goto syntax;
3803*7c478bd9Sstevel@tonic-gate 
3804*7c478bd9Sstevel@tonic-gate 	return p;
3805*7c478bd9Sstevel@tonic-gate }
3806*7c478bd9Sstevel@tonic-gate /*
3807*7c478bd9Sstevel@tonic-gate **  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
3808*7c478bd9Sstevel@tonic-gate **
3809*7c478bd9Sstevel@tonic-gate **	Parameters:
3810*7c478bd9Sstevel@tonic-gate **		kp -- the parameter key.
3811*7c478bd9Sstevel@tonic-gate **		vp -- the value of that parameter.
3812*7c478bd9Sstevel@tonic-gate **		e -- the envelope.
3813*7c478bd9Sstevel@tonic-gate **
3814*7c478bd9Sstevel@tonic-gate **	Returns:
3815*7c478bd9Sstevel@tonic-gate **		none.
3816*7c478bd9Sstevel@tonic-gate */
3817*7c478bd9Sstevel@tonic-gate 
3818*7c478bd9Sstevel@tonic-gate static void
3819*7c478bd9Sstevel@tonic-gate mail_esmtp_args(kp, vp, e)
3820*7c478bd9Sstevel@tonic-gate 	char *kp;
3821*7c478bd9Sstevel@tonic-gate 	char *vp;
3822*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
3823*7c478bd9Sstevel@tonic-gate {
3824*7c478bd9Sstevel@tonic-gate 	if (sm_strcasecmp(kp, "size") == 0)
3825*7c478bd9Sstevel@tonic-gate 	{
3826*7c478bd9Sstevel@tonic-gate 		if (vp == NULL)
3827*7c478bd9Sstevel@tonic-gate 		{
3828*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 SIZE requires a value");
3829*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3830*7c478bd9Sstevel@tonic-gate 		}
3831*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_TEMP, macid("{msg_size}"), vp);
3832*7c478bd9Sstevel@tonic-gate 		errno = 0;
3833*7c478bd9Sstevel@tonic-gate 		e->e_msgsize = strtol(vp, (char **) NULL, 10);
3834*7c478bd9Sstevel@tonic-gate 		if (e->e_msgsize == LONG_MAX && errno == ERANGE)
3835*7c478bd9Sstevel@tonic-gate 		{
3836*7c478bd9Sstevel@tonic-gate 			usrerr("552 5.2.3 Message size exceeds maximum value");
3837*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3838*7c478bd9Sstevel@tonic-gate 		}
3839*7c478bd9Sstevel@tonic-gate 		if (e->e_msgsize < 0)
3840*7c478bd9Sstevel@tonic-gate 		{
3841*7c478bd9Sstevel@tonic-gate 			usrerr("552 5.2.3 Message size invalid");
3842*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3843*7c478bd9Sstevel@tonic-gate 		}
3844*7c478bd9Sstevel@tonic-gate 	}
3845*7c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(kp, "body") == 0)
3846*7c478bd9Sstevel@tonic-gate 	{
3847*7c478bd9Sstevel@tonic-gate 		if (vp == NULL)
3848*7c478bd9Sstevel@tonic-gate 		{
3849*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 BODY requires a value");
3850*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3851*7c478bd9Sstevel@tonic-gate 		}
3852*7c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(vp, "8bitmime") == 0)
3853*7c478bd9Sstevel@tonic-gate 		{
3854*7c478bd9Sstevel@tonic-gate 			SevenBitInput = false;
3855*7c478bd9Sstevel@tonic-gate 		}
3856*7c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(vp, "7bit") == 0)
3857*7c478bd9Sstevel@tonic-gate 		{
3858*7c478bd9Sstevel@tonic-gate 			SevenBitInput = true;
3859*7c478bd9Sstevel@tonic-gate 		}
3860*7c478bd9Sstevel@tonic-gate 		else
3861*7c478bd9Sstevel@tonic-gate 		{
3862*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.4 Unknown BODY type %s", vp);
3863*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3864*7c478bd9Sstevel@tonic-gate 		}
3865*7c478bd9Sstevel@tonic-gate 		e->e_bodytype = sm_rpool_strdup_x(e->e_rpool, vp);
3866*7c478bd9Sstevel@tonic-gate 	}
3867*7c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(kp, "envid") == 0)
3868*7c478bd9Sstevel@tonic-gate 	{
3869*7c478bd9Sstevel@tonic-gate 		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
3870*7c478bd9Sstevel@tonic-gate 		{
3871*7c478bd9Sstevel@tonic-gate 			usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
3872*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3873*7c478bd9Sstevel@tonic-gate 		}
3874*7c478bd9Sstevel@tonic-gate 		if (vp == NULL)
3875*7c478bd9Sstevel@tonic-gate 		{
3876*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 ENVID requires a value");
3877*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3878*7c478bd9Sstevel@tonic-gate 		}
3879*7c478bd9Sstevel@tonic-gate 		if (!xtextok(vp))
3880*7c478bd9Sstevel@tonic-gate 		{
3881*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.4 Syntax error in ENVID parameter value");
3882*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3883*7c478bd9Sstevel@tonic-gate 		}
3884*7c478bd9Sstevel@tonic-gate 		if (e->e_envid != NULL)
3885*7c478bd9Sstevel@tonic-gate 		{
3886*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.0 Duplicate ENVID parameter");
3887*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3888*7c478bd9Sstevel@tonic-gate 		}
3889*7c478bd9Sstevel@tonic-gate 		e->e_envid = sm_rpool_strdup_x(e->e_rpool, vp);
3890*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_PERM,
3891*7c478bd9Sstevel@tonic-gate 			macid("{dsn_envid}"), e->e_envid);
3892*7c478bd9Sstevel@tonic-gate 	}
3893*7c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(kp, "ret") == 0)
3894*7c478bd9Sstevel@tonic-gate 	{
3895*7c478bd9Sstevel@tonic-gate 		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
3896*7c478bd9Sstevel@tonic-gate 		{
3897*7c478bd9Sstevel@tonic-gate 			usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
3898*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3899*7c478bd9Sstevel@tonic-gate 		}
3900*7c478bd9Sstevel@tonic-gate 		if (vp == NULL)
3901*7c478bd9Sstevel@tonic-gate 		{
3902*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 RET requires a value");
3903*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3904*7c478bd9Sstevel@tonic-gate 		}
3905*7c478bd9Sstevel@tonic-gate 		if (bitset(EF_RET_PARAM, e->e_flags))
3906*7c478bd9Sstevel@tonic-gate 		{
3907*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.0 Duplicate RET parameter");
3908*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3909*7c478bd9Sstevel@tonic-gate 		}
3910*7c478bd9Sstevel@tonic-gate 		e->e_flags |= EF_RET_PARAM;
3911*7c478bd9Sstevel@tonic-gate 		if (sm_strcasecmp(vp, "hdrs") == 0)
3912*7c478bd9Sstevel@tonic-gate 			e->e_flags |= EF_NO_BODY_RETN;
3913*7c478bd9Sstevel@tonic-gate 		else if (sm_strcasecmp(vp, "full") != 0)
3914*7c478bd9Sstevel@tonic-gate 		{
3915*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
3916*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3917*7c478bd9Sstevel@tonic-gate 		}
3918*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_TEMP, macid("{dsn_ret}"), vp);
3919*7c478bd9Sstevel@tonic-gate 	}
3920*7c478bd9Sstevel@tonic-gate #if SASL
3921*7c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(kp, "auth") == 0)
3922*7c478bd9Sstevel@tonic-gate 	{
3923*7c478bd9Sstevel@tonic-gate 		int len;
3924*7c478bd9Sstevel@tonic-gate 		char *q;
3925*7c478bd9Sstevel@tonic-gate 		char *auth_param;	/* the value of the AUTH=x */
3926*7c478bd9Sstevel@tonic-gate 		bool saveQuickAbort = QuickAbort;
3927*7c478bd9Sstevel@tonic-gate 		bool saveSuprErrs = SuprErrs;
3928*7c478bd9Sstevel@tonic-gate 		bool saveExitStat = ExitStat;
3929*7c478bd9Sstevel@tonic-gate 
3930*7c478bd9Sstevel@tonic-gate 		if (vp == NULL)
3931*7c478bd9Sstevel@tonic-gate 		{
3932*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 AUTH= requires a value");
3933*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3934*7c478bd9Sstevel@tonic-gate 		}
3935*7c478bd9Sstevel@tonic-gate 		if (e->e_auth_param != NULL)
3936*7c478bd9Sstevel@tonic-gate 		{
3937*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.0 Duplicate AUTH parameter");
3938*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3939*7c478bd9Sstevel@tonic-gate 		}
3940*7c478bd9Sstevel@tonic-gate 		if ((q = strchr(vp, ' ')) != NULL)
3941*7c478bd9Sstevel@tonic-gate 			len = q - vp + 1;
3942*7c478bd9Sstevel@tonic-gate 		else
3943*7c478bd9Sstevel@tonic-gate 			len = strlen(vp) + 1;
3944*7c478bd9Sstevel@tonic-gate 		auth_param = xalloc(len);
3945*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(auth_param, vp, len);
3946*7c478bd9Sstevel@tonic-gate 		if (!xtextok(auth_param))
3947*7c478bd9Sstevel@tonic-gate 		{
3948*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.4 Syntax error in AUTH parameter value");
3949*7c478bd9Sstevel@tonic-gate 			/* just a warning? */
3950*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
3951*7c478bd9Sstevel@tonic-gate 		}
3952*7c478bd9Sstevel@tonic-gate 
3953*7c478bd9Sstevel@tonic-gate 		/* XXX define this always or only if trusted? */
3954*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_TEMP, macid("{auth_author}"),
3955*7c478bd9Sstevel@tonic-gate 			  auth_param);
3956*7c478bd9Sstevel@tonic-gate 
3957*7c478bd9Sstevel@tonic-gate 		/*
3958*7c478bd9Sstevel@tonic-gate 		**  call Strust_auth to find out whether
3959*7c478bd9Sstevel@tonic-gate 		**  auth_param is acceptable (trusted)
3960*7c478bd9Sstevel@tonic-gate 		**  we shouldn't trust it if not authenticated
3961*7c478bd9Sstevel@tonic-gate 		**  (required by RFC, leave it to ruleset?)
3962*7c478bd9Sstevel@tonic-gate 		*/
3963*7c478bd9Sstevel@tonic-gate 
3964*7c478bd9Sstevel@tonic-gate 		SuprErrs = true;
3965*7c478bd9Sstevel@tonic-gate 		QuickAbort = false;
3966*7c478bd9Sstevel@tonic-gate 		if (strcmp(auth_param, "<>") != 0 &&
3967*7c478bd9Sstevel@tonic-gate 		     (rscheck("trust_auth", auth_param, NULL, e, RSF_RMCOMM,
3968*7c478bd9Sstevel@tonic-gate 			      9, NULL, NOQID) != EX_OK || Errors > 0))
3969*7c478bd9Sstevel@tonic-gate 		{
3970*7c478bd9Sstevel@tonic-gate 			if (tTd(95, 8))
3971*7c478bd9Sstevel@tonic-gate 			{
3972*7c478bd9Sstevel@tonic-gate 				q = e->e_auth_param;
3973*7c478bd9Sstevel@tonic-gate 				sm_dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
3974*7c478bd9Sstevel@tonic-gate 					auth_param, (q == NULL) ? "" : q);
3975*7c478bd9Sstevel@tonic-gate 			}
3976*7c478bd9Sstevel@tonic-gate 
3977*7c478bd9Sstevel@tonic-gate 			/* not trusted */
3978*7c478bd9Sstevel@tonic-gate 			e->e_auth_param = "<>";
3979*7c478bd9Sstevel@tonic-gate # if _FFR_AUTH_PASSING
3980*7c478bd9Sstevel@tonic-gate 			macdefine(&BlankEnvelope.e_macro, A_PERM,
3981*7c478bd9Sstevel@tonic-gate 				  macid("{auth_author}"), NULL);
3982*7c478bd9Sstevel@tonic-gate # endif /* _FFR_AUTH_PASSING */
3983*7c478bd9Sstevel@tonic-gate 		}
3984*7c478bd9Sstevel@tonic-gate 		else
3985*7c478bd9Sstevel@tonic-gate 		{
3986*7c478bd9Sstevel@tonic-gate 			if (tTd(95, 8))
3987*7c478bd9Sstevel@tonic-gate 				sm_dprintf("auth=\"%.100s\" trusted\n", auth_param);
3988*7c478bd9Sstevel@tonic-gate 			e->e_auth_param = sm_rpool_strdup_x(e->e_rpool,
3989*7c478bd9Sstevel@tonic-gate 							    auth_param);
3990*7c478bd9Sstevel@tonic-gate 		}
3991*7c478bd9Sstevel@tonic-gate 		sm_free(auth_param); /* XXX */
3992*7c478bd9Sstevel@tonic-gate 
3993*7c478bd9Sstevel@tonic-gate 		/* reset values */
3994*7c478bd9Sstevel@tonic-gate 		Errors = 0;
3995*7c478bd9Sstevel@tonic-gate 		QuickAbort = saveQuickAbort;
3996*7c478bd9Sstevel@tonic-gate 		SuprErrs = saveSuprErrs;
3997*7c478bd9Sstevel@tonic-gate 		ExitStat = saveExitStat;
3998*7c478bd9Sstevel@tonic-gate 	}
3999*7c478bd9Sstevel@tonic-gate #endif /* SASL */
4000*7c478bd9Sstevel@tonic-gate #define PRTCHAR(c)	((isascii(c) && isprint(c)) ? (c) : '?')
4001*7c478bd9Sstevel@tonic-gate 
4002*7c478bd9Sstevel@tonic-gate 	/*
4003*7c478bd9Sstevel@tonic-gate 	**  "by" is only accepted if DeliverByMin >= 0.
4004*7c478bd9Sstevel@tonic-gate 	**  We maybe could add this to the list of server_features.
4005*7c478bd9Sstevel@tonic-gate 	*/
4006*7c478bd9Sstevel@tonic-gate 
4007*7c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(kp, "by") == 0 && DeliverByMin >= 0)
4008*7c478bd9Sstevel@tonic-gate 	{
4009*7c478bd9Sstevel@tonic-gate 		char *s;
4010*7c478bd9Sstevel@tonic-gate 
4011*7c478bd9Sstevel@tonic-gate 		if (vp == NULL)
4012*7c478bd9Sstevel@tonic-gate 		{
4013*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 BY= requires a value");
4014*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4015*7c478bd9Sstevel@tonic-gate 		}
4016*7c478bd9Sstevel@tonic-gate 		errno = 0;
4017*7c478bd9Sstevel@tonic-gate 		e->e_deliver_by = strtol(vp, &s, 10);
4018*7c478bd9Sstevel@tonic-gate 		if (e->e_deliver_by == LONG_MIN ||
4019*7c478bd9Sstevel@tonic-gate 		    e->e_deliver_by == LONG_MAX ||
4020*7c478bd9Sstevel@tonic-gate 		    e->e_deliver_by > 999999999l ||
4021*7c478bd9Sstevel@tonic-gate 		    e->e_deliver_by < -999999999l)
4022*7c478bd9Sstevel@tonic-gate 		{
4023*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 BY=%s out of range", vp);
4024*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4025*7c478bd9Sstevel@tonic-gate 		}
4026*7c478bd9Sstevel@tonic-gate 		if (s == NULL || *s != ';')
4027*7c478bd9Sstevel@tonic-gate 		{
4028*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 BY= missing ';'");
4029*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4030*7c478bd9Sstevel@tonic-gate 		}
4031*7c478bd9Sstevel@tonic-gate 		e->e_dlvr_flag = 0;
4032*7c478bd9Sstevel@tonic-gate 		++s;	/* XXX: spaces allowed? */
4033*7c478bd9Sstevel@tonic-gate 		SKIP_SPACE(s);
4034*7c478bd9Sstevel@tonic-gate 		switch (tolower(*s))
4035*7c478bd9Sstevel@tonic-gate 		{
4036*7c478bd9Sstevel@tonic-gate 		  case 'n':
4037*7c478bd9Sstevel@tonic-gate 			e->e_dlvr_flag = DLVR_NOTIFY;
4038*7c478bd9Sstevel@tonic-gate 			break;
4039*7c478bd9Sstevel@tonic-gate 		  case 'r':
4040*7c478bd9Sstevel@tonic-gate 			e->e_dlvr_flag = DLVR_RETURN;
4041*7c478bd9Sstevel@tonic-gate 			if (e->e_deliver_by <= 0)
4042*7c478bd9Sstevel@tonic-gate 			{
4043*7c478bd9Sstevel@tonic-gate 				usrerr("501 5.5.4 mode R requires BY time > 0");
4044*7c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
4045*7c478bd9Sstevel@tonic-gate 			}
4046*7c478bd9Sstevel@tonic-gate 			if (DeliverByMin > 0 && e->e_deliver_by > 0 &&
4047*7c478bd9Sstevel@tonic-gate 			    e->e_deliver_by < DeliverByMin)
4048*7c478bd9Sstevel@tonic-gate 			{
4049*7c478bd9Sstevel@tonic-gate 				usrerr("555 5.5.2 time %ld less than %ld",
4050*7c478bd9Sstevel@tonic-gate 					e->e_deliver_by, (long) DeliverByMin);
4051*7c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
4052*7c478bd9Sstevel@tonic-gate 			}
4053*7c478bd9Sstevel@tonic-gate 			break;
4054*7c478bd9Sstevel@tonic-gate 		  default:
4055*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 illegal by-mode '%c'", PRTCHAR(*s));
4056*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4057*7c478bd9Sstevel@tonic-gate 		}
4058*7c478bd9Sstevel@tonic-gate 		++s;	/* XXX: spaces allowed? */
4059*7c478bd9Sstevel@tonic-gate 		SKIP_SPACE(s);
4060*7c478bd9Sstevel@tonic-gate 		switch (tolower(*s))
4061*7c478bd9Sstevel@tonic-gate 		{
4062*7c478bd9Sstevel@tonic-gate 		  case 't':
4063*7c478bd9Sstevel@tonic-gate 			e->e_dlvr_flag |= DLVR_TRACE;
4064*7c478bd9Sstevel@tonic-gate 			break;
4065*7c478bd9Sstevel@tonic-gate 		  case '\0':
4066*7c478bd9Sstevel@tonic-gate 			break;
4067*7c478bd9Sstevel@tonic-gate 		  default:
4068*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 illegal by-trace '%c'", PRTCHAR(*s));
4069*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4070*7c478bd9Sstevel@tonic-gate 		}
4071*7c478bd9Sstevel@tonic-gate 
4072*7c478bd9Sstevel@tonic-gate 		/* XXX: check whether more characters follow? */
4073*7c478bd9Sstevel@tonic-gate 	}
4074*7c478bd9Sstevel@tonic-gate 	else
4075*7c478bd9Sstevel@tonic-gate 	{
4076*7c478bd9Sstevel@tonic-gate 		usrerr("555 5.5.4 %s parameter unrecognized", kp);
4077*7c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
4078*7c478bd9Sstevel@tonic-gate 	}
4079*7c478bd9Sstevel@tonic-gate }
4080*7c478bd9Sstevel@tonic-gate /*
4081*7c478bd9Sstevel@tonic-gate **  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
4082*7c478bd9Sstevel@tonic-gate **
4083*7c478bd9Sstevel@tonic-gate **	Parameters:
4084*7c478bd9Sstevel@tonic-gate **		a -- the address corresponding to the To: parameter.
4085*7c478bd9Sstevel@tonic-gate **		kp -- the parameter key.
4086*7c478bd9Sstevel@tonic-gate **		vp -- the value of that parameter.
4087*7c478bd9Sstevel@tonic-gate **		e -- the envelope.
4088*7c478bd9Sstevel@tonic-gate **
4089*7c478bd9Sstevel@tonic-gate **	Returns:
4090*7c478bd9Sstevel@tonic-gate **		none.
4091*7c478bd9Sstevel@tonic-gate */
4092*7c478bd9Sstevel@tonic-gate 
4093*7c478bd9Sstevel@tonic-gate static void
4094*7c478bd9Sstevel@tonic-gate rcpt_esmtp_args(a, kp, vp, e)
4095*7c478bd9Sstevel@tonic-gate 	ADDRESS *a;
4096*7c478bd9Sstevel@tonic-gate 	char *kp;
4097*7c478bd9Sstevel@tonic-gate 	char *vp;
4098*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
4099*7c478bd9Sstevel@tonic-gate {
4100*7c478bd9Sstevel@tonic-gate 	if (sm_strcasecmp(kp, "notify") == 0)
4101*7c478bd9Sstevel@tonic-gate 	{
4102*7c478bd9Sstevel@tonic-gate 		char *p;
4103*7c478bd9Sstevel@tonic-gate 
4104*7c478bd9Sstevel@tonic-gate 		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
4105*7c478bd9Sstevel@tonic-gate 		{
4106*7c478bd9Sstevel@tonic-gate 			usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
4107*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4108*7c478bd9Sstevel@tonic-gate 		}
4109*7c478bd9Sstevel@tonic-gate 		if (vp == NULL)
4110*7c478bd9Sstevel@tonic-gate 		{
4111*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 NOTIFY requires a value");
4112*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4113*7c478bd9Sstevel@tonic-gate 		}
4114*7c478bd9Sstevel@tonic-gate 		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
4115*7c478bd9Sstevel@tonic-gate 		a->q_flags |= QHASNOTIFY;
4116*7c478bd9Sstevel@tonic-gate 		macdefine(&e->e_macro, A_TEMP, macid("{dsn_notify}"), vp);
4117*7c478bd9Sstevel@tonic-gate 
4118*7c478bd9Sstevel@tonic-gate 		if (sm_strcasecmp(vp, "never") == 0)
4119*7c478bd9Sstevel@tonic-gate 			return;
4120*7c478bd9Sstevel@tonic-gate 		for (p = vp; p != NULL; vp = p)
4121*7c478bd9Sstevel@tonic-gate 		{
4122*7c478bd9Sstevel@tonic-gate 			char *s;
4123*7c478bd9Sstevel@tonic-gate 
4124*7c478bd9Sstevel@tonic-gate 			s = p = strchr(p, ',');
4125*7c478bd9Sstevel@tonic-gate 			if (p != NULL)
4126*7c478bd9Sstevel@tonic-gate 				*p++ = '\0';
4127*7c478bd9Sstevel@tonic-gate 			if (sm_strcasecmp(vp, "success") == 0)
4128*7c478bd9Sstevel@tonic-gate 				a->q_flags |= QPINGONSUCCESS;
4129*7c478bd9Sstevel@tonic-gate 			else if (sm_strcasecmp(vp, "failure") == 0)
4130*7c478bd9Sstevel@tonic-gate 				a->q_flags |= QPINGONFAILURE;
4131*7c478bd9Sstevel@tonic-gate 			else if (sm_strcasecmp(vp, "delay") == 0)
4132*7c478bd9Sstevel@tonic-gate 				a->q_flags |= QPINGONDELAY;
4133*7c478bd9Sstevel@tonic-gate 			else
4134*7c478bd9Sstevel@tonic-gate 			{
4135*7c478bd9Sstevel@tonic-gate 				usrerr("501 5.5.4 Bad argument \"%s\"  to NOTIFY",
4136*7c478bd9Sstevel@tonic-gate 					vp);
4137*7c478bd9Sstevel@tonic-gate 				/* NOTREACHED */
4138*7c478bd9Sstevel@tonic-gate 			}
4139*7c478bd9Sstevel@tonic-gate 			if (s != NULL)
4140*7c478bd9Sstevel@tonic-gate 				*s = ',';
4141*7c478bd9Sstevel@tonic-gate 		}
4142*7c478bd9Sstevel@tonic-gate 	}
4143*7c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(kp, "orcpt") == 0)
4144*7c478bd9Sstevel@tonic-gate 	{
4145*7c478bd9Sstevel@tonic-gate 		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
4146*7c478bd9Sstevel@tonic-gate 		{
4147*7c478bd9Sstevel@tonic-gate 			usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
4148*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4149*7c478bd9Sstevel@tonic-gate 		}
4150*7c478bd9Sstevel@tonic-gate 		if (vp == NULL)
4151*7c478bd9Sstevel@tonic-gate 		{
4152*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.2 ORCPT requires a value");
4153*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4154*7c478bd9Sstevel@tonic-gate 		}
4155*7c478bd9Sstevel@tonic-gate 		if (strchr(vp, ';') == NULL || !xtextok(vp))
4156*7c478bd9Sstevel@tonic-gate 		{
4157*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
4158*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4159*7c478bd9Sstevel@tonic-gate 		}
4160*7c478bd9Sstevel@tonic-gate 		if (a->q_orcpt != NULL)
4161*7c478bd9Sstevel@tonic-gate 		{
4162*7c478bd9Sstevel@tonic-gate 			usrerr("501 5.5.0 Duplicate ORCPT parameter");
4163*7c478bd9Sstevel@tonic-gate 			/* NOTREACHED */
4164*7c478bd9Sstevel@tonic-gate 		}
4165*7c478bd9Sstevel@tonic-gate 		a->q_orcpt = sm_rpool_strdup_x(e->e_rpool, vp);
4166*7c478bd9Sstevel@tonic-gate 	}
4167*7c478bd9Sstevel@tonic-gate 	else
4168*7c478bd9Sstevel@tonic-gate 	{
4169*7c478bd9Sstevel@tonic-gate 		usrerr("555 5.5.4 %s parameter unrecognized", kp);
4170*7c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
4171*7c478bd9Sstevel@tonic-gate 	}
4172*7c478bd9Sstevel@tonic-gate }
4173*7c478bd9Sstevel@tonic-gate /*
4174*7c478bd9Sstevel@tonic-gate **  PRINTVRFYADDR -- print an entry in the verify queue
4175*7c478bd9Sstevel@tonic-gate **
4176*7c478bd9Sstevel@tonic-gate **	Parameters:
4177*7c478bd9Sstevel@tonic-gate **		a -- the address to print.
4178*7c478bd9Sstevel@tonic-gate **		last -- set if this is the last one.
4179*7c478bd9Sstevel@tonic-gate **		vrfy -- set if this is a VRFY command.
4180*7c478bd9Sstevel@tonic-gate **
4181*7c478bd9Sstevel@tonic-gate **	Returns:
4182*7c478bd9Sstevel@tonic-gate **		none.
4183*7c478bd9Sstevel@tonic-gate **
4184*7c478bd9Sstevel@tonic-gate **	Side Effects:
4185*7c478bd9Sstevel@tonic-gate **		Prints the appropriate 250 codes.
4186*7c478bd9Sstevel@tonic-gate */
4187*7c478bd9Sstevel@tonic-gate #define OFFF	(3 + 1 + 5 + 1)	/* offset in fmt: SMTP reply + enh. code */
4188*7c478bd9Sstevel@tonic-gate 
4189*7c478bd9Sstevel@tonic-gate static void
4190*7c478bd9Sstevel@tonic-gate printvrfyaddr(a, last, vrfy)
4191*7c478bd9Sstevel@tonic-gate 	register ADDRESS *a;
4192*7c478bd9Sstevel@tonic-gate 	bool last;
4193*7c478bd9Sstevel@tonic-gate 	bool vrfy;
4194*7c478bd9Sstevel@tonic-gate {
4195*7c478bd9Sstevel@tonic-gate 	char fmtbuf[30];
4196*7c478bd9Sstevel@tonic-gate 
4197*7c478bd9Sstevel@tonic-gate 	if (vrfy && a->q_mailer != NULL &&
4198*7c478bd9Sstevel@tonic-gate 	    !bitnset(M_VRFY250, a->q_mailer->m_flags))
4199*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(fmtbuf, "252", sizeof fmtbuf);
4200*7c478bd9Sstevel@tonic-gate 	else
4201*7c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(fmtbuf, "250", sizeof fmtbuf);
4202*7c478bd9Sstevel@tonic-gate 	fmtbuf[3] = last ? ' ' : '-';
4203*7c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4);
4204*7c478bd9Sstevel@tonic-gate 	if (a->q_fullname == NULL)
4205*7c478bd9Sstevel@tonic-gate 	{
4206*7c478bd9Sstevel@tonic-gate 		if ((a->q_mailer == NULL ||
4207*7c478bd9Sstevel@tonic-gate 		     a->q_mailer->m_addrtype == NULL ||
4208*7c478bd9Sstevel@tonic-gate 		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
4209*7c478bd9Sstevel@tonic-gate 		    strchr(a->q_user, '@') == NULL)
4210*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s@%s>",
4211*7c478bd9Sstevel@tonic-gate 				       sizeof fmtbuf - OFFF);
4212*7c478bd9Sstevel@tonic-gate 		else
4213*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcpy(&fmtbuf[OFFF], "<%s>",
4214*7c478bd9Sstevel@tonic-gate 				       sizeof fmtbuf - OFFF);
4215*7c478bd9Sstevel@tonic-gate 		message(fmtbuf, a->q_user, MyHostName);
4216*7c478bd9Sstevel@tonic-gate 	}
4217*7c478bd9Sstevel@tonic-gate 	else
4218*7c478bd9Sstevel@tonic-gate 	{
4219*7c478bd9Sstevel@tonic-gate 		if ((a->q_mailer == NULL ||
4220*7c478bd9Sstevel@tonic-gate 		     a->q_mailer->m_addrtype == NULL ||
4221*7c478bd9Sstevel@tonic-gate 		     sm_strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
4222*7c478bd9Sstevel@tonic-gate 		    strchr(a->q_user, '@') == NULL)
4223*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
4224*7c478bd9Sstevel@tonic-gate 				       sizeof fmtbuf - OFFF);
4225*7c478bd9Sstevel@tonic-gate 		else
4226*7c478bd9Sstevel@tonic-gate 			(void) sm_strlcpy(&fmtbuf[OFFF], "%s <%s>",
4227*7c478bd9Sstevel@tonic-gate 				       sizeof fmtbuf - OFFF);
4228*7c478bd9Sstevel@tonic-gate 		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
4229*7c478bd9Sstevel@tonic-gate 	}
4230*7c478bd9Sstevel@tonic-gate }
4231*7c478bd9Sstevel@tonic-gate 
4232*7c478bd9Sstevel@tonic-gate #if SASL
4233*7c478bd9Sstevel@tonic-gate /*
4234*7c478bd9Sstevel@tonic-gate **  SASLMECHS -- get list of possible AUTH mechanisms
4235*7c478bd9Sstevel@tonic-gate **
4236*7c478bd9Sstevel@tonic-gate **	Parameters:
4237*7c478bd9Sstevel@tonic-gate **		conn -- SASL connection info.
4238*7c478bd9Sstevel@tonic-gate **		mechlist -- output parameter for list of mechanisms.
4239*7c478bd9Sstevel@tonic-gate **
4240*7c478bd9Sstevel@tonic-gate **	Returns:
4241*7c478bd9Sstevel@tonic-gate **		number of mechs.
4242*7c478bd9Sstevel@tonic-gate */
4243*7c478bd9Sstevel@tonic-gate 
4244*7c478bd9Sstevel@tonic-gate static int
4245*7c478bd9Sstevel@tonic-gate saslmechs(conn, mechlist)
4246*7c478bd9Sstevel@tonic-gate 	sasl_conn_t *conn;
4247*7c478bd9Sstevel@tonic-gate 	char **mechlist;
4248*7c478bd9Sstevel@tonic-gate {
4249*7c478bd9Sstevel@tonic-gate 	int len, num, result;
4250*7c478bd9Sstevel@tonic-gate 
4251*7c478bd9Sstevel@tonic-gate 	/* "user" is currently unused */
4252*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
4253*7c478bd9Sstevel@tonic-gate 	result = sasl_listmech(conn, NULL,
4254*7c478bd9Sstevel@tonic-gate 			       "", " ", "", (const char **) mechlist,
4255*7c478bd9Sstevel@tonic-gate 			       (unsigned int *)&len, &num);
4256*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
4257*7c478bd9Sstevel@tonic-gate 	result = sasl_listmech(conn, "user", /* XXX */
4258*7c478bd9Sstevel@tonic-gate 			       "", " ", "", mechlist,
4259*7c478bd9Sstevel@tonic-gate 			       (unsigned int *)&len, (unsigned int *)&num);
4260*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
4261*7c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
4262*7c478bd9Sstevel@tonic-gate 	{
4263*7c478bd9Sstevel@tonic-gate 		if (LogLevel > 9)
4264*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
4265*7c478bd9Sstevel@tonic-gate 				  "AUTH error: listmech=%d, num=%d",
4266*7c478bd9Sstevel@tonic-gate 				  result, num);
4267*7c478bd9Sstevel@tonic-gate 		num = 0;
4268*7c478bd9Sstevel@tonic-gate 	}
4269*7c478bd9Sstevel@tonic-gate 	if (num > 0)
4270*7c478bd9Sstevel@tonic-gate 	{
4271*7c478bd9Sstevel@tonic-gate 		if (LogLevel > 11)
4272*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_INFO, NOQID,
4273*7c478bd9Sstevel@tonic-gate 				  "AUTH: available mech=%s, allowed mech=%s",
4274*7c478bd9Sstevel@tonic-gate 				  *mechlist, AuthMechanisms);
4275*7c478bd9Sstevel@tonic-gate 		*mechlist = intersect(AuthMechanisms, *mechlist, NULL);
4276*7c478bd9Sstevel@tonic-gate 	}
4277*7c478bd9Sstevel@tonic-gate 	else
4278*7c478bd9Sstevel@tonic-gate 	{
4279*7c478bd9Sstevel@tonic-gate 		*mechlist = NULL;	/* be paranoid... */
4280*7c478bd9Sstevel@tonic-gate 		if (result == SASL_OK && LogLevel > 9)
4281*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
4282*7c478bd9Sstevel@tonic-gate 				  "AUTH warning: no mechanisms");
4283*7c478bd9Sstevel@tonic-gate 	}
4284*7c478bd9Sstevel@tonic-gate 	return num;
4285*7c478bd9Sstevel@tonic-gate }
4286*7c478bd9Sstevel@tonic-gate 
4287*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
4288*7c478bd9Sstevel@tonic-gate /*
4289*7c478bd9Sstevel@tonic-gate **  PROXY_POLICY -- define proxy policy for AUTH
4290*7c478bd9Sstevel@tonic-gate **
4291*7c478bd9Sstevel@tonic-gate **	Parameters:
4292*7c478bd9Sstevel@tonic-gate **		conn -- unused.
4293*7c478bd9Sstevel@tonic-gate **		context -- unused.
4294*7c478bd9Sstevel@tonic-gate **		requested_user -- authorization identity.
4295*7c478bd9Sstevel@tonic-gate **		rlen -- authorization identity length.
4296*7c478bd9Sstevel@tonic-gate **		auth_identity -- authentication identity.
4297*7c478bd9Sstevel@tonic-gate **		alen -- authentication identity length.
4298*7c478bd9Sstevel@tonic-gate **		def_realm -- default user realm.
4299*7c478bd9Sstevel@tonic-gate **		urlen -- user realm length.
4300*7c478bd9Sstevel@tonic-gate **		propctx -- unused.
4301*7c478bd9Sstevel@tonic-gate **
4302*7c478bd9Sstevel@tonic-gate **	Returns:
4303*7c478bd9Sstevel@tonic-gate **		ok?
4304*7c478bd9Sstevel@tonic-gate **
4305*7c478bd9Sstevel@tonic-gate **	Side Effects:
4306*7c478bd9Sstevel@tonic-gate **		sets {auth_authen} macro.
4307*7c478bd9Sstevel@tonic-gate */
4308*7c478bd9Sstevel@tonic-gate 
4309*7c478bd9Sstevel@tonic-gate int
4310*7c478bd9Sstevel@tonic-gate proxy_policy(conn, context, requested_user, rlen, auth_identity, alen,
4311*7c478bd9Sstevel@tonic-gate 	     def_realm, urlen, propctx)
4312*7c478bd9Sstevel@tonic-gate 	sasl_conn_t *conn;
4313*7c478bd9Sstevel@tonic-gate 	void *context;
4314*7c478bd9Sstevel@tonic-gate 	const char *requested_user;
4315*7c478bd9Sstevel@tonic-gate 	unsigned rlen;
4316*7c478bd9Sstevel@tonic-gate 	const char *auth_identity;
4317*7c478bd9Sstevel@tonic-gate 	unsigned alen;
4318*7c478bd9Sstevel@tonic-gate 	const char *def_realm;
4319*7c478bd9Sstevel@tonic-gate 	unsigned urlen;
4320*7c478bd9Sstevel@tonic-gate 	struct propctx *propctx;
4321*7c478bd9Sstevel@tonic-gate {
4322*7c478bd9Sstevel@tonic-gate 	if (auth_identity == NULL)
4323*7c478bd9Sstevel@tonic-gate 		return SASL_FAIL;
4324*7c478bd9Sstevel@tonic-gate 
4325*7c478bd9Sstevel@tonic-gate 	macdefine(&BlankEnvelope.e_macro, A_TEMP,
4326*7c478bd9Sstevel@tonic-gate 		  macid("{auth_authen}"), (char *) auth_identity);
4327*7c478bd9Sstevel@tonic-gate 
4328*7c478bd9Sstevel@tonic-gate 	return SASL_OK;
4329*7c478bd9Sstevel@tonic-gate }
4330*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
4331*7c478bd9Sstevel@tonic-gate 
4332*7c478bd9Sstevel@tonic-gate /*
4333*7c478bd9Sstevel@tonic-gate **  PROXY_POLICY -- define proxy policy for AUTH
4334*7c478bd9Sstevel@tonic-gate **
4335*7c478bd9Sstevel@tonic-gate **	Parameters:
4336*7c478bd9Sstevel@tonic-gate **		context -- unused.
4337*7c478bd9Sstevel@tonic-gate **		auth_identity -- authentication identity.
4338*7c478bd9Sstevel@tonic-gate **		requested_user -- authorization identity.
4339*7c478bd9Sstevel@tonic-gate **		user -- allowed user (output).
4340*7c478bd9Sstevel@tonic-gate **		errstr -- possible error string (output).
4341*7c478bd9Sstevel@tonic-gate **
4342*7c478bd9Sstevel@tonic-gate **	Returns:
4343*7c478bd9Sstevel@tonic-gate **		ok?
4344*7c478bd9Sstevel@tonic-gate */
4345*7c478bd9Sstevel@tonic-gate 
4346*7c478bd9Sstevel@tonic-gate int
4347*7c478bd9Sstevel@tonic-gate proxy_policy(context, auth_identity, requested_user, user, errstr)
4348*7c478bd9Sstevel@tonic-gate 	void *context;
4349*7c478bd9Sstevel@tonic-gate 	const char *auth_identity;
4350*7c478bd9Sstevel@tonic-gate 	const char *requested_user;
4351*7c478bd9Sstevel@tonic-gate 	const char **user;
4352*7c478bd9Sstevel@tonic-gate 	const char **errstr;
4353*7c478bd9Sstevel@tonic-gate {
4354*7c478bd9Sstevel@tonic-gate 	if (user == NULL || auth_identity == NULL)
4355*7c478bd9Sstevel@tonic-gate 		return SASL_FAIL;
4356*7c478bd9Sstevel@tonic-gate 	*user = newstr(auth_identity);
4357*7c478bd9Sstevel@tonic-gate 	return SASL_OK;
4358*7c478bd9Sstevel@tonic-gate }
4359*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
4360*7c478bd9Sstevel@tonic-gate #endif /* SASL */
4361*7c478bd9Sstevel@tonic-gate 
4362*7c478bd9Sstevel@tonic-gate #if STARTTLS
4363*7c478bd9Sstevel@tonic-gate /*
4364*7c478bd9Sstevel@tonic-gate **  INITSRVTLS -- initialize server side TLS
4365*7c478bd9Sstevel@tonic-gate **
4366*7c478bd9Sstevel@tonic-gate **	Parameters:
4367*7c478bd9Sstevel@tonic-gate **		tls_ok -- should tls initialization be done?
4368*7c478bd9Sstevel@tonic-gate **
4369*7c478bd9Sstevel@tonic-gate **	Returns:
4370*7c478bd9Sstevel@tonic-gate **		succeeded?
4371*7c478bd9Sstevel@tonic-gate **
4372*7c478bd9Sstevel@tonic-gate **	Side Effects:
4373*7c478bd9Sstevel@tonic-gate **		sets tls_ok_srv which is a static variable in this module.
4374*7c478bd9Sstevel@tonic-gate **		Do NOT remove assignments to it!
4375*7c478bd9Sstevel@tonic-gate */
4376*7c478bd9Sstevel@tonic-gate 
4377*7c478bd9Sstevel@tonic-gate bool
4378*7c478bd9Sstevel@tonic-gate initsrvtls(tls_ok)
4379*7c478bd9Sstevel@tonic-gate 	bool tls_ok;
4380*7c478bd9Sstevel@tonic-gate {
4381*7c478bd9Sstevel@tonic-gate 	if (!tls_ok)
4382*7c478bd9Sstevel@tonic-gate 		return false;
4383*7c478bd9Sstevel@tonic-gate 
4384*7c478bd9Sstevel@tonic-gate 	/* do NOT remove assignment */
4385*7c478bd9Sstevel@tonic-gate 	tls_ok_srv = inittls(&srv_ctx, TLS_Srv_Opts, true, SrvCertFile,
4386*7c478bd9Sstevel@tonic-gate 			     SrvKeyFile, CACertPath, CACertFile, DHParams);
4387*7c478bd9Sstevel@tonic-gate 	return tls_ok_srv;
4388*7c478bd9Sstevel@tonic-gate }
4389*7c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
4390*7c478bd9Sstevel@tonic-gate /*
4391*7c478bd9Sstevel@tonic-gate **  SRVFEATURES -- get features for SMTP server
4392*7c478bd9Sstevel@tonic-gate **
4393*7c478bd9Sstevel@tonic-gate **	Parameters:
4394*7c478bd9Sstevel@tonic-gate **		e -- envelope (should be session context).
4395*7c478bd9Sstevel@tonic-gate **		clientname -- name of client.
4396*7c478bd9Sstevel@tonic-gate **		features -- default features for this invocation.
4397*7c478bd9Sstevel@tonic-gate **
4398*7c478bd9Sstevel@tonic-gate **	Returns:
4399*7c478bd9Sstevel@tonic-gate **		server features.
4400*7c478bd9Sstevel@tonic-gate */
4401*7c478bd9Sstevel@tonic-gate 
4402*7c478bd9Sstevel@tonic-gate /* table with options: it uses just one character, how about strings? */
4403*7c478bd9Sstevel@tonic-gate static struct
4404*7c478bd9Sstevel@tonic-gate {
4405*7c478bd9Sstevel@tonic-gate 	char		srvf_opt;
4406*7c478bd9Sstevel@tonic-gate 	unsigned int	srvf_flag;
4407*7c478bd9Sstevel@tonic-gate } srv_feat_table[] =
4408*7c478bd9Sstevel@tonic-gate {
4409*7c478bd9Sstevel@tonic-gate 	{ 'A',	SRV_OFFER_AUTH	},
4410*7c478bd9Sstevel@tonic-gate 	{ 'B',	SRV_OFFER_VERB	},
4411*7c478bd9Sstevel@tonic-gate 	{ 'C',	SRV_REQ_SEC	},
4412*7c478bd9Sstevel@tonic-gate 	{ 'D',	SRV_OFFER_DSN	},
4413*7c478bd9Sstevel@tonic-gate 	{ 'E',	SRV_OFFER_ETRN	},
4414*7c478bd9Sstevel@tonic-gate 	{ 'L',	SRV_REQ_AUTH	},
4415*7c478bd9Sstevel@tonic-gate #if PIPELINING
4416*7c478bd9Sstevel@tonic-gate # if _FFR_NO_PIPE
4417*7c478bd9Sstevel@tonic-gate 	{ 'N',	SRV_NO_PIPE	},
4418*7c478bd9Sstevel@tonic-gate # endif /* _FFR_NO_PIPE */
4419*7c478bd9Sstevel@tonic-gate 	{ 'P',	SRV_OFFER_PIPE	},
4420*7c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
4421*7c478bd9Sstevel@tonic-gate 	{ 'R',	SRV_VRFY_CLT	},	/* same as V; not documented */
4422*7c478bd9Sstevel@tonic-gate 	{ 'S',	SRV_OFFER_TLS	},
4423*7c478bd9Sstevel@tonic-gate /*	{ 'T',	SRV_TMP_FAIL	},	*/
4424*7c478bd9Sstevel@tonic-gate 	{ 'V',	SRV_VRFY_CLT	},
4425*7c478bd9Sstevel@tonic-gate 	{ 'X',	SRV_OFFER_EXPN	},
4426*7c478bd9Sstevel@tonic-gate /*	{ 'Y',	SRV_OFFER_VRFY	},	*/
4427*7c478bd9Sstevel@tonic-gate 	{ '\0',	SRV_NONE	}
4428*7c478bd9Sstevel@tonic-gate };
4429*7c478bd9Sstevel@tonic-gate 
4430*7c478bd9Sstevel@tonic-gate static unsigned int
4431*7c478bd9Sstevel@tonic-gate srvfeatures(e, clientname, features)
4432*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
4433*7c478bd9Sstevel@tonic-gate 	char *clientname;
4434*7c478bd9Sstevel@tonic-gate 	unsigned int features;
4435*7c478bd9Sstevel@tonic-gate {
4436*7c478bd9Sstevel@tonic-gate 	int r, i, j;
4437*7c478bd9Sstevel@tonic-gate 	char **pvp, c, opt;
4438*7c478bd9Sstevel@tonic-gate 	char pvpbuf[PSBUFSIZE];
4439*7c478bd9Sstevel@tonic-gate 
4440*7c478bd9Sstevel@tonic-gate 	pvp = NULL;
4441*7c478bd9Sstevel@tonic-gate 	r = rscap("srv_features", clientname, "", e, &pvp, pvpbuf,
4442*7c478bd9Sstevel@tonic-gate 		  sizeof(pvpbuf));
4443*7c478bd9Sstevel@tonic-gate 	if (r != EX_OK)
4444*7c478bd9Sstevel@tonic-gate 		return features;
4445*7c478bd9Sstevel@tonic-gate 	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
4446*7c478bd9Sstevel@tonic-gate 		return features;
4447*7c478bd9Sstevel@tonic-gate 	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
4448*7c478bd9Sstevel@tonic-gate 		return SRV_TMP_FAIL;
4449*7c478bd9Sstevel@tonic-gate 
4450*7c478bd9Sstevel@tonic-gate 	/*
4451*7c478bd9Sstevel@tonic-gate 	**  General rule (see sendmail.h, d_flags):
4452*7c478bd9Sstevel@tonic-gate 	**  lower case: required/offered, upper case: Not required/available
4453*7c478bd9Sstevel@tonic-gate 	**
4454*7c478bd9Sstevel@tonic-gate 	**  Since we can change some features per daemon, we have both
4455*7c478bd9Sstevel@tonic-gate 	**  cases here: turn on/off a feature.
4456*7c478bd9Sstevel@tonic-gate 	*/
4457*7c478bd9Sstevel@tonic-gate 
4458*7c478bd9Sstevel@tonic-gate 	for (i = 1; pvp[i] != NULL; i++)
4459*7c478bd9Sstevel@tonic-gate 	{
4460*7c478bd9Sstevel@tonic-gate 		c = pvp[i][0];
4461*7c478bd9Sstevel@tonic-gate 		j = 0;
4462*7c478bd9Sstevel@tonic-gate 		for (;;)
4463*7c478bd9Sstevel@tonic-gate 		{
4464*7c478bd9Sstevel@tonic-gate 			if ((opt = srv_feat_table[j].srvf_opt) == '\0')
4465*7c478bd9Sstevel@tonic-gate 			{
4466*7c478bd9Sstevel@tonic-gate 				if (LogLevel > 9)
4467*7c478bd9Sstevel@tonic-gate 					sm_syslog(LOG_WARNING, e->e_id,
4468*7c478bd9Sstevel@tonic-gate 						  "srvfeatures: unknown feature %s",
4469*7c478bd9Sstevel@tonic-gate 						  pvp[i]);
4470*7c478bd9Sstevel@tonic-gate 				break;
4471*7c478bd9Sstevel@tonic-gate 			}
4472*7c478bd9Sstevel@tonic-gate 			if (c == opt)
4473*7c478bd9Sstevel@tonic-gate 			{
4474*7c478bd9Sstevel@tonic-gate 				features &= ~(srv_feat_table[j].srvf_flag);
4475*7c478bd9Sstevel@tonic-gate 				break;
4476*7c478bd9Sstevel@tonic-gate 			}
4477*7c478bd9Sstevel@tonic-gate 			if (c == tolower(opt))
4478*7c478bd9Sstevel@tonic-gate 			{
4479*7c478bd9Sstevel@tonic-gate 				features |= srv_feat_table[j].srvf_flag;
4480*7c478bd9Sstevel@tonic-gate 				break;
4481*7c478bd9Sstevel@tonic-gate 			}
4482*7c478bd9Sstevel@tonic-gate 			++j;
4483*7c478bd9Sstevel@tonic-gate 		}
4484*7c478bd9Sstevel@tonic-gate 	}
4485*7c478bd9Sstevel@tonic-gate 	return features;
4486*7c478bd9Sstevel@tonic-gate }
4487*7c478bd9Sstevel@tonic-gate 
4488*7c478bd9Sstevel@tonic-gate /*
4489*7c478bd9Sstevel@tonic-gate **  HELP -- implement the HELP command.
4490*7c478bd9Sstevel@tonic-gate **
4491*7c478bd9Sstevel@tonic-gate **	Parameters:
4492*7c478bd9Sstevel@tonic-gate **		topic -- the topic we want help for.
4493*7c478bd9Sstevel@tonic-gate **		e -- envelope.
4494*7c478bd9Sstevel@tonic-gate **
4495*7c478bd9Sstevel@tonic-gate **	Returns:
4496*7c478bd9Sstevel@tonic-gate **		none.
4497*7c478bd9Sstevel@tonic-gate **
4498*7c478bd9Sstevel@tonic-gate **	Side Effects:
4499*7c478bd9Sstevel@tonic-gate **		outputs the help file to message output.
4500*7c478bd9Sstevel@tonic-gate */
4501*7c478bd9Sstevel@tonic-gate #define HELPVSTR	"#vers	"
4502*7c478bd9Sstevel@tonic-gate #define HELPVERSION	2
4503*7c478bd9Sstevel@tonic-gate 
4504*7c478bd9Sstevel@tonic-gate void
4505*7c478bd9Sstevel@tonic-gate help(topic, e)
4506*7c478bd9Sstevel@tonic-gate 	char *topic;
4507*7c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
4508*7c478bd9Sstevel@tonic-gate {
4509*7c478bd9Sstevel@tonic-gate 	register SM_FILE_T *hf;
4510*7c478bd9Sstevel@tonic-gate 	register char *p;
4511*7c478bd9Sstevel@tonic-gate 	int len;
4512*7c478bd9Sstevel@tonic-gate 	bool noinfo;
4513*7c478bd9Sstevel@tonic-gate 	bool first = true;
4514*7c478bd9Sstevel@tonic-gate 	long sff = SFF_OPENASROOT|SFF_REGONLY;
4515*7c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
4516*7c478bd9Sstevel@tonic-gate 	char inp[MAXLINE];
4517*7c478bd9Sstevel@tonic-gate 	static int foundvers = -1;
4518*7c478bd9Sstevel@tonic-gate 	extern char Version[];
4519*7c478bd9Sstevel@tonic-gate 
4520*7c478bd9Sstevel@tonic-gate 	if (DontLockReadFiles)
4521*7c478bd9Sstevel@tonic-gate 		sff |= SFF_NOLOCK;
4522*7c478bd9Sstevel@tonic-gate 	if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
4523*7c478bd9Sstevel@tonic-gate 		sff |= SFF_SAFEDIRPATH;
4524*7c478bd9Sstevel@tonic-gate 
4525*7c478bd9Sstevel@tonic-gate 	if (HelpFile == NULL ||
4526*7c478bd9Sstevel@tonic-gate 	    (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
4527*7c478bd9Sstevel@tonic-gate 	{
4528*7c478bd9Sstevel@tonic-gate 		/* no help */
4529*7c478bd9Sstevel@tonic-gate 		errno = 0;
4530*7c478bd9Sstevel@tonic-gate 		message("502 5.3.0 Sendmail %s -- HELP not implemented",
4531*7c478bd9Sstevel@tonic-gate 			Version);
4532*7c478bd9Sstevel@tonic-gate 		return;
4533*7c478bd9Sstevel@tonic-gate 	}
4534*7c478bd9Sstevel@tonic-gate 
4535*7c478bd9Sstevel@tonic-gate 	if (topic == NULL || *topic == '\0')
4536*7c478bd9Sstevel@tonic-gate 	{
4537*7c478bd9Sstevel@tonic-gate 		topic = "smtp";
4538*7c478bd9Sstevel@tonic-gate 		noinfo = false;
4539*7c478bd9Sstevel@tonic-gate 	}
4540*7c478bd9Sstevel@tonic-gate 	else
4541*7c478bd9Sstevel@tonic-gate 	{
4542*7c478bd9Sstevel@tonic-gate 		makelower(topic);
4543*7c478bd9Sstevel@tonic-gate 		noinfo = true;
4544*7c478bd9Sstevel@tonic-gate 	}
4545*7c478bd9Sstevel@tonic-gate 
4546*7c478bd9Sstevel@tonic-gate 	len = strlen(topic);
4547*7c478bd9Sstevel@tonic-gate 
4548*7c478bd9Sstevel@tonic-gate 	while (sm_io_fgets(hf, SM_TIME_DEFAULT, buf, sizeof buf) != NULL)
4549*7c478bd9Sstevel@tonic-gate 	{
4550*7c478bd9Sstevel@tonic-gate 		if (buf[0] == '#')
4551*7c478bd9Sstevel@tonic-gate 		{
4552*7c478bd9Sstevel@tonic-gate 			if (foundvers < 0 &&
4553*7c478bd9Sstevel@tonic-gate 			    strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
4554*7c478bd9Sstevel@tonic-gate 			{
4555*7c478bd9Sstevel@tonic-gate 				int h;
4556*7c478bd9Sstevel@tonic-gate 
4557*7c478bd9Sstevel@tonic-gate 				if (sm_io_sscanf(buf + strlen(HELPVSTR), "%d",
4558*7c478bd9Sstevel@tonic-gate 						 &h) == 1)
4559*7c478bd9Sstevel@tonic-gate 					foundvers = h;
4560*7c478bd9Sstevel@tonic-gate 			}
4561*7c478bd9Sstevel@tonic-gate 			continue;
4562*7c478bd9Sstevel@tonic-gate 		}
4563*7c478bd9Sstevel@tonic-gate 		if (strncmp(buf, topic, len) == 0)
4564*7c478bd9Sstevel@tonic-gate 		{
4565*7c478bd9Sstevel@tonic-gate 			if (first)
4566*7c478bd9Sstevel@tonic-gate 			{
4567*7c478bd9Sstevel@tonic-gate 				first = false;
4568*7c478bd9Sstevel@tonic-gate 
4569*7c478bd9Sstevel@tonic-gate 				/* print version if no/old vers# in file */
4570*7c478bd9Sstevel@tonic-gate 				if (foundvers < 2 && !noinfo)
4571*7c478bd9Sstevel@tonic-gate 					message("214-2.0.0 This is Sendmail version %s", Version);
4572*7c478bd9Sstevel@tonic-gate 			}
4573*7c478bd9Sstevel@tonic-gate 			p = strpbrk(buf, " \t");
4574*7c478bd9Sstevel@tonic-gate 			if (p == NULL)
4575*7c478bd9Sstevel@tonic-gate 				p = buf + strlen(buf) - 1;
4576*7c478bd9Sstevel@tonic-gate 			else
4577*7c478bd9Sstevel@tonic-gate 				p++;
4578*7c478bd9Sstevel@tonic-gate 			fixcrlf(p, true);
4579*7c478bd9Sstevel@tonic-gate 			if (foundvers >= 2)
4580*7c478bd9Sstevel@tonic-gate 			{
4581*7c478bd9Sstevel@tonic-gate 				translate_dollars(p);
4582*7c478bd9Sstevel@tonic-gate 				expand(p, inp, sizeof inp, e);
4583*7c478bd9Sstevel@tonic-gate 				p = inp;
4584*7c478bd9Sstevel@tonic-gate 			}
4585*7c478bd9Sstevel@tonic-gate 			message("214-2.0.0 %s", p);
4586*7c478bd9Sstevel@tonic-gate 			noinfo = false;
4587*7c478bd9Sstevel@tonic-gate 		}
4588*7c478bd9Sstevel@tonic-gate 	}
4589*7c478bd9Sstevel@tonic-gate 
4590*7c478bd9Sstevel@tonic-gate 	if (noinfo)
4591*7c478bd9Sstevel@tonic-gate 		message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
4592*7c478bd9Sstevel@tonic-gate 	else
4593*7c478bd9Sstevel@tonic-gate 		message("214 2.0.0 End of HELP info");
4594*7c478bd9Sstevel@tonic-gate 
4595*7c478bd9Sstevel@tonic-gate 	if (foundvers != 0 && foundvers < HELPVERSION)
4596*7c478bd9Sstevel@tonic-gate 	{
4597*7c478bd9Sstevel@tonic-gate 		if (LogLevel > 1)
4598*7c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, e->e_id,
4599*7c478bd9Sstevel@tonic-gate 				  "%s too old (require version %d)",
4600*7c478bd9Sstevel@tonic-gate 				  HelpFile, HELPVERSION);
4601*7c478bd9Sstevel@tonic-gate 
4602*7c478bd9Sstevel@tonic-gate 		/* avoid log next time */
4603*7c478bd9Sstevel@tonic-gate 		foundvers = 0;
4604*7c478bd9Sstevel@tonic-gate 	}
4605*7c478bd9Sstevel@tonic-gate 
4606*7c478bd9Sstevel@tonic-gate 	(void) sm_io_close(hf, SM_TIME_DEFAULT);
4607*7c478bd9Sstevel@tonic-gate }
4608*7c478bd9Sstevel@tonic-gate 
4609*7c478bd9Sstevel@tonic-gate #if SASL
4610*7c478bd9Sstevel@tonic-gate /*
4611*7c478bd9Sstevel@tonic-gate **  RESET_SASLCONN -- reset SASL connection data
4612*7c478bd9Sstevel@tonic-gate **
4613*7c478bd9Sstevel@tonic-gate **	Parameters:
4614*7c478bd9Sstevel@tonic-gate **		conn -- SASL connection context
4615*7c478bd9Sstevel@tonic-gate **		hostname -- host name
4616*7c478bd9Sstevel@tonic-gate **		various connection data
4617*7c478bd9Sstevel@tonic-gate **
4618*7c478bd9Sstevel@tonic-gate **	Returns:
4619*7c478bd9Sstevel@tonic-gate **		SASL result
4620*7c478bd9Sstevel@tonic-gate */
4621*7c478bd9Sstevel@tonic-gate 
4622*7c478bd9Sstevel@tonic-gate static int
4623*7c478bd9Sstevel@tonic-gate reset_saslconn(sasl_conn_t **conn, char *hostname,
4624*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
4625*7c478bd9Sstevel@tonic-gate 	       char *remoteip, char *localip,
4626*7c478bd9Sstevel@tonic-gate 	       char *auth_id, sasl_ssf_t * ext_ssf)
4627*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
4628*7c478bd9Sstevel@tonic-gate 	       struct sockaddr_in *saddr_r, struct sockaddr_in *saddr_l,
4629*7c478bd9Sstevel@tonic-gate 	       sasl_external_properties_t * ext_ssf)
4630*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
4631*7c478bd9Sstevel@tonic-gate {
4632*7c478bd9Sstevel@tonic-gate 	int result;
4633*7c478bd9Sstevel@tonic-gate 
4634*7c478bd9Sstevel@tonic-gate 	sasl_dispose(conn);
4635*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
4636*7c478bd9Sstevel@tonic-gate 	result = sasl_server_new("smtp", hostname, NULL, NULL, NULL,
4637*7c478bd9Sstevel@tonic-gate 				 NULL, 0, conn);
4638*7c478bd9Sstevel@tonic-gate # elif SASL > 10505
4639*7c478bd9Sstevel@tonic-gate 	/* use empty realm: only works in SASL > 1.5.5 */
4640*7c478bd9Sstevel@tonic-gate 	result = sasl_server_new("smtp", hostname, "", NULL, 0, conn);
4641*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
4642*7c478bd9Sstevel@tonic-gate 	/* use no realm -> realm is set to hostname by SASL lib */
4643*7c478bd9Sstevel@tonic-gate 	result = sasl_server_new("smtp", hostname, NULL, NULL, 0,
4644*7c478bd9Sstevel@tonic-gate 				 conn);
4645*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
4646*7c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
4647*7c478bd9Sstevel@tonic-gate 		return result;
4648*7c478bd9Sstevel@tonic-gate 
4649*7c478bd9Sstevel@tonic-gate # if SASL >= 20000
4650*7c478bd9Sstevel@tonic-gate #  if NETINET || NETINET6
4651*7c478bd9Sstevel@tonic-gate 	if (remoteip != NULL && *remoteip != '\0')
4652*7c478bd9Sstevel@tonic-gate 		result = sasl_setprop(*conn, SASL_IPREMOTEPORT, remoteip);
4653*7c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
4654*7c478bd9Sstevel@tonic-gate 		return result;
4655*7c478bd9Sstevel@tonic-gate 
4656*7c478bd9Sstevel@tonic-gate 	if (localip != NULL && *localip != '\0')
4657*7c478bd9Sstevel@tonic-gate 		result = sasl_setprop(*conn, SASL_IPLOCALPORT, localip);
4658*7c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
4659*7c478bd9Sstevel@tonic-gate 		return result;
4660*7c478bd9Sstevel@tonic-gate #  endif /* NETINET || NETINET6 */
4661*7c478bd9Sstevel@tonic-gate 
4662*7c478bd9Sstevel@tonic-gate 	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
4663*7c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
4664*7c478bd9Sstevel@tonic-gate 		return result;
4665*7c478bd9Sstevel@tonic-gate 
4666*7c478bd9Sstevel@tonic-gate 	result = sasl_setprop(*conn, SASL_AUTH_EXTERNAL, auth_id);
4667*7c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
4668*7c478bd9Sstevel@tonic-gate 		return result;
4669*7c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
4670*7c478bd9Sstevel@tonic-gate #  if NETINET
4671*7c478bd9Sstevel@tonic-gate 	if (saddr_r != NULL)
4672*7c478bd9Sstevel@tonic-gate 		result = sasl_setprop(*conn, SASL_IP_REMOTE, saddr_r);
4673*7c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
4674*7c478bd9Sstevel@tonic-gate 		return result;
4675*7c478bd9Sstevel@tonic-gate 
4676*7c478bd9Sstevel@tonic-gate 	if (saddr_l != NULL)
4677*7c478bd9Sstevel@tonic-gate 		result = sasl_setprop(*conn, SASL_IP_LOCAL, saddr_l);
4678*7c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
4679*7c478bd9Sstevel@tonic-gate 		return result;
4680*7c478bd9Sstevel@tonic-gate #  endif /* NETINET */
4681*7c478bd9Sstevel@tonic-gate 
4682*7c478bd9Sstevel@tonic-gate 	result = sasl_setprop(*conn, SASL_SSF_EXTERNAL, ext_ssf);
4683*7c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
4684*7c478bd9Sstevel@tonic-gate 		return result;
4685*7c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
4686*7c478bd9Sstevel@tonic-gate 	return SASL_OK;
4687*7c478bd9Sstevel@tonic-gate }
4688*7c478bd9Sstevel@tonic-gate #endif /* SASL */
4689