xref: /freebsd/contrib/sendmail/src/usersmtp.c (revision d39bd2c1388b520fcba9abed1932acacead60fba)
1c2aa98e2SPeter Wemm /*
2da7d7b9cSGregory Neil Shapiro  * Copyright (c) 1998-2006, 2008-2010, 2014 Proofpoint, Inc. and its suppliers.
306f25ae9SGregory Neil Shapiro  *	All rights reserved.
4c2aa98e2SPeter Wemm  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5c2aa98e2SPeter Wemm  * Copyright (c) 1988, 1993
6c2aa98e2SPeter Wemm  *	The Regents of the University of California.  All rights reserved.
7c2aa98e2SPeter Wemm  *
8c2aa98e2SPeter Wemm  * By using this file, you agree to the terms and conditions set
9c2aa98e2SPeter Wemm  * forth in the LICENSE file which can be found at the top level of
10c2aa98e2SPeter Wemm  * the sendmail distribution.
11c2aa98e2SPeter Wemm  *
12c2aa98e2SPeter Wemm  */
13c2aa98e2SPeter Wemm 
1406f25ae9SGregory Neil Shapiro #include <sendmail.h>
15c2aa98e2SPeter Wemm 
164313cc83SGregory Neil Shapiro SM_RCSID("@(#)$Id: usersmtp.c,v 8.488 2013-11-22 20:51:57 ca Exp $")
17c2aa98e2SPeter Wemm 
182fb4f839SGregory Neil Shapiro #include <sm/sendmail.h>
19c2aa98e2SPeter Wemm 
2006f25ae9SGregory Neil Shapiro static void	esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
2106f25ae9SGregory Neil Shapiro static void	helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
2240266059SGregory Neil Shapiro static int	smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
2340266059SGregory Neil Shapiro 
2440266059SGregory Neil Shapiro #if SASL
2540266059SGregory Neil Shapiro extern void	*sm_sasl_malloc __P((unsigned long));
2640266059SGregory Neil Shapiro extern void	sm_sasl_free __P((void *));
275b0945b5SGregory Neil Shapiro #endif
2806f25ae9SGregory Neil Shapiro 
29c2aa98e2SPeter Wemm /*
30c2aa98e2SPeter Wemm **  USERSMTP -- run SMTP protocol from the user end.
31c2aa98e2SPeter Wemm **
32c2aa98e2SPeter Wemm **	This protocol is described in RFC821.
33c2aa98e2SPeter Wemm */
34c2aa98e2SPeter Wemm 
35c2aa98e2SPeter Wemm #define SMTPCLOSING	421			/* "Service Shutting Down" */
36c2aa98e2SPeter Wemm 
3740266059SGregory Neil Shapiro #define ENHSCN(e, d)	((e) == NULL ? (d) : (e))
3840266059SGregory Neil Shapiro 
3940266059SGregory Neil Shapiro #define ENHSCN_RPOOL(e, d, rpool) \
4040266059SGregory Neil Shapiro 	((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
41c2aa98e2SPeter Wemm 
4206f25ae9SGregory Neil Shapiro static char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
4306f25ae9SGregory Neil Shapiro static char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
4406f25ae9SGregory Neil Shapiro static bool	SmtpNeedIntro;		/* need "while talking" in transcript */
452fb4f839SGregory Neil Shapiro 
462fb4f839SGregory Neil Shapiro /*
472fb4f839SGregory Neil Shapiro **  SMTPCCLRSE -- clear session related data in envelope
482fb4f839SGregory Neil Shapiro **
492fb4f839SGregory Neil Shapiro **	Parameters:
502fb4f839SGregory Neil Shapiro **		e -- the envelope.
512fb4f839SGregory Neil Shapiro **
522fb4f839SGregory Neil Shapiro **	Returns:
532fb4f839SGregory Neil Shapiro **		none.
542fb4f839SGregory Neil Shapiro */
552fb4f839SGregory Neil Shapiro 
562fb4f839SGregory Neil Shapiro void
smtpclrse(e)572fb4f839SGregory Neil Shapiro smtpclrse(e)
582fb4f839SGregory Neil Shapiro 	ENVELOPE *e;
592fb4f839SGregory Neil Shapiro {
602fb4f839SGregory Neil Shapiro 	SmtpError[0] = '\0';
612fb4f839SGregory Neil Shapiro 	e->e_rcode = 0;
622fb4f839SGregory Neil Shapiro 	e->e_renhsc[0] = '\0';
632fb4f839SGregory Neil Shapiro 	e->e_text = NULL;
64*d39bd2c1SGregory Neil Shapiro #if _FFR_LOG_STAGE
65*d39bd2c1SGregory Neil Shapiro 	e->e_estate = -1;
66*d39bd2c1SGregory Neil Shapiro #endif
672fb4f839SGregory Neil Shapiro 
682fb4f839SGregory Neil Shapiro 	/*
692fb4f839SGregory Neil Shapiro 	**  Reset to avoid access to potentially dangling pointer
702fb4f839SGregory Neil Shapiro 	**  via macvalue().
712fb4f839SGregory Neil Shapiro 	*/
722fb4f839SGregory Neil Shapiro 
732fb4f839SGregory Neil Shapiro 	e->e_mci = NULL;
742fb4f839SGregory Neil Shapiro }
752fb4f839SGregory Neil Shapiro 
7640266059SGregory Neil Shapiro /*
77c2aa98e2SPeter Wemm **  SMTPINIT -- initialize SMTP.
78c2aa98e2SPeter Wemm **
79c2aa98e2SPeter Wemm **	Opens the connection and sends the initial protocol.
80c2aa98e2SPeter Wemm **
81c2aa98e2SPeter Wemm **	Parameters:
82c2aa98e2SPeter Wemm **		m -- mailer to create connection to.
8306f25ae9SGregory Neil Shapiro **		mci -- the mailer connection info.
8406f25ae9SGregory Neil Shapiro **		e -- the envelope.
8506f25ae9SGregory Neil Shapiro **		onlyhelo -- send only helo command?
86c2aa98e2SPeter Wemm **
87c2aa98e2SPeter Wemm **	Returns:
88c2aa98e2SPeter Wemm **		none.
89c2aa98e2SPeter Wemm **
90c2aa98e2SPeter Wemm **	Side Effects:
91c2aa98e2SPeter Wemm **		creates connection and sends initial protocol.
92c2aa98e2SPeter Wemm */
93c2aa98e2SPeter Wemm 
94c2aa98e2SPeter Wemm void
smtpinit(m,mci,e,onlyhelo)9506f25ae9SGregory Neil Shapiro smtpinit(m, mci, e, onlyhelo)
96c2aa98e2SPeter Wemm 	MAILER *m;
97c2aa98e2SPeter Wemm 	register MCI *mci;
98c2aa98e2SPeter Wemm 	ENVELOPE *e;
9906f25ae9SGregory Neil Shapiro 	bool onlyhelo;
100c2aa98e2SPeter Wemm {
101c2aa98e2SPeter Wemm 	register int r;
102605302a5SGregory Neil Shapiro 	int state;
103c2aa98e2SPeter Wemm 	register char *p;
10406f25ae9SGregory Neil Shapiro 	register char *hn;
1055b0945b5SGregory Neil Shapiro #if _FFR_EXPAND_HELONAME
1062fb4f839SGregory Neil Shapiro 	char hnbuf[MAXNAME + 1];	/* EAI:ok:EHLO name must be ASCII */
1075b0945b5SGregory Neil Shapiro #endif
10806f25ae9SGregory Neil Shapiro 	char *enhsc;
109c2aa98e2SPeter Wemm 
11006f25ae9SGregory Neil Shapiro 	enhsc = NULL;
111c2aa98e2SPeter Wemm 	if (tTd(18, 1))
112c2aa98e2SPeter Wemm 	{
11340266059SGregory Neil Shapiro 		sm_dprintf("smtpinit ");
114e92d3f3fSGregory Neil Shapiro 		mci_dump(sm_debug_file(), mci, false);
115c2aa98e2SPeter Wemm 	}
116c2aa98e2SPeter Wemm 
117c2aa98e2SPeter Wemm 	/*
118c2aa98e2SPeter Wemm 	**  Open the connection to the mailer.
119c2aa98e2SPeter Wemm 	*/
120c2aa98e2SPeter Wemm 
121c2aa98e2SPeter Wemm 	SmtpError[0] = '\0';
12227bec481SGregory Neil Shapiro 	SmtpMsgBuffer[0] = '\0';
123c2aa98e2SPeter Wemm 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
124c2aa98e2SPeter Wemm 	if (CurHostName == NULL)
125c2aa98e2SPeter Wemm 		CurHostName = MyHostName;
12640266059SGregory Neil Shapiro 	SmtpNeedIntro = true;
127605302a5SGregory Neil Shapiro 	state = mci->mci_state;
128da7d7b9cSGregory Neil Shapiro 	e->e_rcode = 0;
129da7d7b9cSGregory Neil Shapiro 	e->e_renhsc[0] = '\0';
130da7d7b9cSGregory Neil Shapiro 	e->e_text = NULL;
131*d39bd2c1SGregory Neil Shapiro 	maps_reset_chged("client:smtpinit");
13294c01205SGregory Neil Shapiro 	switch (state)
133c2aa98e2SPeter Wemm 	{
13440266059SGregory Neil Shapiro 	  case MCIS_MAIL:
13540266059SGregory Neil Shapiro 	  case MCIS_RCPT:
13640266059SGregory Neil Shapiro 	  case MCIS_DATA:
137c2aa98e2SPeter Wemm 		/* need to clear old information */
138c2aa98e2SPeter Wemm 		smtprset(m, mci, e);
13906f25ae9SGregory Neil Shapiro 		/* FALLTHROUGH */
140c2aa98e2SPeter Wemm 
141c2aa98e2SPeter Wemm 	  case MCIS_OPEN:
14206f25ae9SGregory Neil Shapiro 		if (!onlyhelo)
143c2aa98e2SPeter Wemm 			return;
14406f25ae9SGregory Neil Shapiro 		break;
145c2aa98e2SPeter Wemm 
146c2aa98e2SPeter Wemm 	  case MCIS_ERROR:
14706f25ae9SGregory Neil Shapiro 	  case MCIS_QUITING:
148c2aa98e2SPeter Wemm 	  case MCIS_SSD:
149c2aa98e2SPeter Wemm 		/* shouldn't happen */
150c2aa98e2SPeter Wemm 		smtpquit(m, mci, e);
15106f25ae9SGregory Neil Shapiro 		/* FALLTHROUGH */
152c2aa98e2SPeter Wemm 
153c2aa98e2SPeter Wemm 	  case MCIS_CLOSED:
154605302a5SGregory Neil Shapiro 		syserr("451 4.4.0 smtpinit: state CLOSED (was %d)", state);
155c2aa98e2SPeter Wemm 		return;
156c2aa98e2SPeter Wemm 
157c2aa98e2SPeter Wemm 	  case MCIS_OPENING:
158c2aa98e2SPeter Wemm 		break;
159c2aa98e2SPeter Wemm 	}
16006f25ae9SGregory Neil Shapiro 	if (onlyhelo)
16106f25ae9SGregory Neil Shapiro 		goto helo;
162c2aa98e2SPeter Wemm 
163c2aa98e2SPeter Wemm 	mci->mci_state = MCIS_OPENING;
16413bd1963SGregory Neil Shapiro 	clrsessenvelope(e);
165c2aa98e2SPeter Wemm 
166c2aa98e2SPeter Wemm 	/*
167c2aa98e2SPeter Wemm 	**  Get the greeting message.
168c2aa98e2SPeter Wemm 	**	This should appear spontaneously.  Give it five minutes to
169c2aa98e2SPeter Wemm 	**	happen.
170c2aa98e2SPeter Wemm 	*/
171c2aa98e2SPeter Wemm 
172c2aa98e2SPeter Wemm 	SmtpPhase = mci->mci_phase = "client greeting";
17340266059SGregory Neil Shapiro 	sm_setproctitle(true, e, "%s %s: %s",
17406f25ae9SGregory Neil Shapiro 			qid_printname(e), CurHostName, mci->mci_phase);
175*d39bd2c1SGregory Neil Shapiro 	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL, XS_GREET,
176*d39bd2c1SGregory Neil Shapiro 		  NULL);
177c2aa98e2SPeter Wemm 	if (r < 0)
178c2aa98e2SPeter Wemm 		goto tempfail1;
179c2aa98e2SPeter Wemm 	if (REPLYTYPE(r) == 4)
180c2aa98e2SPeter Wemm 		goto tempfail2;
181c2aa98e2SPeter Wemm 	if (REPLYTYPE(r) != 2)
182c2aa98e2SPeter Wemm 		goto unavailable;
183c2aa98e2SPeter Wemm 
184c2aa98e2SPeter Wemm 	/*
185c2aa98e2SPeter Wemm 	**  Send the HELO command.
186c2aa98e2SPeter Wemm 	**	My mother taught me to always introduce myself.
187c2aa98e2SPeter Wemm 	*/
188c2aa98e2SPeter Wemm 
18906f25ae9SGregory Neil Shapiro helo:
190c2aa98e2SPeter Wemm 	if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
191c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_ESMTP;
1925b0945b5SGregory Neil Shapiro 	if (mci->mci_heloname != NULL)
1935b0945b5SGregory Neil Shapiro 	{
1945b0945b5SGregory Neil Shapiro #if _FFR_EXPAND_HELONAME
1955b0945b5SGregory Neil Shapiro 		expand(mci->mci_heloname, hnbuf, sizeof(hnbuf), e);
1965b0945b5SGregory Neil Shapiro 		hn = hnbuf;
1975b0945b5SGregory Neil Shapiro #else
1985b0945b5SGregory Neil Shapiro 		hn = mci->mci_heloname;
1995b0945b5SGregory Neil Shapiro #endif
2005b0945b5SGregory Neil Shapiro 	}
2015b0945b5SGregory Neil Shapiro 	else
2025b0945b5SGregory Neil Shapiro 		hn = MyHostName;
203c2aa98e2SPeter Wemm 
204c2aa98e2SPeter Wemm tryhelo:
20540266059SGregory Neil Shapiro #if _FFR_IGNORE_EXT_ON_HELO
20640266059SGregory Neil Shapiro 	mci->mci_flags &= ~MCIF_HELO;
2075b0945b5SGregory Neil Shapiro #endif
208c2aa98e2SPeter Wemm 	if (bitnset(M_LMTP, m->m_flags))
209c2aa98e2SPeter Wemm 	{
21006f25ae9SGregory Neil Shapiro 		smtpmessage("LHLO %s", m, mci, hn);
211c2aa98e2SPeter Wemm 		SmtpPhase = mci->mci_phase = "client LHLO";
212c2aa98e2SPeter Wemm 	}
21340266059SGregory Neil Shapiro 	else if (bitset(MCIF_ESMTP, mci->mci_flags) &&
21440266059SGregory Neil Shapiro 		 !bitnset(M_FSMTP, m->m_flags))
215c2aa98e2SPeter Wemm 	{
21606f25ae9SGregory Neil Shapiro 		smtpmessage("EHLO %s", m, mci, hn);
217c2aa98e2SPeter Wemm 		SmtpPhase = mci->mci_phase = "client EHLO";
218c2aa98e2SPeter Wemm 	}
219c2aa98e2SPeter Wemm 	else
220c2aa98e2SPeter Wemm 	{
22106f25ae9SGregory Neil Shapiro 		smtpmessage("HELO %s", m, mci, hn);
222c2aa98e2SPeter Wemm 		SmtpPhase = mci->mci_phase = "client HELO";
22340266059SGregory Neil Shapiro #if _FFR_IGNORE_EXT_ON_HELO
22440266059SGregory Neil Shapiro 		mci->mci_flags |= MCIF_HELO;
2255b0945b5SGregory Neil Shapiro #endif
226c2aa98e2SPeter Wemm 	}
22740266059SGregory Neil Shapiro 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
22806f25ae9SGregory Neil Shapiro 			CurHostName, mci->mci_phase);
22940266059SGregory Neil Shapiro 	r = reply(m, mci, e,
23040266059SGregory Neil Shapiro 		  bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo
23140266059SGregory Neil Shapiro 					      : TimeOuts.to_helo,
232*d39bd2c1SGregory Neil Shapiro 		  helo_options, NULL, XS_EHLO, NULL);
233c2aa98e2SPeter Wemm 	if (r < 0)
234c2aa98e2SPeter Wemm 		goto tempfail1;
235c2aa98e2SPeter Wemm 	else if (REPLYTYPE(r) == 5)
236c2aa98e2SPeter Wemm 	{
237c2aa98e2SPeter Wemm 		if (bitset(MCIF_ESMTP, mci->mci_flags) &&
238c2aa98e2SPeter Wemm 		    !bitnset(M_LMTP, m->m_flags))
239c2aa98e2SPeter Wemm 		{
240c2aa98e2SPeter Wemm 			/* try old SMTP instead */
241c2aa98e2SPeter Wemm 			mci->mci_flags &= ~MCIF_ESMTP;
242c2aa98e2SPeter Wemm 			goto tryhelo;
243c2aa98e2SPeter Wemm 		}
244c2aa98e2SPeter Wemm 		goto unavailable;
245c2aa98e2SPeter Wemm 	}
246c2aa98e2SPeter Wemm 	else if (REPLYTYPE(r) != 2)
247c2aa98e2SPeter Wemm 		goto tempfail2;
248c2aa98e2SPeter Wemm 
249c2aa98e2SPeter Wemm 	/*
250c2aa98e2SPeter Wemm 	**  Check to see if we actually ended up talking to ourself.
251c2aa98e2SPeter Wemm 	**  This means we didn't know about an alias or MX, or we managed
252c2aa98e2SPeter Wemm 	**  to connect to an echo server.
253c2aa98e2SPeter Wemm 	*/
254c2aa98e2SPeter Wemm 
255c2aa98e2SPeter Wemm 	p = strchr(&SmtpReplyBuffer[4], ' ');
256c2aa98e2SPeter Wemm 	if (p != NULL)
257c2aa98e2SPeter Wemm 		*p = '\0';
258c2aa98e2SPeter Wemm 	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
259c2aa98e2SPeter Wemm 	    !bitnset(M_LMTP, m->m_flags) &&
2602fb4f839SGregory Neil Shapiro 	    SM_STRCASEEQ(&SmtpReplyBuffer[4], MyHostName))
261c2aa98e2SPeter Wemm 	{
26206f25ae9SGregory Neil Shapiro 		syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
263c2aa98e2SPeter Wemm 			CurHostName);
2648774250cSGregory Neil Shapiro 		mci_setstat(mci, EX_CONFIG, "5.3.5",
2658774250cSGregory Neil Shapiro 			    "553 5.3.5 system config error");
266c2aa98e2SPeter Wemm 		mci->mci_errno = 0;
267c2aa98e2SPeter Wemm 		smtpquit(m, mci, e);
268c2aa98e2SPeter Wemm 		return;
269c2aa98e2SPeter Wemm 	}
270c2aa98e2SPeter Wemm 
271c2aa98e2SPeter Wemm 	/*
272c2aa98e2SPeter Wemm 	**  If this is expected to be another sendmail, send some internal
273c2aa98e2SPeter Wemm 	**  commands.
274e92d3f3fSGregory Neil Shapiro 	**  If we're running as MSP, "propagate" -v flag if possible.
275c2aa98e2SPeter Wemm 	*/
276c2aa98e2SPeter Wemm 
277e92d3f3fSGregory Neil Shapiro 	if ((UseMSP && Verbose && bitset(MCIF_VERB, mci->mci_flags))
278da7d7b9cSGregory Neil Shapiro 	    || bitnset(M_INTERNAL, m->m_flags))
279c2aa98e2SPeter Wemm 	{
280c2aa98e2SPeter Wemm 		/* tell it to be verbose */
281c2aa98e2SPeter Wemm 		smtpmessage("VERB", m, mci);
282e92d3f3fSGregory Neil Shapiro 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc,
283*d39bd2c1SGregory Neil Shapiro 			XS_DEFAULT, NULL);
284c2aa98e2SPeter Wemm 		if (r < 0)
285c2aa98e2SPeter Wemm 			goto tempfail1;
286c2aa98e2SPeter Wemm 	}
287c2aa98e2SPeter Wemm 
288c2aa98e2SPeter Wemm 	if (mci->mci_state != MCIS_CLOSED)
289c2aa98e2SPeter Wemm 	{
290c2aa98e2SPeter Wemm 		mci->mci_state = MCIS_OPEN;
291c2aa98e2SPeter Wemm 		return;
292c2aa98e2SPeter Wemm 	}
293c2aa98e2SPeter Wemm 
294c2aa98e2SPeter Wemm 	/* got a 421 error code during startup */
295c2aa98e2SPeter Wemm 
296c2aa98e2SPeter Wemm   tempfail1:
29706f25ae9SGregory Neil Shapiro 	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
298c2aa98e2SPeter Wemm 	if (mci->mci_state != MCIS_CLOSED)
299c2aa98e2SPeter Wemm 		smtpquit(m, mci, e);
300c2aa98e2SPeter Wemm 	return;
301c2aa98e2SPeter Wemm 
302c2aa98e2SPeter Wemm   tempfail2:
303c2aa98e2SPeter Wemm 	/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
30406f25ae9SGregory Neil Shapiro 	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
30506f25ae9SGregory Neil Shapiro 		    SmtpReplyBuffer);
306c2aa98e2SPeter Wemm 	if (mci->mci_state != MCIS_CLOSED)
307c2aa98e2SPeter Wemm 		smtpquit(m, mci, e);
308c2aa98e2SPeter Wemm 	return;
309c2aa98e2SPeter Wemm 
310c2aa98e2SPeter Wemm   unavailable:
311c2aa98e2SPeter Wemm 	mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
312c2aa98e2SPeter Wemm 	smtpquit(m, mci, e);
313c2aa98e2SPeter Wemm 	return;
314c2aa98e2SPeter Wemm }
31540266059SGregory Neil Shapiro /*
316c2aa98e2SPeter Wemm **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
317c2aa98e2SPeter Wemm **
318c2aa98e2SPeter Wemm **	Parameters:
319c2aa98e2SPeter Wemm **		line -- the response line.
320c2aa98e2SPeter Wemm **		firstline -- set if this is the first line of the reply.
321c2aa98e2SPeter Wemm **		m -- the mailer.
322c2aa98e2SPeter Wemm **		mci -- the mailer connection info.
323c2aa98e2SPeter Wemm **		e -- the envelope.
324c2aa98e2SPeter Wemm **
325c2aa98e2SPeter Wemm **	Returns:
326c2aa98e2SPeter Wemm **		none.
327c2aa98e2SPeter Wemm */
328c2aa98e2SPeter Wemm 
32906f25ae9SGregory Neil Shapiro static void
esmtp_check(line,firstline,m,mci,e)330c2aa98e2SPeter Wemm esmtp_check(line, firstline, m, mci, e)
331c2aa98e2SPeter Wemm 	char *line;
332c2aa98e2SPeter Wemm 	bool firstline;
333c2aa98e2SPeter Wemm 	MAILER *m;
334c2aa98e2SPeter Wemm 	register MCI *mci;
335c2aa98e2SPeter Wemm 	ENVELOPE *e;
336c2aa98e2SPeter Wemm {
337c2aa98e2SPeter Wemm 	if (strstr(line, "ESMTP") != NULL)
338c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_ESMTP;
33940266059SGregory Neil Shapiro 
34040266059SGregory Neil Shapiro 	/*
34140266059SGregory Neil Shapiro 	**  Dirty hack below. Quoting the author:
34240266059SGregory Neil Shapiro 	**  This was a response to people who wanted SMTP transmission to be
34340266059SGregory Neil Shapiro 	**  just-send-8 by default.  Essentially, you could put this tag into
34440266059SGregory Neil Shapiro 	**  your greeting message to behave as though the F=8 flag was set on
34540266059SGregory Neil Shapiro 	**  the mailer.
34640266059SGregory Neil Shapiro 	*/
34740266059SGregory Neil Shapiro 
348c2aa98e2SPeter Wemm 	if (strstr(line, "8BIT-OK") != NULL)
349c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_8BITOK;
350c2aa98e2SPeter Wemm }
35140266059SGregory Neil Shapiro 
35206f25ae9SGregory Neil Shapiro #if SASL
35340266059SGregory Neil Shapiro /* specify prototype so compiler can check calls */
35440266059SGregory Neil Shapiro static char *str_union __P((char *, char *, SM_RPOOL_T *));
35540266059SGregory Neil Shapiro 
35640266059SGregory Neil Shapiro /*
35706f25ae9SGregory Neil Shapiro **  STR_UNION -- create the union of two lists
35806f25ae9SGregory Neil Shapiro **
35906f25ae9SGregory Neil Shapiro **	Parameters:
36006f25ae9SGregory Neil Shapiro **		s1, s2 -- lists of items (separated by single blanks).
36140266059SGregory Neil Shapiro **		rpool -- resource pool from which result is allocated.
36206f25ae9SGregory Neil Shapiro **
36306f25ae9SGregory Neil Shapiro **	Returns:
36406f25ae9SGregory Neil Shapiro **		the union of both lists.
36506f25ae9SGregory Neil Shapiro */
36606f25ae9SGregory Neil Shapiro 
36706f25ae9SGregory Neil Shapiro static char *
str_union(s1,s2,rpool)36840266059SGregory Neil Shapiro str_union(s1, s2, rpool)
36906f25ae9SGregory Neil Shapiro 	char *s1, *s2;
37040266059SGregory Neil Shapiro 	SM_RPOOL_T *rpool;
37106f25ae9SGregory Neil Shapiro {
37206f25ae9SGregory Neil Shapiro 	char *hr, *h1, *h, *res;
37306f25ae9SGregory Neil Shapiro 	int l1, l2, rl;
37406f25ae9SGregory Neil Shapiro 
3752fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(s1))
37606f25ae9SGregory Neil Shapiro 		return s2;
3772fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(s2))
37806f25ae9SGregory Neil Shapiro 		return s1;
37906f25ae9SGregory Neil Shapiro 	l1 = strlen(s1);
38006f25ae9SGregory Neil Shapiro 	l2 = strlen(s2);
38106f25ae9SGregory Neil Shapiro 	rl = l1 + l2;
3826f9c8e5bSGregory Neil Shapiro 	if (rl <= 0)
3836f9c8e5bSGregory Neil Shapiro 	{
3846f9c8e5bSGregory Neil Shapiro 		sm_syslog(LOG_WARNING, NOQID,
3856f9c8e5bSGregory Neil Shapiro 			  "str_union: stringlen1=%d, stringlen2=%d, sum=%d, status=overflow",
3866f9c8e5bSGregory Neil Shapiro 			  l1, l2, rl);
3876f9c8e5bSGregory Neil Shapiro 		res = NULL;
3886f9c8e5bSGregory Neil Shapiro 	}
3896f9c8e5bSGregory Neil Shapiro 	else
39040266059SGregory Neil Shapiro 		res = (char *) sm_rpool_malloc(rpool, rl + 2);
39140266059SGregory Neil Shapiro 	if (res == NULL)
39240266059SGregory Neil Shapiro 	{
39340266059SGregory Neil Shapiro 		if (l1 > l2)
39440266059SGregory Neil Shapiro 			return s1;
39540266059SGregory Neil Shapiro 		return s2;
39640266059SGregory Neil Shapiro 	}
39740266059SGregory Neil Shapiro 	(void) sm_strlcpy(res, s1, rl);
3988774250cSGregory Neil Shapiro 	hr = res + l1;
39906f25ae9SGregory Neil Shapiro 	h1 = s2;
40006f25ae9SGregory Neil Shapiro 	h = s2;
40106f25ae9SGregory Neil Shapiro 
40206f25ae9SGregory Neil Shapiro 	/* walk through s2 */
40306f25ae9SGregory Neil Shapiro 	while (h != NULL && *h1 != '\0')
40406f25ae9SGregory Neil Shapiro 	{
40506f25ae9SGregory Neil Shapiro 		/* is there something after the current word? */
40606f25ae9SGregory Neil Shapiro 		if ((h = strchr(h1, ' ')) != NULL)
40706f25ae9SGregory Neil Shapiro 			*h = '\0';
40806f25ae9SGregory Neil Shapiro 		l1 = strlen(h1);
40906f25ae9SGregory Neil Shapiro 
41006f25ae9SGregory Neil Shapiro 		/* does the current word appear in s1 ? */
41106f25ae9SGregory Neil Shapiro 		if (iteminlist(h1, s1, " ") == NULL)
41206f25ae9SGregory Neil Shapiro 		{
41306f25ae9SGregory Neil Shapiro 			/* add space as delimiter */
41406f25ae9SGregory Neil Shapiro 			*hr++ = ' ';
41506f25ae9SGregory Neil Shapiro 
41606f25ae9SGregory Neil Shapiro 			/* copy the item */
41706f25ae9SGregory Neil Shapiro 			memcpy(hr, h1, l1);
41806f25ae9SGregory Neil Shapiro 
41906f25ae9SGregory Neil Shapiro 			/* advance pointer in result list */
42006f25ae9SGregory Neil Shapiro 			hr += l1;
42106f25ae9SGregory Neil Shapiro 			*hr = '\0';
42206f25ae9SGregory Neil Shapiro 		}
42306f25ae9SGregory Neil Shapiro 		if (h != NULL)
42406f25ae9SGregory Neil Shapiro 		{
42506f25ae9SGregory Neil Shapiro 			/* there are more items */
42606f25ae9SGregory Neil Shapiro 			*h = ' ';
42706f25ae9SGregory Neil Shapiro 			h1 = h + 1;
42806f25ae9SGregory Neil Shapiro 		}
42906f25ae9SGregory Neil Shapiro 	}
43006f25ae9SGregory Neil Shapiro 	return res;
43106f25ae9SGregory Neil Shapiro }
43206f25ae9SGregory Neil Shapiro #endif /* SASL */
43340266059SGregory Neil Shapiro 
43440266059SGregory Neil Shapiro /*
435c2aa98e2SPeter Wemm **  HELO_OPTIONS -- process the options on a HELO line.
436c2aa98e2SPeter Wemm **
437c2aa98e2SPeter Wemm **	Parameters:
438c2aa98e2SPeter Wemm **		line -- the response line.
439c2aa98e2SPeter Wemm **		firstline -- set if this is the first line of the reply.
440c2aa98e2SPeter Wemm **		m -- the mailer.
441c2aa98e2SPeter Wemm **		mci -- the mailer connection info.
44240266059SGregory Neil Shapiro **		e -- the envelope (unused).
443c2aa98e2SPeter Wemm **
444c2aa98e2SPeter Wemm **	Returns:
445c2aa98e2SPeter Wemm **		none.
446c2aa98e2SPeter Wemm */
447c2aa98e2SPeter Wemm 
44806f25ae9SGregory Neil Shapiro static void
helo_options(line,firstline,m,mci,e)449c2aa98e2SPeter Wemm helo_options(line, firstline, m, mci, e)
450c2aa98e2SPeter Wemm 	char *line;
451c2aa98e2SPeter Wemm 	bool firstline;
452c2aa98e2SPeter Wemm 	MAILER *m;
453c2aa98e2SPeter Wemm 	register MCI *mci;
454c2aa98e2SPeter Wemm 	ENVELOPE *e;
455c2aa98e2SPeter Wemm {
456c2aa98e2SPeter Wemm 	register char *p;
45740266059SGregory Neil Shapiro #if _FFR_IGNORE_EXT_ON_HELO
45840266059SGregory Neil Shapiro 	static bool logged = false;
4595b0945b5SGregory Neil Shapiro #endif
460c2aa98e2SPeter Wemm 
461c2aa98e2SPeter Wemm 	if (firstline)
46206f25ae9SGregory Neil Shapiro 	{
4636f9c8e5bSGregory Neil Shapiro 		mci_clr_extensions(mci);
46440266059SGregory Neil Shapiro #if _FFR_IGNORE_EXT_ON_HELO
46540266059SGregory Neil Shapiro 		logged = false;
4665b0945b5SGregory Neil Shapiro #endif
467c2aa98e2SPeter Wemm 		return;
46806f25ae9SGregory Neil Shapiro 	}
46940266059SGregory Neil Shapiro #if _FFR_IGNORE_EXT_ON_HELO
47040266059SGregory Neil Shapiro 	else if (bitset(MCIF_HELO, mci->mci_flags))
47140266059SGregory Neil Shapiro 	{
47240266059SGregory Neil Shapiro 		if (LogLevel > 8 && !logged)
47340266059SGregory Neil Shapiro 		{
47440266059SGregory Neil Shapiro 			sm_syslog(LOG_WARNING, NOQID,
47540266059SGregory Neil Shapiro 				  "server=%s [%s] returned extensions despite HELO command",
47640266059SGregory Neil Shapiro 				  macvalue(macid("{server_name}"), e),
47740266059SGregory Neil Shapiro 				  macvalue(macid("{server_addr}"), e));
47840266059SGregory Neil Shapiro 			logged = true;
47940266059SGregory Neil Shapiro 		}
48040266059SGregory Neil Shapiro 		return;
48140266059SGregory Neil Shapiro 	}
48240266059SGregory Neil Shapiro #endif /* _FFR_IGNORE_EXT_ON_HELO */
483c2aa98e2SPeter Wemm 
48440266059SGregory Neil Shapiro 	if (strlen(line) < 5)
485c2aa98e2SPeter Wemm 		return;
486c2aa98e2SPeter Wemm 	line += 4;
48706f25ae9SGregory Neil Shapiro 	p = strpbrk(line, " =");
488c2aa98e2SPeter Wemm 	if (p != NULL)
489c2aa98e2SPeter Wemm 		*p++ = '\0';
4902fb4f839SGregory Neil Shapiro 	if (SM_STRCASEEQ(line, "size"))
491c2aa98e2SPeter Wemm 	{
492c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_SIZE;
493c2aa98e2SPeter Wemm 		if (p != NULL)
494c2aa98e2SPeter Wemm 			mci->mci_maxsize = atol(p);
495c2aa98e2SPeter Wemm 	}
4962fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(line, "8bitmime"))
497c2aa98e2SPeter Wemm 	{
498c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_8BITMIME;
499c2aa98e2SPeter Wemm 		mci->mci_flags &= ~MCIF_7BIT;
500c2aa98e2SPeter Wemm 	}
5012fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(line, "expn"))
502c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_EXPN;
5032fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(line, "dsn"))
504c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_DSN;
5052fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(line, "enhancedstatuscodes"))
50606f25ae9SGregory Neil Shapiro 		mci->mci_flags |= MCIF_ENHSTAT;
5072fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(line, "pipelining"))
50840266059SGregory Neil Shapiro 		mci->mci_flags |= MCIF_PIPELINED;
5092fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(line, "verb"))
51013bd1963SGregory Neil Shapiro 		mci->mci_flags |= MCIF_VERB;
5112fb4f839SGregory Neil Shapiro #if USE_EAI
5122fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(line, "smtputf8"))
5135b0945b5SGregory Neil Shapiro 		mci->mci_flags |= MCIF_EAI;
5142fb4f839SGregory Neil Shapiro #endif
51506f25ae9SGregory Neil Shapiro #if STARTTLS
5162fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(line, "starttls"))
51706f25ae9SGregory Neil Shapiro 		mci->mci_flags |= MCIF_TLS;
5185b0945b5SGregory Neil Shapiro #endif
5192fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(line, "deliverby"))
52040266059SGregory Neil Shapiro 	{
52140266059SGregory Neil Shapiro 		mci->mci_flags |= MCIF_DLVR_BY;
52240266059SGregory Neil Shapiro 		if (p != NULL)
52340266059SGregory Neil Shapiro 			mci->mci_min_by = atol(p);
52440266059SGregory Neil Shapiro 	}
52506f25ae9SGregory Neil Shapiro #if SASL
5262fb4f839SGregory Neil Shapiro 	else if (SM_STRCASEEQ(line, "auth"))
52706f25ae9SGregory Neil Shapiro 	{
5286f9c8e5bSGregory Neil Shapiro 		if (p != NULL && *p != '\0' &&
5296f9c8e5bSGregory Neil Shapiro 		    !bitset(MCIF_AUTH2, mci->mci_flags))
53006f25ae9SGregory Neil Shapiro 		{
53106f25ae9SGregory Neil Shapiro 			if (mci->mci_saslcap != NULL)
53206f25ae9SGregory Neil Shapiro 			{
53306f25ae9SGregory Neil Shapiro 				/*
53440266059SGregory Neil Shapiro 				**  Create the union with previous auth
53506f25ae9SGregory Neil Shapiro 				**  offerings because we recognize "auth "
53606f25ae9SGregory Neil Shapiro 				**  and "auth=" (old format).
53706f25ae9SGregory Neil Shapiro 				*/
53840266059SGregory Neil Shapiro 
53940266059SGregory Neil Shapiro 				mci->mci_saslcap = str_union(mci->mci_saslcap,
54040266059SGregory Neil Shapiro 							     p, mci->mci_rpool);
5416f9c8e5bSGregory Neil Shapiro 				mci->mci_flags |= MCIF_AUTH2;
542c2aa98e2SPeter Wemm 			}
54306f25ae9SGregory Neil Shapiro 			else
54406f25ae9SGregory Neil Shapiro 			{
54506f25ae9SGregory Neil Shapiro 				int l;
54606f25ae9SGregory Neil Shapiro 
54706f25ae9SGregory Neil Shapiro 				l = strlen(p) + 1;
54840266059SGregory Neil Shapiro 				mci->mci_saslcap = (char *)
54940266059SGregory Neil Shapiro 					sm_rpool_malloc(mci->mci_rpool, l);
55040266059SGregory Neil Shapiro 				if (mci->mci_saslcap != NULL)
55140266059SGregory Neil Shapiro 				{
55240266059SGregory Neil Shapiro 					(void) sm_strlcpy(mci->mci_saslcap, p,
55340266059SGregory Neil Shapiro 							  l);
55406f25ae9SGregory Neil Shapiro 					mci->mci_flags |= MCIF_AUTH;
55506f25ae9SGregory Neil Shapiro 				}
55606f25ae9SGregory Neil Shapiro 			}
55706f25ae9SGregory Neil Shapiro 		}
5586f9c8e5bSGregory Neil Shapiro 		if (tTd(95, 5))
5596f9c8e5bSGregory Neil Shapiro 			sm_syslog(LOG_DEBUG, NOQID, "AUTH flags=%lx, mechs=%s",
5606f9c8e5bSGregory Neil Shapiro 				mci->mci_flags, mci->mci_saslcap);
56140266059SGregory Neil Shapiro 	}
56206f25ae9SGregory Neil Shapiro #endif /* SASL */
56306f25ae9SGregory Neil Shapiro }
56406f25ae9SGregory Neil Shapiro #if SASL
56506f25ae9SGregory Neil Shapiro 
56640266059SGregory Neil Shapiro static int getsimple	__P((void *, int, const char **, unsigned *));
56740266059SGregory Neil Shapiro static int getsecret	__P((sasl_conn_t *, void *, int, sasl_secret_t **));
56840266059SGregory Neil Shapiro static int saslgetrealm	__P((void *, int, const char **, const char **));
56940266059SGregory Neil Shapiro static int readauth	__P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *));
57040266059SGregory Neil Shapiro static int getauth	__P((MCI *, ENVELOPE *, SASL_AI_T *));
57140266059SGregory Neil Shapiro static char *removemech	__P((char *, char *, SM_RPOOL_T *));
57240266059SGregory Neil Shapiro static int attemptauth	__P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *));
57340266059SGregory Neil Shapiro 
57440266059SGregory Neil Shapiro static sasl_callback_t callbacks[] =
57540266059SGregory Neil Shapiro {
57635954bbaSHajimu UMEMOTO 	{	SASL_CB_GETREALM,	(sasl_callback_ft)&saslgetrealm,	NULL	},
57740266059SGregory Neil Shapiro #define CB_GETREALM_IDX	0
57835954bbaSHajimu UMEMOTO 	{	SASL_CB_PASS,		(sasl_callback_ft)&getsecret,	NULL	},
57940266059SGregory Neil Shapiro #define CB_PASS_IDX	1
58035954bbaSHajimu UMEMOTO 	{	SASL_CB_USER,		(sasl_callback_ft)&getsimple,	NULL	},
58140266059SGregory Neil Shapiro #define CB_USER_IDX	2
58235954bbaSHajimu UMEMOTO 	{	SASL_CB_AUTHNAME,	(sasl_callback_ft)&getsimple,	NULL	},
58340266059SGregory Neil Shapiro #define CB_AUTHNAME_IDX	3
58435954bbaSHajimu UMEMOTO 	{	SASL_CB_VERIFYFILE,	(sasl_callback_ft)&safesaslfile,	NULL	},
58540266059SGregory Neil Shapiro #define CB_SAFESASL_IDX	4
58640266059SGregory Neil Shapiro 	{	SASL_CB_LIST_END,	NULL,		NULL	}
58740266059SGregory Neil Shapiro };
58840266059SGregory Neil Shapiro 
58940266059SGregory Neil Shapiro /*
59040266059SGregory Neil Shapiro **  INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
59140266059SGregory Neil Shapiro **
59240266059SGregory Neil Shapiro **	Parameters:
59340266059SGregory Neil Shapiro **		none.
59440266059SGregory Neil Shapiro **
59540266059SGregory Neil Shapiro **	Returns:
59640266059SGregory Neil Shapiro **		SASL_OK -- if successful.
59740266059SGregory Neil Shapiro **		SASL error code -- otherwise.
59840266059SGregory Neil Shapiro **
59940266059SGregory Neil Shapiro **	Side Effects:
60040266059SGregory Neil Shapiro **		checks/sets sasl_clt_init.
601d0cef73dSGregory Neil Shapiro **
602d0cef73dSGregory Neil Shapiro **	Note:
603d0cef73dSGregory Neil Shapiro **	Callbacks are ignored if sasl_client_init() has
604d0cef73dSGregory Neil Shapiro **	been called before (by a library such as libnss_ldap)
60540266059SGregory Neil Shapiro */
60640266059SGregory Neil Shapiro 
60740266059SGregory Neil Shapiro static bool sasl_clt_init = false;
60840266059SGregory Neil Shapiro 
60940266059SGregory Neil Shapiro static int
init_sasl_client()61040266059SGregory Neil Shapiro init_sasl_client()
61140266059SGregory Neil Shapiro {
61240266059SGregory Neil Shapiro 	int result;
61340266059SGregory Neil Shapiro 
61440266059SGregory Neil Shapiro 	if (sasl_clt_init)
61540266059SGregory Neil Shapiro 		return SASL_OK;
61640266059SGregory Neil Shapiro 	result = sasl_client_init(callbacks);
61740266059SGregory Neil Shapiro 
61840266059SGregory Neil Shapiro 	/* should we retry later again or just remember that it failed? */
61940266059SGregory Neil Shapiro 	if (result == SASL_OK)
62040266059SGregory Neil Shapiro 		sasl_clt_init = true;
62140266059SGregory Neil Shapiro 	return result;
62240266059SGregory Neil Shapiro }
62340266059SGregory Neil Shapiro /*
62440266059SGregory Neil Shapiro **  STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
62540266059SGregory Neil Shapiro **
62640266059SGregory Neil Shapiro **	Parameters:
62740266059SGregory Neil Shapiro **		none.
62840266059SGregory Neil Shapiro **
62940266059SGregory Neil Shapiro **	Returns:
63040266059SGregory Neil Shapiro **		none.
63140266059SGregory Neil Shapiro **
63240266059SGregory Neil Shapiro **	Side Effects:
63340266059SGregory Neil Shapiro **		checks/sets sasl_clt_init.
63440266059SGregory Neil Shapiro */
63540266059SGregory Neil Shapiro 
63640266059SGregory Neil Shapiro void
stop_sasl_client()63740266059SGregory Neil Shapiro stop_sasl_client()
63840266059SGregory Neil Shapiro {
63940266059SGregory Neil Shapiro 	if (!sasl_clt_init)
64040266059SGregory Neil Shapiro 		return;
64140266059SGregory Neil Shapiro 	sasl_clt_init = false;
64240266059SGregory Neil Shapiro 	sasl_done();
64340266059SGregory Neil Shapiro }
64440266059SGregory Neil Shapiro /*
64506f25ae9SGregory Neil Shapiro **  GETSASLDATA -- process the challenges from the SASL protocol
64606f25ae9SGregory Neil Shapiro **
64706f25ae9SGregory Neil Shapiro **	This gets the relevant sasl response data out of the reply
64840266059SGregory Neil Shapiro **	from the server.
64906f25ae9SGregory Neil Shapiro **
65006f25ae9SGregory Neil Shapiro **	Parameters:
65106f25ae9SGregory Neil Shapiro **		line -- the response line.
65206f25ae9SGregory Neil Shapiro **		firstline -- set if this is the first line of the reply.
65306f25ae9SGregory Neil Shapiro **		m -- the mailer.
65406f25ae9SGregory Neil Shapiro **		mci -- the mailer connection info.
65540266059SGregory Neil Shapiro **		e -- the envelope (unused).
65606f25ae9SGregory Neil Shapiro **
65706f25ae9SGregory Neil Shapiro **	Returns:
65806f25ae9SGregory Neil Shapiro **		none.
65906f25ae9SGregory Neil Shapiro */
66006f25ae9SGregory Neil Shapiro 
66140266059SGregory Neil Shapiro static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
66240266059SGregory Neil Shapiro 
66340266059SGregory Neil Shapiro static void
getsasldata(line,firstline,m,mci,e)66406f25ae9SGregory Neil Shapiro getsasldata(line, firstline, m, mci, e)
66506f25ae9SGregory Neil Shapiro 	char *line;
66606f25ae9SGregory Neil Shapiro 	bool firstline;
66706f25ae9SGregory Neil Shapiro 	MAILER *m;
66806f25ae9SGregory Neil Shapiro 	register MCI *mci;
66906f25ae9SGregory Neil Shapiro 	ENVELOPE *e;
67006f25ae9SGregory Neil Shapiro {
67106f25ae9SGregory Neil Shapiro 	int len;
67206f25ae9SGregory Neil Shapiro 	int result;
67394c01205SGregory Neil Shapiro # if SASL < 20000
67440266059SGregory Neil Shapiro 	char *out;
6755b0945b5SGregory Neil Shapiro # endif
67606f25ae9SGregory Neil Shapiro 
67706f25ae9SGregory Neil Shapiro 	/* if not a continue we don't care about it */
67840266059SGregory Neil Shapiro 	len = strlen(line);
67940266059SGregory Neil Shapiro 	if ((len <= 4) ||
68006f25ae9SGregory Neil Shapiro 	    (line[0] != '3') ||
68140266059SGregory Neil Shapiro 	     !isascii(line[1]) || !isdigit(line[1]) ||
68240266059SGregory Neil Shapiro 	     !isascii(line[2]) || !isdigit(line[2]))
68306f25ae9SGregory Neil Shapiro 	{
6845b0945b5SGregory Neil Shapiro 		SM_FREE(mci->mci_sasl_string);
68506f25ae9SGregory Neil Shapiro 		return;
68606f25ae9SGregory Neil Shapiro 	}
68706f25ae9SGregory Neil Shapiro 
68806f25ae9SGregory Neil Shapiro 	/* forget about "334 " */
68906f25ae9SGregory Neil Shapiro 	line += 4;
69040266059SGregory Neil Shapiro 	len -= 4;
69194c01205SGregory Neil Shapiro # if SASL >= 20000
69294c01205SGregory Neil Shapiro 	/* XXX put this into a macro/function? It's duplicated below */
69394c01205SGregory Neil Shapiro 	if (mci->mci_sasl_string != NULL)
69494c01205SGregory Neil Shapiro 	{
69594c01205SGregory Neil Shapiro 		if (mci->mci_sasl_string_len <= len)
69694c01205SGregory Neil Shapiro 		{
69794c01205SGregory Neil Shapiro 			sm_free(mci->mci_sasl_string); /* XXX */
69894c01205SGregory Neil Shapiro 			mci->mci_sasl_string = xalloc(len + 1);
69994c01205SGregory Neil Shapiro 		}
70094c01205SGregory Neil Shapiro 	}
70194c01205SGregory Neil Shapiro 	else
70294c01205SGregory Neil Shapiro 		mci->mci_sasl_string = xalloc(len + 1);
70306f25ae9SGregory Neil Shapiro 
70494c01205SGregory Neil Shapiro 	result = sasl_decode64(line, len, mci->mci_sasl_string, len + 1,
70594c01205SGregory Neil Shapiro 			       (unsigned int *) &mci->mci_sasl_string_len);
70694c01205SGregory Neil Shapiro 	if (result != SASL_OK)
70794c01205SGregory Neil Shapiro 	{
70894c01205SGregory Neil Shapiro 		mci->mci_sasl_string_len = 0;
70994c01205SGregory Neil Shapiro 		*mci->mci_sasl_string = '\0';
71094c01205SGregory Neil Shapiro 	}
71194c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
71240266059SGregory Neil Shapiro 	out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1);
71340266059SGregory Neil Shapiro 	result = sasl_decode64(line, len, out, (unsigned int *) &len);
71406f25ae9SGregory Neil Shapiro 	if (result != SASL_OK)
71506f25ae9SGregory Neil Shapiro 	{
71606f25ae9SGregory Neil Shapiro 		len = 0;
71706f25ae9SGregory Neil Shapiro 		*out = '\0';
71806f25ae9SGregory Neil Shapiro 	}
71940266059SGregory Neil Shapiro 
72040266059SGregory Neil Shapiro 	/*
72140266059SGregory Neil Shapiro 	**  mci_sasl_string is "shared" with Cyrus-SASL library; hence
72240266059SGregory Neil Shapiro 	**	it can't be in an rpool unless we use the same memory
72340266059SGregory Neil Shapiro 	**	management mechanism (with same rpool!) for Cyrus SASL.
72440266059SGregory Neil Shapiro 	*/
72540266059SGregory Neil Shapiro 
72606f25ae9SGregory Neil Shapiro 	if (mci->mci_sasl_string != NULL)
72706f25ae9SGregory Neil Shapiro 	{
72806f25ae9SGregory Neil Shapiro 		if (mci->mci_sasl_string_len <= len)
72906f25ae9SGregory Neil Shapiro 		{
73040266059SGregory Neil Shapiro 			sm_free(mci->mci_sasl_string); /* XXX */
73106f25ae9SGregory Neil Shapiro 			mci->mci_sasl_string = xalloc(len + 1);
73206f25ae9SGregory Neil Shapiro 		}
73306f25ae9SGregory Neil Shapiro 	}
73406f25ae9SGregory Neil Shapiro 	else
73506f25ae9SGregory Neil Shapiro 		mci->mci_sasl_string = xalloc(len + 1);
73640266059SGregory Neil Shapiro 
73706f25ae9SGregory Neil Shapiro 	memcpy(mci->mci_sasl_string, out, len);
73806f25ae9SGregory Neil Shapiro 	mci->mci_sasl_string[len] = '\0';
73906f25ae9SGregory Neil Shapiro 	mci->mci_sasl_string_len = len;
74094c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
74106f25ae9SGregory Neil Shapiro 	return;
74206f25ae9SGregory Neil Shapiro }
74340266059SGregory Neil Shapiro /*
74440266059SGregory Neil Shapiro **  READAUTH -- read auth values from a file
74506f25ae9SGregory Neil Shapiro **
74606f25ae9SGregory Neil Shapiro **	Parameters:
74706f25ae9SGregory Neil Shapiro **		filename -- name of file to read.
74806f25ae9SGregory Neil Shapiro **		safe -- if set, this is a safe read.
74940266059SGregory Neil Shapiro **		sai -- where to store auth_info.
75040266059SGregory Neil Shapiro **		rpool -- resource pool for sai.
75106f25ae9SGregory Neil Shapiro **
75206f25ae9SGregory Neil Shapiro **	Returns:
7535b0945b5SGregory Neil Shapiro **		EX_OK -- data successfully read.
75440266059SGregory Neil Shapiro **		EX_UNAVAILABLE -- no valid filename.
75540266059SGregory Neil Shapiro **		EX_TEMPFAIL -- temporary failure.
75606f25ae9SGregory Neil Shapiro */
75706f25ae9SGregory Neil Shapiro 
75806f25ae9SGregory Neil Shapiro static char *sasl_info_name[] =
75906f25ae9SGregory Neil Shapiro {
76006f25ae9SGregory Neil Shapiro 	"user id",
76140266059SGregory Neil Shapiro 	"authentication id",
76206f25ae9SGregory Neil Shapiro 	"password",
76306f25ae9SGregory Neil Shapiro 	"realm",
76440266059SGregory Neil Shapiro 	"mechlist"
76506f25ae9SGregory Neil Shapiro };
76640266059SGregory Neil Shapiro static int
readauth(filename,safe,sai,rpool)76740266059SGregory Neil Shapiro readauth(filename, safe, sai, rpool)
76806f25ae9SGregory Neil Shapiro 	char *filename;
76906f25ae9SGregory Neil Shapiro 	bool safe;
77040266059SGregory Neil Shapiro 	SASL_AI_T *sai;
77140266059SGregory Neil Shapiro 	SM_RPOOL_T *rpool;
77206f25ae9SGregory Neil Shapiro {
77340266059SGregory Neil Shapiro 	SM_FILE_T *f;
77406f25ae9SGregory Neil Shapiro 	long sff;
77506f25ae9SGregory Neil Shapiro 	pid_t pid;
77606f25ae9SGregory Neil Shapiro 	int lc;
77740266059SGregory Neil Shapiro 	char *s;
77840266059SGregory Neil Shapiro 	char buf[MAXLINE];
77906f25ae9SGregory Neil Shapiro 
7802fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(filename))
78140266059SGregory Neil Shapiro 		return EX_UNAVAILABLE;
78240266059SGregory Neil Shapiro 
78306f25ae9SGregory Neil Shapiro # if !_FFR_ALLOW_SASLINFO
78406f25ae9SGregory Neil Shapiro 	/*
78506f25ae9SGregory Neil Shapiro 	**  make sure we don't use a program that is not
7865b0945b5SGregory Neil Shapiro 	**  accessible to the user who specified a different authinfo file.
78706f25ae9SGregory Neil Shapiro 	**  However, currently we don't pass this info (authinfo file
78806f25ae9SGregory Neil Shapiro 	**  specified by user) around, so we just turn off program access.
78906f25ae9SGregory Neil Shapiro 	*/
79040266059SGregory Neil Shapiro 
79106f25ae9SGregory Neil Shapiro 	if (filename[0] == '|')
79206f25ae9SGregory Neil Shapiro 	{
79306f25ae9SGregory Neil Shapiro 		auto int fd;
79406f25ae9SGregory Neil Shapiro 		int i;
79506f25ae9SGregory Neil Shapiro 		char *p;
79606f25ae9SGregory Neil Shapiro 		char *argv[MAXPV + 1];
79706f25ae9SGregory Neil Shapiro 
79806f25ae9SGregory Neil Shapiro 		i = 0;
79906f25ae9SGregory Neil Shapiro 		for (p = strtok(&filename[1], " \t"); p != NULL;
80006f25ae9SGregory Neil Shapiro 		     p = strtok(NULL, " \t"))
80106f25ae9SGregory Neil Shapiro 		{
80206f25ae9SGregory Neil Shapiro 			if (i >= MAXPV)
80306f25ae9SGregory Neil Shapiro 				break;
80406f25ae9SGregory Neil Shapiro 			argv[i++] = p;
80506f25ae9SGregory Neil Shapiro 		}
80606f25ae9SGregory Neil Shapiro 		argv[i] = NULL;
80706f25ae9SGregory Neil Shapiro 		pid = prog_open(argv, &fd, CurEnv);
80806f25ae9SGregory Neil Shapiro 		if (pid < 0)
80906f25ae9SGregory Neil Shapiro 			f = NULL;
81006f25ae9SGregory Neil Shapiro 		else
81140266059SGregory Neil Shapiro 			f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
81240266059SGregory Neil Shapiro 				       (void *) &fd, SM_IO_RDONLY, NULL);
81306f25ae9SGregory Neil Shapiro 	}
81406f25ae9SGregory Neil Shapiro 	else
81506f25ae9SGregory Neil Shapiro # endif /* !_FFR_ALLOW_SASLINFO */
8162fb4f839SGregory Neil Shapiro 	/* "else" in #if code above */
81706f25ae9SGregory Neil Shapiro 	{
81806f25ae9SGregory Neil Shapiro 		pid = -1;
81906f25ae9SGregory Neil Shapiro 		sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
82040266059SGregory Neil Shapiro 		      |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES;
82140266059SGregory Neil Shapiro 		if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail))
82240266059SGregory Neil Shapiro 			sff |= SFF_NOGRFILES;
82306f25ae9SGregory Neil Shapiro 		if (DontLockReadFiles)
82406f25ae9SGregory Neil Shapiro 			sff |= SFF_NOLOCK;
82540266059SGregory Neil Shapiro 
82606f25ae9SGregory Neil Shapiro # if _FFR_ALLOW_SASLINFO
82706f25ae9SGregory Neil Shapiro 		/*
82806f25ae9SGregory Neil Shapiro 		**  XXX: make sure we don't read or open files that are not
8295b0945b5SGregory Neil Shapiro 		**  accessible to the user who specified a different authinfo
83006f25ae9SGregory Neil Shapiro 		**  file.
83106f25ae9SGregory Neil Shapiro 		*/
83240266059SGregory Neil Shapiro 
83306f25ae9SGregory Neil Shapiro 		sff |= SFF_MUSTOWN;
83406f25ae9SGregory Neil Shapiro # else /* _FFR_ALLOW_SASLINFO */
83506f25ae9SGregory Neil Shapiro 		if (safe)
83606f25ae9SGregory Neil Shapiro 			sff |= SFF_OPENASROOT;
83706f25ae9SGregory Neil Shapiro # endif /* _FFR_ALLOW_SASLINFO */
83806f25ae9SGregory Neil Shapiro 
83906f25ae9SGregory Neil Shapiro 		f = safefopen(filename, O_RDONLY, 0, sff);
84006f25ae9SGregory Neil Shapiro 	}
84106f25ae9SGregory Neil Shapiro 	if (f == NULL)
84206f25ae9SGregory Neil Shapiro 	{
84340266059SGregory Neil Shapiro 		if (LogLevel > 5)
84440266059SGregory Neil Shapiro 			sm_syslog(LOG_ERR, NOQID,
84540266059SGregory Neil Shapiro 				  "AUTH=client, error: can't open %s: %s",
84640266059SGregory Neil Shapiro 				  filename, sm_errstring(errno));
84740266059SGregory Neil Shapiro 		return EX_TEMPFAIL;
84806f25ae9SGregory Neil Shapiro 	}
84906f25ae9SGregory Neil Shapiro 
85006f25ae9SGregory Neil Shapiro 	lc = 0;
85140266059SGregory Neil Shapiro 	while (lc <= SASL_MECHLIST &&
852552d4955SGregory Neil Shapiro 		sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
85306f25ae9SGregory Neil Shapiro 	{
85406f25ae9SGregory Neil Shapiro 		if (buf[0] != '#')
85540266059SGregory Neil Shapiro 		{
85640266059SGregory Neil Shapiro 			(*sai)[lc] = sm_rpool_strdup_x(rpool, buf);
85740266059SGregory Neil Shapiro 			if ((s = strchr((*sai)[lc], '\n')) != NULL)
85840266059SGregory Neil Shapiro 				*s = '\0';
85906f25ae9SGregory Neil Shapiro 			lc++;
86006f25ae9SGregory Neil Shapiro 		}
86140266059SGregory Neil Shapiro 	}
86206f25ae9SGregory Neil Shapiro 
86340266059SGregory Neil Shapiro 	(void) sm_io_close(f, SM_TIME_DEFAULT);
86406f25ae9SGregory Neil Shapiro 	if (pid > 0)
86506f25ae9SGregory Neil Shapiro 		(void) waitfor(pid);
86640266059SGregory Neil Shapiro 	if (lc < SASL_PASSWORD)
86706f25ae9SGregory Neil Shapiro 	{
86840266059SGregory Neil Shapiro 		if (LogLevel > 8)
86940266059SGregory Neil Shapiro 			sm_syslog(LOG_ERR, NOQID,
87040266059SGregory Neil Shapiro 				  "AUTH=client, error: can't read %s from %s",
87140266059SGregory Neil Shapiro 				  sasl_info_name[lc + 1], filename);
87240266059SGregory Neil Shapiro 		return EX_TEMPFAIL;
87306f25ae9SGregory Neil Shapiro 	}
87440266059SGregory Neil Shapiro 	return EX_OK;
87506f25ae9SGregory Neil Shapiro }
87606f25ae9SGregory Neil Shapiro 
87740266059SGregory Neil Shapiro /*
87840266059SGregory Neil Shapiro **  GETAUTH -- get authinfo from ruleset call
87940266059SGregory Neil Shapiro **
88040266059SGregory Neil Shapiro **	{server_name}, {server_addr} must be set
88140266059SGregory Neil Shapiro **
88240266059SGregory Neil Shapiro **	Parameters:
88340266059SGregory Neil Shapiro **		mci -- the mailer connection structure.
88440266059SGregory Neil Shapiro **		e -- the envelope (including the sender to specify).
88540266059SGregory Neil Shapiro **		sai -- pointer to authinfo (result).
88640266059SGregory Neil Shapiro **
88740266059SGregory Neil Shapiro **	Returns:
8885b0945b5SGregory Neil Shapiro **		EX_OK -- ruleset was successfully called, data may not
88940266059SGregory Neil Shapiro **			be available, sai must be checked.
89040266059SGregory Neil Shapiro **		EX_UNAVAILABLE -- ruleset unavailable (or failed).
89140266059SGregory Neil Shapiro **		EX_TEMPFAIL -- temporary failure (from ruleset).
89240266059SGregory Neil Shapiro **
89340266059SGregory Neil Shapiro **	Side Effects:
89440266059SGregory Neil Shapiro **		Fills in sai if successful.
89540266059SGregory Neil Shapiro */
89606f25ae9SGregory Neil Shapiro 
89740266059SGregory Neil Shapiro static int
getauth(mci,e,sai)89840266059SGregory Neil Shapiro getauth(mci, e, sai)
89940266059SGregory Neil Shapiro 	MCI *mci;
90040266059SGregory Neil Shapiro 	ENVELOPE *e;
90140266059SGregory Neil Shapiro 	SASL_AI_T *sai;
90206f25ae9SGregory Neil Shapiro {
90340266059SGregory Neil Shapiro 	int i, r, l, got, ret;
90440266059SGregory Neil Shapiro 	char **pvp;
90540266059SGregory Neil Shapiro 	char pvpbuf[PSBUFSIZE];
90606f25ae9SGregory Neil Shapiro 
90740266059SGregory Neil Shapiro 	r = rscap("authinfo", macvalue(macid("{server_name}"), e),
90840266059SGregory Neil Shapiro 		   macvalue(macid("{server_addr}"), e), e,
90940266059SGregory Neil Shapiro 		   &pvp, pvpbuf, sizeof(pvpbuf));
91040266059SGregory Neil Shapiro 
91140266059SGregory Neil Shapiro 	if (r != EX_OK)
91240266059SGregory Neil Shapiro 		return EX_UNAVAILABLE;
91340266059SGregory Neil Shapiro 
91440266059SGregory Neil Shapiro 	/* other than expected return value: ok (i.e., no auth) */
91540266059SGregory Neil Shapiro 	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
91640266059SGregory Neil Shapiro 		return EX_OK;
91740266059SGregory Neil Shapiro 	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
91840266059SGregory Neil Shapiro 		return EX_TEMPFAIL;
91940266059SGregory Neil Shapiro 
92040266059SGregory Neil Shapiro 	/*
92140266059SGregory Neil Shapiro 	**  parse the data, put it into sai
92240266059SGregory Neil Shapiro 	**  format: "TDstring" (including the '"' !)
92340266059SGregory Neil Shapiro 	**  where T is a tag: 'U', ...
92440266059SGregory Neil Shapiro 	**  D is a delimiter: ':' or '='
92540266059SGregory Neil Shapiro 	*/
92640266059SGregory Neil Shapiro 
92740266059SGregory Neil Shapiro 	ret = EX_OK;	/* default return value */
92840266059SGregory Neil Shapiro 	i = 0;
92940266059SGregory Neil Shapiro 	got = 0;
93040266059SGregory Neil Shapiro 	while (i < SASL_ENTRIES)
93140266059SGregory Neil Shapiro 	{
93240266059SGregory Neil Shapiro 		if (pvp[i + 1] == NULL)
93340266059SGregory Neil Shapiro 			break;
93440266059SGregory Neil Shapiro 		if (pvp[i + 1][0] != '"')
93540266059SGregory Neil Shapiro 			break;
93640266059SGregory Neil Shapiro 		switch (pvp[i + 1][1])
93740266059SGregory Neil Shapiro 		{
93840266059SGregory Neil Shapiro 		  case 'U':
93940266059SGregory Neil Shapiro 		  case 'u':
94040266059SGregory Neil Shapiro 			r = SASL_USER;
94140266059SGregory Neil Shapiro 			break;
94240266059SGregory Neil Shapiro 		  case 'I':
94340266059SGregory Neil Shapiro 		  case 'i':
94440266059SGregory Neil Shapiro 			r = SASL_AUTHID;
94540266059SGregory Neil Shapiro 			break;
94640266059SGregory Neil Shapiro 		  case 'P':
94740266059SGregory Neil Shapiro 		  case 'p':
94840266059SGregory Neil Shapiro 			r = SASL_PASSWORD;
94940266059SGregory Neil Shapiro 			break;
95040266059SGregory Neil Shapiro 		  case 'R':
95140266059SGregory Neil Shapiro 		  case 'r':
95240266059SGregory Neil Shapiro 			r = SASL_DEFREALM;
95340266059SGregory Neil Shapiro 			break;
95440266059SGregory Neil Shapiro 		  case 'M':
95540266059SGregory Neil Shapiro 		  case 'm':
95640266059SGregory Neil Shapiro 			r = SASL_MECHLIST;
95740266059SGregory Neil Shapiro 			break;
95840266059SGregory Neil Shapiro 		  default:
95940266059SGregory Neil Shapiro 			goto fail;
96040266059SGregory Neil Shapiro 		}
96140266059SGregory Neil Shapiro 		l = strlen(pvp[i + 1]);
96240266059SGregory Neil Shapiro 
96340266059SGregory Neil Shapiro 		/* check syntax */
96440266059SGregory Neil Shapiro 		if (l <= 3 || pvp[i + 1][l - 1] != '"')
96540266059SGregory Neil Shapiro 			goto fail;
96640266059SGregory Neil Shapiro 
96740266059SGregory Neil Shapiro 		/* remove closing quote */
96840266059SGregory Neil Shapiro 		pvp[i + 1][l - 1] = '\0';
96940266059SGregory Neil Shapiro 
97040266059SGregory Neil Shapiro 		/* remove "TD and " */
97140266059SGregory Neil Shapiro 		l -= 4;
97240266059SGregory Neil Shapiro 		(*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1);
97340266059SGregory Neil Shapiro 		if ((*sai)[r] == NULL)
97440266059SGregory Neil Shapiro 			goto tempfail;
97540266059SGregory Neil Shapiro 		if (pvp[i + 1][2] == ':')
97640266059SGregory Neil Shapiro 		{
97740266059SGregory Neil Shapiro 			/* ':text' (just copy) */
97840266059SGregory Neil Shapiro 			(void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1);
97940266059SGregory Neil Shapiro 			got |= 1 << r;
98040266059SGregory Neil Shapiro 		}
98140266059SGregory Neil Shapiro 		else if (pvp[i + 1][2] == '=')
98240266059SGregory Neil Shapiro 		{
98340266059SGregory Neil Shapiro 			unsigned int len;
98440266059SGregory Neil Shapiro 
98540266059SGregory Neil Shapiro 			/* '=base64' (decode) */
98694c01205SGregory Neil Shapiro # if SASL >= 20000
987959366dcSGregory Neil Shapiro 			ret = sasl_decode64(pvp[i + 1] + 3,
98894c01205SGregory Neil Shapiro 					  (unsigned int) l, (*sai)[r],
98994c01205SGregory Neil Shapiro 					  (unsigned int) l + 1, &len);
99094c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
991959366dcSGregory Neil Shapiro 			ret = sasl_decode64(pvp[i + 1] + 3,
99240266059SGregory Neil Shapiro 					  (unsigned int) l, (*sai)[r], &len);
99394c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
994959366dcSGregory Neil Shapiro 			if (ret != SASL_OK)
99540266059SGregory Neil Shapiro 				goto fail;
99640266059SGregory Neil Shapiro 			got |= 1 << r;
99740266059SGregory Neil Shapiro 		}
99840266059SGregory Neil Shapiro 		else
99940266059SGregory Neil Shapiro 			goto fail;
100040266059SGregory Neil Shapiro 		if (tTd(95, 5))
100194c01205SGregory Neil Shapiro 			sm_syslog(LOG_DEBUG, NOQID, "getauth %s=%s",
100240266059SGregory Neil Shapiro 				  sasl_info_name[r], (*sai)[r]);
100340266059SGregory Neil Shapiro 		++i;
100440266059SGregory Neil Shapiro 	}
100540266059SGregory Neil Shapiro 
100640266059SGregory Neil Shapiro 	/* did we get the expected data? */
1007959366dcSGregory Neil Shapiro 	/* XXX: EXTERNAL mechanism only requires (and only uses) SASL_USER */
100840266059SGregory Neil Shapiro 	if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) &&
100940266059SGregory Neil Shapiro 	      bitset(SASL_PASSWORD_BIT, got)))
101040266059SGregory Neil Shapiro 		goto fail;
101140266059SGregory Neil Shapiro 
101240266059SGregory Neil Shapiro 	/* no authid? copy uid */
101340266059SGregory Neil Shapiro 	if (!bitset(SASL_AUTHID_BIT, got))
101440266059SGregory Neil Shapiro 	{
101540266059SGregory Neil Shapiro 		l = strlen((*sai)[SASL_USER]) + 1;
101640266059SGregory Neil Shapiro 		(*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool,
101740266059SGregory Neil Shapiro 							       l + 1);
101840266059SGregory Neil Shapiro 		if ((*sai)[SASL_AUTHID] == NULL)
101940266059SGregory Neil Shapiro 			goto tempfail;
102040266059SGregory Neil Shapiro 		(void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l);
102140266059SGregory Neil Shapiro 	}
102240266059SGregory Neil Shapiro 
102340266059SGregory Neil Shapiro 	/* no uid? copy authid */
102440266059SGregory Neil Shapiro 	if (!bitset(SASL_USER_BIT, got))
102540266059SGregory Neil Shapiro 	{
102640266059SGregory Neil Shapiro 		l = strlen((*sai)[SASL_AUTHID]) + 1;
102740266059SGregory Neil Shapiro 		(*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool,
102840266059SGregory Neil Shapiro 							     l + 1);
102940266059SGregory Neil Shapiro 		if ((*sai)[SASL_USER] == NULL)
103040266059SGregory Neil Shapiro 			goto tempfail;
103140266059SGregory Neil Shapiro 		(void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l);
103240266059SGregory Neil Shapiro 	}
103340266059SGregory Neil Shapiro 	return EX_OK;
103440266059SGregory Neil Shapiro 
103540266059SGregory Neil Shapiro   tempfail:
103640266059SGregory Neil Shapiro 	ret = EX_TEMPFAIL;
103740266059SGregory Neil Shapiro   fail:
103840266059SGregory Neil Shapiro 	if (LogLevel > 8)
103940266059SGregory Neil Shapiro 		sm_syslog(LOG_WARNING, NOQID,
104040266059SGregory Neil Shapiro 			  "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
104140266059SGregory Neil Shapiro 			  macvalue(macid("{server_name}"), e),
104240266059SGregory Neil Shapiro 			  macvalue(macid("{server_addr}"), e),
104340266059SGregory Neil Shapiro 			  ret == EX_TEMPFAIL ? "temp" : "");
104440266059SGregory Neil Shapiro 	for (i = 0; i <= SASL_MECHLIST; i++)
104540266059SGregory Neil Shapiro 		(*sai)[i] = NULL;	/* just clear; rpool */
104640266059SGregory Neil Shapiro 	return ret;
104740266059SGregory Neil Shapiro }
104894c01205SGregory Neil Shapiro 
104994c01205SGregory Neil Shapiro # if SASL >= 20000
105094c01205SGregory Neil Shapiro /*
105194c01205SGregory Neil Shapiro **  GETSIMPLE -- callback to get userid or authid
105294c01205SGregory Neil Shapiro **
105394c01205SGregory Neil Shapiro **	Parameters:
105494c01205SGregory Neil Shapiro **		context -- sai
105594c01205SGregory Neil Shapiro **		id -- what to do
105694c01205SGregory Neil Shapiro **		result -- (pointer to) result
105794c01205SGregory Neil Shapiro **		len -- (pointer to) length of result
105894c01205SGregory Neil Shapiro **
105994c01205SGregory Neil Shapiro **	Returns:
106094c01205SGregory Neil Shapiro **		OK/failure values
106194c01205SGregory Neil Shapiro */
106294c01205SGregory Neil Shapiro 
106394c01205SGregory Neil Shapiro static int
getsimple(context,id,result,len)106494c01205SGregory Neil Shapiro getsimple(context, id, result, len)
106594c01205SGregory Neil Shapiro 	void *context;
106694c01205SGregory Neil Shapiro 	int id;
106794c01205SGregory Neil Shapiro 	const char **result;
106894c01205SGregory Neil Shapiro 	unsigned *len;
106994c01205SGregory Neil Shapiro {
107094c01205SGregory Neil Shapiro 	SASL_AI_T *sai;
107194c01205SGregory Neil Shapiro 
107294c01205SGregory Neil Shapiro 	if (result == NULL || context == NULL)
107394c01205SGregory Neil Shapiro 		return SASL_BADPARAM;
107494c01205SGregory Neil Shapiro 	sai = (SASL_AI_T *) context;
107594c01205SGregory Neil Shapiro 
107694c01205SGregory Neil Shapiro 	switch (id)
107794c01205SGregory Neil Shapiro 	{
107894c01205SGregory Neil Shapiro 	  case SASL_CB_USER:
107994c01205SGregory Neil Shapiro 		*result = (*sai)[SASL_USER];
108094c01205SGregory Neil Shapiro 		if (tTd(95, 5))
108194c01205SGregory Neil Shapiro 			sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
108294c01205SGregory Neil Shapiro 				  *result);
108394c01205SGregory Neil Shapiro 		if (len != NULL)
108494c01205SGregory Neil Shapiro 			*len = *result != NULL ? strlen(*result) : 0;
108594c01205SGregory Neil Shapiro 		break;
108694c01205SGregory Neil Shapiro 
108794c01205SGregory Neil Shapiro 	  case SASL_CB_AUTHNAME:
108894c01205SGregory Neil Shapiro 		*result = (*sai)[SASL_AUTHID];
108994c01205SGregory Neil Shapiro 		if (tTd(95, 5))
109094c01205SGregory Neil Shapiro 			sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
109194c01205SGregory Neil Shapiro 				  *result);
109294c01205SGregory Neil Shapiro 		if (len != NULL)
109394c01205SGregory Neil Shapiro 			*len = *result != NULL ? strlen(*result) : 0;
109494c01205SGregory Neil Shapiro 		break;
109594c01205SGregory Neil Shapiro 
109694c01205SGregory Neil Shapiro 	  case SASL_CB_LANGUAGE:
109794c01205SGregory Neil Shapiro 		*result = NULL;
109894c01205SGregory Neil Shapiro 		if (len != NULL)
109994c01205SGregory Neil Shapiro 			*len = 0;
110094c01205SGregory Neil Shapiro 		break;
110194c01205SGregory Neil Shapiro 
110294c01205SGregory Neil Shapiro 	  default:
110394c01205SGregory Neil Shapiro 		return SASL_BADPARAM;
110494c01205SGregory Neil Shapiro 	}
110594c01205SGregory Neil Shapiro 	return SASL_OK;
110694c01205SGregory Neil Shapiro }
110794c01205SGregory Neil Shapiro /*
110894c01205SGregory Neil Shapiro **  GETSECRET -- callback to get password
110994c01205SGregory Neil Shapiro **
111094c01205SGregory Neil Shapiro **	Parameters:
111194c01205SGregory Neil Shapiro **		conn -- connection information
111294c01205SGregory Neil Shapiro **		context -- sai
111394c01205SGregory Neil Shapiro **		id -- what to do
111494c01205SGregory Neil Shapiro **		psecret -- (pointer to) result
111594c01205SGregory Neil Shapiro **
111694c01205SGregory Neil Shapiro **	Returns:
111794c01205SGregory Neil Shapiro **		OK/failure values
111894c01205SGregory Neil Shapiro */
111994c01205SGregory Neil Shapiro 
112094c01205SGregory Neil Shapiro static int
getsecret(conn,context,id,psecret)112194c01205SGregory Neil Shapiro getsecret(conn, context, id, psecret)
112294c01205SGregory Neil Shapiro 	sasl_conn_t *conn;
112394c01205SGregory Neil Shapiro 	SM_UNUSED(void *context);
112494c01205SGregory Neil Shapiro 	int id;
112594c01205SGregory Neil Shapiro 	sasl_secret_t **psecret;
112694c01205SGregory Neil Shapiro {
112794c01205SGregory Neil Shapiro 	int len;
112894c01205SGregory Neil Shapiro 	char *authpass;
112994c01205SGregory Neil Shapiro 	MCI *mci;
113094c01205SGregory Neil Shapiro 
113194c01205SGregory Neil Shapiro 	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
113294c01205SGregory Neil Shapiro 		return SASL_BADPARAM;
113394c01205SGregory Neil Shapiro 
113494c01205SGregory Neil Shapiro 	mci = (MCI *) context;
113594c01205SGregory Neil Shapiro 	authpass = mci->mci_sai[SASL_PASSWORD];
113694c01205SGregory Neil Shapiro 	len = strlen(authpass);
113794c01205SGregory Neil Shapiro 
113894c01205SGregory Neil Shapiro 	/*
113994c01205SGregory Neil Shapiro 	**  use an rpool because we are responsible for free()ing the secret,
114094c01205SGregory Neil Shapiro 	**  but we can't free() it until after the auth completes
114194c01205SGregory Neil Shapiro 	*/
114294c01205SGregory Neil Shapiro 
114394c01205SGregory Neil Shapiro 	*psecret = (sasl_secret_t *) sm_rpool_malloc(mci->mci_rpool,
114494c01205SGregory Neil Shapiro 						     sizeof(sasl_secret_t) +
114594c01205SGregory Neil Shapiro 						     len + 1);
114694c01205SGregory Neil Shapiro 	if (*psecret == NULL)
114794c01205SGregory Neil Shapiro 		return SASL_FAIL;
1148a7ec597cSGregory Neil Shapiro 	(void) sm_strlcpy((char *) (*psecret)->data, authpass, len + 1);
114994c01205SGregory Neil Shapiro 	(*psecret)->len = (unsigned long) len;
115094c01205SGregory Neil Shapiro 	return SASL_OK;
115194c01205SGregory Neil Shapiro }
115294c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
115340266059SGregory Neil Shapiro /*
115406f25ae9SGregory Neil Shapiro **  GETSIMPLE -- callback to get userid or authid
115506f25ae9SGregory Neil Shapiro **
115606f25ae9SGregory Neil Shapiro **	Parameters:
115740266059SGregory Neil Shapiro **		context -- sai
115806f25ae9SGregory Neil Shapiro **		id -- what to do
115906f25ae9SGregory Neil Shapiro **		result -- (pointer to) result
116006f25ae9SGregory Neil Shapiro **		len -- (pointer to) length of result
116106f25ae9SGregory Neil Shapiro **
116206f25ae9SGregory Neil Shapiro **	Returns:
116306f25ae9SGregory Neil Shapiro **		OK/failure values
116406f25ae9SGregory Neil Shapiro */
116506f25ae9SGregory Neil Shapiro 
116606f25ae9SGregory Neil Shapiro static int
getsimple(context,id,result,len)116706f25ae9SGregory Neil Shapiro getsimple(context, id, result, len)
116840266059SGregory Neil Shapiro 	void *context;
116906f25ae9SGregory Neil Shapiro 	int id;
117006f25ae9SGregory Neil Shapiro 	const char **result;
117106f25ae9SGregory Neil Shapiro 	unsigned *len;
117206f25ae9SGregory Neil Shapiro {
117340266059SGregory Neil Shapiro 	char *h, *s;
117406f25ae9SGregory Neil Shapiro #  if SASL > 10509
117540266059SGregory Neil Shapiro 	bool addrealm;
11765b0945b5SGregory Neil Shapiro #  endif
117740266059SGregory Neil Shapiro 	size_t l;
117840266059SGregory Neil Shapiro 	SASL_AI_T *sai;
117940266059SGregory Neil Shapiro 	char *authid = NULL;
118006f25ae9SGregory Neil Shapiro 
118140266059SGregory Neil Shapiro 	if (result == NULL || context == NULL)
118206f25ae9SGregory Neil Shapiro 		return SASL_BADPARAM;
118340266059SGregory Neil Shapiro 	sai = (SASL_AI_T *) context;
118440266059SGregory Neil Shapiro 
118540266059SGregory Neil Shapiro 	/*
118640266059SGregory Neil Shapiro 	**  Unfortunately it is not clear whether this routine should
118740266059SGregory Neil Shapiro 	**  return a copy of a string or just a pointer to a string.
118840266059SGregory Neil Shapiro 	**  The Cyrus-SASL plugins treat these return values differently, e.g.,
118940266059SGregory Neil Shapiro 	**  plugins/cram.c free()s authid, plugings/digestmd5.c does not.
119040266059SGregory Neil Shapiro 	**  The best solution to this problem is to fix Cyrus-SASL, but it
119140266059SGregory Neil Shapiro 	**  seems there is nobody who creates patches... Hello CMU!?
119240266059SGregory Neil Shapiro 	**  The second best solution is to have flags that tell this routine
119340266059SGregory Neil Shapiro 	**  whether to return an malloc()ed copy.
119440266059SGregory Neil Shapiro 	**  The next best solution is to always return an malloc()ed copy,
119540266059SGregory Neil Shapiro 	**  and suffer from some memory leak, which is ugly for persistent
119640266059SGregory Neil Shapiro 	**  queue runners.
119740266059SGregory Neil Shapiro 	**  For now we go with the last solution...
119840266059SGregory Neil Shapiro 	**  We can't use rpools (which would avoid this particular problem)
119940266059SGregory Neil Shapiro 	**  as explained in sasl.c.
120040266059SGregory Neil Shapiro 	*/
120106f25ae9SGregory Neil Shapiro 
120206f25ae9SGregory Neil Shapiro 	switch (id)
120306f25ae9SGregory Neil Shapiro 	{
120406f25ae9SGregory Neil Shapiro 	  case SASL_CB_USER:
120540266059SGregory Neil Shapiro 		l = strlen((*sai)[SASL_USER]) + 1;
120640266059SGregory Neil Shapiro 		s = sm_sasl_malloc(l);
120740266059SGregory Neil Shapiro 		if (s == NULL)
120806f25ae9SGregory Neil Shapiro 		{
120906f25ae9SGregory Neil Shapiro 			if (len != NULL)
121040266059SGregory Neil Shapiro 				*len = 0;
121140266059SGregory Neil Shapiro 			*result = NULL;
121240266059SGregory Neil Shapiro 			return SASL_NOMEM;
121340266059SGregory Neil Shapiro 		}
121440266059SGregory Neil Shapiro 		(void) sm_strlcpy(s, (*sai)[SASL_USER], l);
121540266059SGregory Neil Shapiro 		*result = s;
121640266059SGregory Neil Shapiro 		if (tTd(95, 5))
121794c01205SGregory Neil Shapiro 			sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
121840266059SGregory Neil Shapiro 				  *result);
121940266059SGregory Neil Shapiro 		if (len != NULL)
122040266059SGregory Neil Shapiro 			*len = *result != NULL ? strlen(*result) : 0;
122106f25ae9SGregory Neil Shapiro 		break;
122206f25ae9SGregory Neil Shapiro 
122306f25ae9SGregory Neil Shapiro 	  case SASL_CB_AUTHNAME:
122440266059SGregory Neil Shapiro 		h = (*sai)[SASL_AUTHID];
122506f25ae9SGregory Neil Shapiro #  if SASL > 10509
122606f25ae9SGregory Neil Shapiro 		/* XXX maybe other mechanisms too?! */
122740266059SGregory Neil Shapiro 		addrealm = (*sai)[SASL_MECH] != NULL &&
12282fb4f839SGregory Neil Shapiro 			   SM_STRCASEEQ((*sai)[SASL_MECH], "cram-md5");
122940266059SGregory Neil Shapiro 
123006f25ae9SGregory Neil Shapiro 		/*
123140266059SGregory Neil Shapiro 		**  Add realm to authentication id unless authid contains
123240266059SGregory Neil Shapiro 		**  '@' (i.e., a realm) or the default realm is empty.
123306f25ae9SGregory Neil Shapiro 		*/
12348774250cSGregory Neil Shapiro 
123540266059SGregory Neil Shapiro 		if (addrealm && h != NULL && strchr(h, '@') == NULL)
123606f25ae9SGregory Neil Shapiro 		{
123740266059SGregory Neil Shapiro 			/* has this been done before? */
123840266059SGregory Neil Shapiro 			if ((*sai)[SASL_ID_REALM] == NULL)
123906f25ae9SGregory Neil Shapiro 			{
124006f25ae9SGregory Neil Shapiro 				char *realm;
124106f25ae9SGregory Neil Shapiro 
124240266059SGregory Neil Shapiro 				realm = (*sai)[SASL_DEFREALM];
124340266059SGregory Neil Shapiro 
124440266059SGregory Neil Shapiro 				/* do not add an empty realm */
124540266059SGregory Neil Shapiro 				if (*realm == '\0')
124640266059SGregory Neil Shapiro 				{
124740266059SGregory Neil Shapiro 					authid = h;
124840266059SGregory Neil Shapiro 					(*sai)[SASL_ID_REALM] = NULL;
124940266059SGregory Neil Shapiro 				}
125040266059SGregory Neil Shapiro 				else
125140266059SGregory Neil Shapiro 				{
125206f25ae9SGregory Neil Shapiro 					l = strlen(h) + strlen(realm) + 2;
125340266059SGregory Neil Shapiro 
125440266059SGregory Neil Shapiro 					/* should use rpool, but from where? */
125540266059SGregory Neil Shapiro 					authid = sm_sasl_malloc(l);
125640266059SGregory Neil Shapiro 					if (authid != NULL)
125740266059SGregory Neil Shapiro 					{
125840266059SGregory Neil Shapiro 						(void) sm_snprintf(authid, l,
125940266059SGregory Neil Shapiro 								  "%s@%s",
126040266059SGregory Neil Shapiro 								   h, realm);
126140266059SGregory Neil Shapiro 						(*sai)[SASL_ID_REALM] = authid;
126240266059SGregory Neil Shapiro 					}
126340266059SGregory Neil Shapiro 					else
126440266059SGregory Neil Shapiro 					{
126540266059SGregory Neil Shapiro 						authid = h;
126640266059SGregory Neil Shapiro 						(*sai)[SASL_ID_REALM] = NULL;
126740266059SGregory Neil Shapiro 					}
126840266059SGregory Neil Shapiro 				}
126940266059SGregory Neil Shapiro 			}
127040266059SGregory Neil Shapiro 			else
127140266059SGregory Neil Shapiro 				authid = (*sai)[SASL_ID_REALM];
127206f25ae9SGregory Neil Shapiro 		}
127306f25ae9SGregory Neil Shapiro 		else
127406f25ae9SGregory Neil Shapiro #  endif /* SASL > 10509 */
127540266059SGregory Neil Shapiro 			authid = h;
127640266059SGregory Neil Shapiro 		l = strlen(authid) + 1;
127740266059SGregory Neil Shapiro 		s = sm_sasl_malloc(l);
127840266059SGregory Neil Shapiro 		if (s == NULL)
127940266059SGregory Neil Shapiro 		{
128040266059SGregory Neil Shapiro 			if (len != NULL)
128140266059SGregory Neil Shapiro 				*len = 0;
128240266059SGregory Neil Shapiro 			*result = NULL;
128340266059SGregory Neil Shapiro 			return SASL_NOMEM;
128406f25ae9SGregory Neil Shapiro 		}
128540266059SGregory Neil Shapiro 		(void) sm_strlcpy(s, authid, l);
128640266059SGregory Neil Shapiro 		*result = s;
128706f25ae9SGregory Neil Shapiro 		if (tTd(95, 5))
128894c01205SGregory Neil Shapiro 			sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
128940266059SGregory Neil Shapiro 				  *result);
129006f25ae9SGregory Neil Shapiro 		if (len != NULL)
129106f25ae9SGregory Neil Shapiro 			*len = authid ? strlen(authid) : 0;
129206f25ae9SGregory Neil Shapiro 		break;
129306f25ae9SGregory Neil Shapiro 
129406f25ae9SGregory Neil Shapiro 	  case SASL_CB_LANGUAGE:
129506f25ae9SGregory Neil Shapiro 		*result = NULL;
129606f25ae9SGregory Neil Shapiro 		if (len != NULL)
129706f25ae9SGregory Neil Shapiro 			*len = 0;
129806f25ae9SGregory Neil Shapiro 		break;
129906f25ae9SGregory Neil Shapiro 
130006f25ae9SGregory Neil Shapiro 	  default:
130106f25ae9SGregory Neil Shapiro 		return SASL_BADPARAM;
130206f25ae9SGregory Neil Shapiro 	}
130306f25ae9SGregory Neil Shapiro 	return SASL_OK;
130406f25ae9SGregory Neil Shapiro }
130540266059SGregory Neil Shapiro /*
130606f25ae9SGregory Neil Shapiro **  GETSECRET -- callback to get password
130706f25ae9SGregory Neil Shapiro **
130806f25ae9SGregory Neil Shapiro **	Parameters:
130906f25ae9SGregory Neil Shapiro **		conn -- connection information
131040266059SGregory Neil Shapiro **		context -- sai
131106f25ae9SGregory Neil Shapiro **		id -- what to do
131206f25ae9SGregory Neil Shapiro **		psecret -- (pointer to) result
131306f25ae9SGregory Neil Shapiro **
131406f25ae9SGregory Neil Shapiro **	Returns:
131506f25ae9SGregory Neil Shapiro **		OK/failure values
131606f25ae9SGregory Neil Shapiro */
131706f25ae9SGregory Neil Shapiro 
131806f25ae9SGregory Neil Shapiro static int
getsecret(conn,context,id,psecret)131906f25ae9SGregory Neil Shapiro getsecret(conn, context, id, psecret)
132006f25ae9SGregory Neil Shapiro 	sasl_conn_t *conn;
132140266059SGregory Neil Shapiro 	SM_UNUSED(void *context);
132206f25ae9SGregory Neil Shapiro 	int id;
132306f25ae9SGregory Neil Shapiro 	sasl_secret_t **psecret;
132406f25ae9SGregory Neil Shapiro {
132506f25ae9SGregory Neil Shapiro 	int len;
132640266059SGregory Neil Shapiro 	char *authpass;
132740266059SGregory Neil Shapiro 	SASL_AI_T *sai;
132806f25ae9SGregory Neil Shapiro 
132906f25ae9SGregory Neil Shapiro 	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
133006f25ae9SGregory Neil Shapiro 		return SASL_BADPARAM;
133106f25ae9SGregory Neil Shapiro 
133240266059SGregory Neil Shapiro 	sai = (SASL_AI_T *) context;
133340266059SGregory Neil Shapiro 	authpass = (*sai)[SASL_PASSWORD];
133406f25ae9SGregory Neil Shapiro 	len = strlen(authpass);
133540266059SGregory Neil Shapiro 	*psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) +
133640266059SGregory Neil Shapiro 						    len + 1);
133740266059SGregory Neil Shapiro 	if (*psecret == NULL)
133840266059SGregory Neil Shapiro 		return SASL_FAIL;
133940266059SGregory Neil Shapiro 	(void) sm_strlcpy((*psecret)->data, authpass, len + 1);
134040266059SGregory Neil Shapiro 	(*psecret)->len = (unsigned long) len;
134106f25ae9SGregory Neil Shapiro 	return SASL_OK;
134206f25ae9SGregory Neil Shapiro }
134394c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
134494c01205SGregory Neil Shapiro 
134540266059SGregory Neil Shapiro /*
134606f25ae9SGregory Neil Shapiro **  SAFESASLFILE -- callback for sasl: is file safe?
134706f25ae9SGregory Neil Shapiro **
134806f25ae9SGregory Neil Shapiro **	Parameters:
134906f25ae9SGregory Neil Shapiro **		context -- pointer to context between invocations (unused)
135006f25ae9SGregory Neil Shapiro **		file -- name of file to check
135106f25ae9SGregory Neil Shapiro **		type -- type of file to check
135206f25ae9SGregory Neil Shapiro **
135306f25ae9SGregory Neil Shapiro **	Returns:
135440266059SGregory Neil Shapiro **		SASL_OK -- file can be used
135540266059SGregory Neil Shapiro **		SASL_CONTINUE -- don't use file
135640266059SGregory Neil Shapiro **		SASL_FAIL -- failure (not used here)
135706f25ae9SGregory Neil Shapiro **
135806f25ae9SGregory Neil Shapiro */
135940266059SGregory Neil Shapiro 
136006f25ae9SGregory Neil Shapiro int
136106f25ae9SGregory Neil Shapiro # if SASL > 10515
safesaslfile(context,file,type)136206f25ae9SGregory Neil Shapiro safesaslfile(context, file, type)
13635b0945b5SGregory Neil Shapiro # else
136406f25ae9SGregory Neil Shapiro safesaslfile(context, file)
13655b0945b5SGregory Neil Shapiro # endif
136606f25ae9SGregory Neil Shapiro 	void *context;
136794c01205SGregory Neil Shapiro # if SASL >= 20000
136894c01205SGregory Neil Shapiro 	const char *file;
13695b0945b5SGregory Neil Shapiro # else
137006f25ae9SGregory Neil Shapiro 	char *file;
13715b0945b5SGregory Neil Shapiro # endif
137206f25ae9SGregory Neil Shapiro # if SASL > 10515
137394c01205SGregory Neil Shapiro #  if SASL >= 20000
137494c01205SGregory Neil Shapiro 	sasl_verify_type_t type;
13755b0945b5SGregory Neil Shapiro #  else
137606f25ae9SGregory Neil Shapiro 	int type;
13775b0945b5SGregory Neil Shapiro #  endif
13785b0945b5SGregory Neil Shapiro # endif
137906f25ae9SGregory Neil Shapiro {
138006f25ae9SGregory Neil Shapiro 	long sff;
138106f25ae9SGregory Neil Shapiro 	int r;
138240266059SGregory Neil Shapiro # if SASL <= 10515
138340266059SGregory Neil Shapiro 	size_t len;
13845b0945b5SGregory Neil Shapiro # endif
138506f25ae9SGregory Neil Shapiro 	char *p;
138606f25ae9SGregory Neil Shapiro 
13872fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(file))
138806f25ae9SGregory Neil Shapiro 		return SASL_OK;
13895b0945b5SGregory Neil Shapiro 	if (tTd(95, 16))
13905b0945b5SGregory Neil Shapiro 		sm_dprintf("safesaslfile=%s\n", file);
139140266059SGregory Neil Shapiro 	sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
139240266059SGregory Neil Shapiro # if SASL <= 10515
139306f25ae9SGregory Neil Shapiro 	if ((p = strrchr(file, '/')) == NULL)
139406f25ae9SGregory Neil Shapiro 		p = file;
139506f25ae9SGregory Neil Shapiro 	else
139606f25ae9SGregory Neil Shapiro 		++p;
139706f25ae9SGregory Neil Shapiro 
139806f25ae9SGregory Neil Shapiro 	/* everything beside libs and .conf files must not be readable */
139940266059SGregory Neil Shapiro 	len = strlen(p);
140040266059SGregory Neil Shapiro 	if ((len <= 3 || strncmp(p, "lib", 3) != 0) &&
140140266059SGregory Neil Shapiro 	    (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0))
140240266059SGregory Neil Shapiro 	{
140340266059SGregory Neil Shapiro 		if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
140406f25ae9SGregory Neil Shapiro 			sff |= SFF_NORFILES;
140540266059SGregory Neil Shapiro 		if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
140640266059SGregory Neil Shapiro 			sff |= SFF_NOGWFILES;
140740266059SGregory Neil Shapiro 	}
140840266059SGregory Neil Shapiro # else /* SASL <= 10515 */
140906f25ae9SGregory Neil Shapiro 	/* files containing passwords should be not readable */
141006f25ae9SGregory Neil Shapiro 	if (type == SASL_VRFY_PASSWD)
141106f25ae9SGregory Neil Shapiro 	{
141240266059SGregory Neil Shapiro 		if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
141306f25ae9SGregory Neil Shapiro 			sff |= SFF_NOWRFILES;
141406f25ae9SGregory Neil Shapiro 		else
141506f25ae9SGregory Neil Shapiro 			sff |= SFF_NORFILES;
141640266059SGregory Neil Shapiro 		if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
141740266059SGregory Neil Shapiro 			sff |= SFF_NOGWFILES;
141806f25ae9SGregory Neil Shapiro 	}
141906f25ae9SGregory Neil Shapiro # endif /* SASL <= 10515 */
142006f25ae9SGregory Neil Shapiro 
142194c01205SGregory Neil Shapiro 	p = (char *) file;
1422602a2b1bSGregory Neil Shapiro 	if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
142306f25ae9SGregory Neil Shapiro 			  S_IRUSR, NULL)) == 0)
142406f25ae9SGregory Neil Shapiro 		return SASL_OK;
142540266059SGregory Neil Shapiro 	if (LogLevel > (r != ENOENT ? 8 : 10))
142606f25ae9SGregory Neil Shapiro 		sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
142740266059SGregory Neil Shapiro 			  p, sm_errstring(r));
142806f25ae9SGregory Neil Shapiro 	return SASL_CONTINUE;
142906f25ae9SGregory Neil Shapiro }
143006f25ae9SGregory Neil Shapiro 
143106f25ae9SGregory Neil Shapiro /*
143206f25ae9SGregory Neil Shapiro **  SASLGETREALM -- return the realm for SASL
143306f25ae9SGregory Neil Shapiro **
143406f25ae9SGregory Neil Shapiro **	return the realm for the client
143506f25ae9SGregory Neil Shapiro **
143606f25ae9SGregory Neil Shapiro **	Parameters:
143706f25ae9SGregory Neil Shapiro **		context -- context shared between invocations
143806f25ae9SGregory Neil Shapiro **		availrealms -- list of available realms
143906f25ae9SGregory Neil Shapiro **			{realm, realm, ...}
144006f25ae9SGregory Neil Shapiro **		result -- pointer to result
144106f25ae9SGregory Neil Shapiro **
144206f25ae9SGregory Neil Shapiro **	Returns:
144306f25ae9SGregory Neil Shapiro **		failure/success
144406f25ae9SGregory Neil Shapiro */
144540266059SGregory Neil Shapiro 
144606f25ae9SGregory Neil Shapiro static int
saslgetrealm(context,id,availrealms,result)144706f25ae9SGregory Neil Shapiro saslgetrealm(context, id, availrealms, result)
144806f25ae9SGregory Neil Shapiro 	void *context;
144906f25ae9SGregory Neil Shapiro 	int id;
145006f25ae9SGregory Neil Shapiro 	const char **availrealms;
145106f25ae9SGregory Neil Shapiro 	const char **result;
145206f25ae9SGregory Neil Shapiro {
145340266059SGregory Neil Shapiro 	char *r;
145440266059SGregory Neil Shapiro 	SASL_AI_T *sai;
145506f25ae9SGregory Neil Shapiro 
145640266059SGregory Neil Shapiro 	sai = (SASL_AI_T *) context;
145740266059SGregory Neil Shapiro 	if (sai == NULL)
145840266059SGregory Neil Shapiro 		return SASL_FAIL;
145940266059SGregory Neil Shapiro 	r = (*sai)[SASL_DEFREALM];
146040266059SGregory Neil Shapiro 
146140266059SGregory Neil Shapiro 	if (LogLevel > 12)
146240266059SGregory Neil Shapiro 		sm_syslog(LOG_INFO, NOQID,
146340266059SGregory Neil Shapiro 			  "AUTH=client, realm=%s, available realms=%s",
146440266059SGregory Neil Shapiro 			  r == NULL ? "<No Realm>" : r,
14652fb4f839SGregory Neil Shapiro 			  (NULL == availrealms || SM_IS_EMPTY(*availrealms))
146640266059SGregory Neil Shapiro 				? "<No Realms>" : *availrealms);
146740266059SGregory Neil Shapiro 
146840266059SGregory Neil Shapiro 	/* check whether context is in list */
146942e5d165SGregory Neil Shapiro 	if (availrealms != NULL && *availrealms != NULL)
147006f25ae9SGregory Neil Shapiro 	{
147106f25ae9SGregory Neil Shapiro 		if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
147206f25ae9SGregory Neil Shapiro 		    NULL)
147306f25ae9SGregory Neil Shapiro 		{
147406f25ae9SGregory Neil Shapiro 			if (LogLevel > 8)
147506f25ae9SGregory Neil Shapiro 				sm_syslog(LOG_ERR, NOQID,
147640266059SGregory Neil Shapiro 					  "AUTH=client, realm=%s not in list=%s",
147740266059SGregory Neil Shapiro 					  r, *availrealms);
147806f25ae9SGregory Neil Shapiro 			return SASL_FAIL;
147906f25ae9SGregory Neil Shapiro 		}
148006f25ae9SGregory Neil Shapiro 	}
148140266059SGregory Neil Shapiro 	*result = r;
148206f25ae9SGregory Neil Shapiro 	return SASL_OK;
148306f25ae9SGregory Neil Shapiro }
148440266059SGregory Neil Shapiro /*
148506f25ae9SGregory Neil Shapiro **  ITEMINLIST -- does item appear in list?
148606f25ae9SGregory Neil Shapiro **
148706f25ae9SGregory Neil Shapiro **	Check whether item appears in list (which must be separated by a
148806f25ae9SGregory Neil Shapiro **	character in delim) as a "word", i.e. it must appear at the begin
148906f25ae9SGregory Neil Shapiro **	of the list or after a space, and it must end with a space or the
149006f25ae9SGregory Neil Shapiro **	end of the list.
149106f25ae9SGregory Neil Shapiro **
149206f25ae9SGregory Neil Shapiro **	Parameters:
149306f25ae9SGregory Neil Shapiro **		item -- item to search.
149406f25ae9SGregory Neil Shapiro **		list -- list of items.
149506f25ae9SGregory Neil Shapiro **		delim -- list of delimiters.
149606f25ae9SGregory Neil Shapiro **
149706f25ae9SGregory Neil Shapiro **	Returns:
149806f25ae9SGregory Neil Shapiro **		pointer to occurrence (NULL if not found).
149906f25ae9SGregory Neil Shapiro */
150006f25ae9SGregory Neil Shapiro 
150106f25ae9SGregory Neil Shapiro char *
iteminlist(item,list,delim)150206f25ae9SGregory Neil Shapiro iteminlist(item, list, delim)
150306f25ae9SGregory Neil Shapiro 	char *item;
150406f25ae9SGregory Neil Shapiro 	char *list;
150506f25ae9SGregory Neil Shapiro 	char *delim;
150606f25ae9SGregory Neil Shapiro {
150706f25ae9SGregory Neil Shapiro 	char *s;
150806f25ae9SGregory Neil Shapiro 	int len;
150906f25ae9SGregory Neil Shapiro 
15102fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(list))
151106f25ae9SGregory Neil Shapiro 		return NULL;
15122fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(item))
151306f25ae9SGregory Neil Shapiro 		return NULL;
151406f25ae9SGregory Neil Shapiro 	s = list;
151506f25ae9SGregory Neil Shapiro 	len = strlen(item);
151606f25ae9SGregory Neil Shapiro 	while (s != NULL && *s != '\0')
151706f25ae9SGregory Neil Shapiro 	{
151840266059SGregory Neil Shapiro 		if (sm_strncasecmp(s, item, len) == 0 &&
151906f25ae9SGregory Neil Shapiro 		    (s[len] == '\0' || strchr(delim, s[len]) != NULL))
152006f25ae9SGregory Neil Shapiro 			return s;
152106f25ae9SGregory Neil Shapiro 		s = strpbrk(s, delim);
152206f25ae9SGregory Neil Shapiro 		if (s != NULL)
152306f25ae9SGregory Neil Shapiro 			while (*++s == ' ')
152406f25ae9SGregory Neil Shapiro 				continue;
152506f25ae9SGregory Neil Shapiro 	}
152606f25ae9SGregory Neil Shapiro 	return NULL;
152706f25ae9SGregory Neil Shapiro }
152840266059SGregory Neil Shapiro /*
152906f25ae9SGregory Neil Shapiro **  REMOVEMECH -- remove item [rem] from list [list]
153006f25ae9SGregory Neil Shapiro **
153106f25ae9SGregory Neil Shapiro **	Parameters:
153206f25ae9SGregory Neil Shapiro **		rem -- item to remove
153306f25ae9SGregory Neil Shapiro **		list -- list of items
153440266059SGregory Neil Shapiro **		rpool -- resource pool from which result is allocated.
153506f25ae9SGregory Neil Shapiro **
153606f25ae9SGregory Neil Shapiro **	Returns:
153706f25ae9SGregory Neil Shapiro **		pointer to new list (NULL in case of error).
153806f25ae9SGregory Neil Shapiro */
153906f25ae9SGregory Neil Shapiro 
154040266059SGregory Neil Shapiro static char *
removemech(rem,list,rpool)154140266059SGregory Neil Shapiro removemech(rem, list, rpool)
154206f25ae9SGregory Neil Shapiro 	char *rem;
154306f25ae9SGregory Neil Shapiro 	char *list;
154440266059SGregory Neil Shapiro 	SM_RPOOL_T *rpool;
154506f25ae9SGregory Neil Shapiro {
154606f25ae9SGregory Neil Shapiro 	char *ret;
154706f25ae9SGregory Neil Shapiro 	char *needle;
154806f25ae9SGregory Neil Shapiro 	int len;
154906f25ae9SGregory Neil Shapiro 
155006f25ae9SGregory Neil Shapiro 	if (list == NULL)
155106f25ae9SGregory Neil Shapiro 		return NULL;
15522fb4f839SGregory Neil Shapiro 	if (SM_IS_EMPTY(rem))
155306f25ae9SGregory Neil Shapiro 	{
155406f25ae9SGregory Neil Shapiro 		/* take out what? */
155506f25ae9SGregory Neil Shapiro 		return NULL;
155606f25ae9SGregory Neil Shapiro 	}
155706f25ae9SGregory Neil Shapiro 
155806f25ae9SGregory Neil Shapiro 	/* find the item in the list */
155906f25ae9SGregory Neil Shapiro 	if ((needle = iteminlist(rem, list, " ")) == NULL)
156006f25ae9SGregory Neil Shapiro 	{
156106f25ae9SGregory Neil Shapiro 		/* not in there: return original */
156206f25ae9SGregory Neil Shapiro 		return list;
156306f25ae9SGregory Neil Shapiro 	}
156406f25ae9SGregory Neil Shapiro 
156506f25ae9SGregory Neil Shapiro 	/* length of string without rem */
156606f25ae9SGregory Neil Shapiro 	len = strlen(list) - strlen(rem);
156740266059SGregory Neil Shapiro 	if (len <= 0)
156806f25ae9SGregory Neil Shapiro 	{
156940266059SGregory Neil Shapiro 		ret = (char *) sm_rpool_malloc_x(rpool, 1);
157006f25ae9SGregory Neil Shapiro 		*ret = '\0';
157106f25ae9SGregory Neil Shapiro 		return ret;
157206f25ae9SGregory Neil Shapiro 	}
157340266059SGregory Neil Shapiro 	ret = (char *) sm_rpool_malloc_x(rpool, len);
157406f25ae9SGregory Neil Shapiro 	memset(ret, '\0', len);
157506f25ae9SGregory Neil Shapiro 
157606f25ae9SGregory Neil Shapiro 	/* copy from start to removed item */
157706f25ae9SGregory Neil Shapiro 	memcpy(ret, list, needle - list);
157806f25ae9SGregory Neil Shapiro 
157906f25ae9SGregory Neil Shapiro 	/* length of rest of string past removed item */
158006f25ae9SGregory Neil Shapiro 	len = strlen(needle) - strlen(rem) - 1;
158106f25ae9SGregory Neil Shapiro 	if (len > 0)
158206f25ae9SGregory Neil Shapiro 	{
158306f25ae9SGregory Neil Shapiro 		/* not last item -- copy into string */
158406f25ae9SGregory Neil Shapiro 		memcpy(ret + (needle - list),
158506f25ae9SGregory Neil Shapiro 		       list + (needle - list) + strlen(rem) + 1,
158606f25ae9SGregory Neil Shapiro 		       len);
158706f25ae9SGregory Neil Shapiro 	}
158806f25ae9SGregory Neil Shapiro 	else
158906f25ae9SGregory Neil Shapiro 		ret[(needle - list) - 1] = '\0';
159006f25ae9SGregory Neil Shapiro 	return ret;
159106f25ae9SGregory Neil Shapiro }
159240266059SGregory Neil Shapiro /*
159306f25ae9SGregory Neil Shapiro **  ATTEMPTAUTH -- try to AUTHenticate using one mechanism
159406f25ae9SGregory Neil Shapiro **
159506f25ae9SGregory Neil Shapiro **	Parameters:
159606f25ae9SGregory Neil Shapiro **		m -- the mailer.
159706f25ae9SGregory Neil Shapiro **		mci -- the mailer connection structure.
159806f25ae9SGregory Neil Shapiro **		e -- the envelope (including the sender to specify).
159940266059SGregory Neil Shapiro **		sai - sasl authinfo
160006f25ae9SGregory Neil Shapiro **
160106f25ae9SGregory Neil Shapiro **	Returns:
160240266059SGregory Neil Shapiro **		EX_OK -- authentication was successful.
160340266059SGregory Neil Shapiro **		EX_NOPERM -- authentication failed.
160440266059SGregory Neil Shapiro **		EX_IOERR -- authentication dialogue failed (I/O problem?).
160540266059SGregory Neil Shapiro **		EX_TEMPFAIL -- temporary failure.
160640266059SGregory Neil Shapiro **
160706f25ae9SGregory Neil Shapiro */
160806f25ae9SGregory Neil Shapiro 
160940266059SGregory Neil Shapiro static int
attemptauth(m,mci,e,sai)161040266059SGregory Neil Shapiro attemptauth(m, mci, e, sai)
161106f25ae9SGregory Neil Shapiro 	MAILER *m;
161206f25ae9SGregory Neil Shapiro 	MCI *mci;
161306f25ae9SGregory Neil Shapiro 	ENVELOPE *e;
161440266059SGregory Neil Shapiro 	SASL_AI_T *sai;
161506f25ae9SGregory Neil Shapiro {
161606f25ae9SGregory Neil Shapiro 	int saslresult, smtpresult;
161794c01205SGregory Neil Shapiro # if SASL >= 20000
161894c01205SGregory Neil Shapiro 	sasl_ssf_t ssf;
161994c01205SGregory Neil Shapiro 	const char *auth_id;
162094c01205SGregory Neil Shapiro 	const char *out;
162194c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
162206f25ae9SGregory Neil Shapiro 	sasl_external_properties_t ssf;
162306f25ae9SGregory Neil Shapiro 	char *out;
162494c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
162506f25ae9SGregory Neil Shapiro 	unsigned int outlen;
162694c01205SGregory Neil Shapiro 	sasl_interact_t *client_interact = NULL;
162740266059SGregory Neil Shapiro 	char *mechusing;
162806f25ae9SGregory Neil Shapiro 	sasl_security_properties_t ssp;
16299bd497b8SGregory Neil Shapiro 
16309bd497b8SGregory Neil Shapiro 	/* MUST NOT be a multiple of 4: bug in some sasl_encode64() versions */
16319bd497b8SGregory Neil Shapiro 	char in64[MAXOUTLEN + 1];
163294c01205SGregory Neil Shapiro # if NETINET || (NETINET6 && SASL >= 20000)
163306f25ae9SGregory Neil Shapiro 	extern SOCKADDR CurHostAddr;
16345b0945b5SGregory Neil Shapiro # endif
163506f25ae9SGregory Neil Shapiro 
163640266059SGregory Neil Shapiro 	/* no mechanism selected (yet) */
163740266059SGregory Neil Shapiro 	(*sai)[SASL_MECH] = NULL;
163806f25ae9SGregory Neil Shapiro 
163940266059SGregory Neil Shapiro 	/* dispose old connection */
164040266059SGregory Neil Shapiro 	if (mci->mci_conn != NULL)
164140266059SGregory Neil Shapiro 		sasl_dispose(&(mci->mci_conn));
164206f25ae9SGregory Neil Shapiro 
164306f25ae9SGregory Neil Shapiro 	/* make a new client sasl connection */
164494c01205SGregory Neil Shapiro # if SASL >= 20000
1645d0cef73dSGregory Neil Shapiro 	/*
1646d0cef73dSGregory Neil Shapiro 	**  We provide the callbacks again because global callbacks in
1647d0cef73dSGregory Neil Shapiro 	**  sasl_client_init() are ignored if SASL has been initialized
1648d0cef73dSGregory Neil Shapiro 	**  before, for example, by a library such as libnss-ldap.
1649d0cef73dSGregory Neil Shapiro 	*/
1650d0cef73dSGregory Neil Shapiro 
165194c01205SGregory Neil Shapiro 	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
165294c01205SGregory Neil Shapiro 								 : "smtp",
1653d0cef73dSGregory Neil Shapiro 				     CurHostName, NULL, NULL, callbacks, 0,
165494c01205SGregory Neil Shapiro 				     &mci->mci_conn);
165594c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
165606f25ae9SGregory Neil Shapiro 	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
165706f25ae9SGregory Neil Shapiro 								 : "smtp",
165806f25ae9SGregory Neil Shapiro 				     CurHostName, NULL, 0, &mci->mci_conn);
165994c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
166040266059SGregory Neil Shapiro 	if (saslresult != SASL_OK)
166140266059SGregory Neil Shapiro 		return EX_TEMPFAIL;
166206f25ae9SGregory Neil Shapiro 
166306f25ae9SGregory Neil Shapiro 	/* set properties */
1664d0cef73dSGregory Neil Shapiro 	(void) memset(&ssp, '\0', sizeof(ssp));
166540266059SGregory Neil Shapiro 
166606f25ae9SGregory Neil Shapiro 	/* XXX should these be options settable via .cf ? */
166740266059SGregory Neil Shapiro 	ssp.max_ssf = MaxSLBits;
166806f25ae9SGregory Neil Shapiro 	ssp.maxbufsize = MAXOUTLEN;
166906f25ae9SGregory Neil Shapiro # if 0
167006f25ae9SGregory Neil Shapiro 	ssp.security_flags = SASL_SEC_NOPLAINTEXT;
16715b0945b5SGregory Neil Shapiro # endif
167206f25ae9SGregory Neil Shapiro 	saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
167306f25ae9SGregory Neil Shapiro 	if (saslresult != SASL_OK)
167406f25ae9SGregory Neil Shapiro 		return EX_TEMPFAIL;
167506f25ae9SGregory Neil Shapiro 
167694c01205SGregory Neil Shapiro # if SASL >= 20000
167794c01205SGregory Neil Shapiro 	/* external security strength factor, authentication id */
167894c01205SGregory Neil Shapiro 	ssf = 0;
167994c01205SGregory Neil Shapiro 	auth_id = NULL;
168094c01205SGregory Neil Shapiro #  if STARTTLS
168194c01205SGregory Neil Shapiro 	out = macvalue(macid("{cert_subject}"), e);
168294c01205SGregory Neil Shapiro 	if (out != NULL && *out != '\0')
168394c01205SGregory Neil Shapiro 		auth_id = out;
168494c01205SGregory Neil Shapiro 	out = macvalue(macid("{cipher_bits}"), e);
168594c01205SGregory Neil Shapiro 	if (out != NULL && *out != '\0')
168694c01205SGregory Neil Shapiro 		ssf = atoi(out);
168794c01205SGregory Neil Shapiro #  endif /* STARTTLS */
168894c01205SGregory Neil Shapiro 	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
168994c01205SGregory Neil Shapiro 	if (saslresult != SASL_OK)
169094c01205SGregory Neil Shapiro 		return EX_TEMPFAIL;
169194c01205SGregory Neil Shapiro 	saslresult = sasl_setprop(mci->mci_conn, SASL_AUTH_EXTERNAL, auth_id);
169294c01205SGregory Neil Shapiro 	if (saslresult != SASL_OK)
169394c01205SGregory Neil Shapiro 		return EX_TEMPFAIL;
169494c01205SGregory Neil Shapiro 
169594c01205SGregory Neil Shapiro #  if NETINET || NETINET6
169694c01205SGregory Neil Shapiro 	/* set local/remote ipv4 addresses */
169794c01205SGregory Neil Shapiro 	if (mci->mci_out != NULL && (
169894c01205SGregory Neil Shapiro #   if NETINET6
169994c01205SGregory Neil Shapiro 		CurHostAddr.sa.sa_family == AF_INET6 ||
17005b0945b5SGregory Neil Shapiro #   endif
170194c01205SGregory Neil Shapiro 		CurHostAddr.sa.sa_family == AF_INET))
170294c01205SGregory Neil Shapiro 	{
170394c01205SGregory Neil Shapiro 		SOCKADDR_LEN_T addrsize;
170494c01205SGregory Neil Shapiro 		SOCKADDR saddr_l;
170594c01205SGregory Neil Shapiro 		char localip[60], remoteip[60];
170694c01205SGregory Neil Shapiro 
170794c01205SGregory Neil Shapiro 		switch (CurHostAddr.sa.sa_family)
170894c01205SGregory Neil Shapiro 		{
170994c01205SGregory Neil Shapiro 		  case AF_INET:
171094c01205SGregory Neil Shapiro 			addrsize = sizeof(struct sockaddr_in);
171194c01205SGregory Neil Shapiro 			break;
171294c01205SGregory Neil Shapiro #   if NETINET6
171394c01205SGregory Neil Shapiro 		  case AF_INET6:
171494c01205SGregory Neil Shapiro 			addrsize = sizeof(struct sockaddr_in6);
171594c01205SGregory Neil Shapiro 			break;
17165b0945b5SGregory Neil Shapiro #   endif
171794c01205SGregory Neil Shapiro 		  default:
171894c01205SGregory Neil Shapiro 			break;
171994c01205SGregory Neil Shapiro 		}
172094c01205SGregory Neil Shapiro 		if (iptostring(&CurHostAddr, addrsize,
1721d0cef73dSGregory Neil Shapiro 			       remoteip, sizeof(remoteip)))
172294c01205SGregory Neil Shapiro 		{
172394c01205SGregory Neil Shapiro 			if (sasl_setprop(mci->mci_conn, SASL_IPREMOTEPORT,
172494c01205SGregory Neil Shapiro 					 remoteip) != SASL_OK)
172594c01205SGregory Neil Shapiro 				return EX_TEMPFAIL;
172694c01205SGregory Neil Shapiro 		}
172794c01205SGregory Neil Shapiro 		addrsize = sizeof(saddr_l);
172894c01205SGregory Neil Shapiro 		if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
172994c01205SGregory Neil Shapiro 					      NULL),
173094c01205SGregory Neil Shapiro 				(struct sockaddr *) &saddr_l, &addrsize) == 0)
173194c01205SGregory Neil Shapiro 		{
173294c01205SGregory Neil Shapiro 			if (iptostring(&saddr_l, addrsize,
1733d0cef73dSGregory Neil Shapiro 				       localip, sizeof(localip)))
173494c01205SGregory Neil Shapiro 			{
173594c01205SGregory Neil Shapiro 				if (sasl_setprop(mci->mci_conn,
173694c01205SGregory Neil Shapiro 						 SASL_IPLOCALPORT,
173794c01205SGregory Neil Shapiro 						 localip) != SASL_OK)
173894c01205SGregory Neil Shapiro 					return EX_TEMPFAIL;
173994c01205SGregory Neil Shapiro 			}
174094c01205SGregory Neil Shapiro 		}
174194c01205SGregory Neil Shapiro 	}
174294c01205SGregory Neil Shapiro #  endif /* NETINET || NETINET6 */
174394c01205SGregory Neil Shapiro 
174494c01205SGregory Neil Shapiro 	/* start client side of sasl */
174594c01205SGregory Neil Shapiro 	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
174694c01205SGregory Neil Shapiro 				       &client_interact,
174794c01205SGregory Neil Shapiro 				       &out, &outlen,
174894c01205SGregory Neil Shapiro 				       (const char **) &mechusing);
174994c01205SGregory Neil Shapiro # else /* SASL >= 20000 */
175006f25ae9SGregory Neil Shapiro 	/* external security strength factor, authentication id */
175106f25ae9SGregory Neil Shapiro 	ssf.ssf = 0;
175206f25ae9SGregory Neil Shapiro 	ssf.auth_id = NULL;
175340266059SGregory Neil Shapiro #  if STARTTLS
175440266059SGregory Neil Shapiro 	out = macvalue(macid("{cert_subject}"), e);
175506f25ae9SGregory Neil Shapiro 	if (out != NULL && *out != '\0')
175606f25ae9SGregory Neil Shapiro 		ssf.auth_id = out;
175740266059SGregory Neil Shapiro 	out = macvalue(macid("{cipher_bits}"), e);
175806f25ae9SGregory Neil Shapiro 	if (out != NULL && *out != '\0')
175906f25ae9SGregory Neil Shapiro 		ssf.ssf = atoi(out);
176040266059SGregory Neil Shapiro #  endif /* STARTTLS */
176106f25ae9SGregory Neil Shapiro 	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
176206f25ae9SGregory Neil Shapiro 	if (saslresult != SASL_OK)
176306f25ae9SGregory Neil Shapiro 		return EX_TEMPFAIL;
176406f25ae9SGregory Neil Shapiro 
176506f25ae9SGregory Neil Shapiro #  if NETINET
176606f25ae9SGregory Neil Shapiro 	/* set local/remote ipv4 addresses */
176706f25ae9SGregory Neil Shapiro 	if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
176806f25ae9SGregory Neil Shapiro 	{
176906f25ae9SGregory Neil Shapiro 		SOCKADDR_LEN_T addrsize;
177006f25ae9SGregory Neil Shapiro 		struct sockaddr_in saddr_l;
177106f25ae9SGregory Neil Shapiro 
177206f25ae9SGregory Neil Shapiro 		if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
177306f25ae9SGregory Neil Shapiro 				 (struct sockaddr_in *) &CurHostAddr)
177406f25ae9SGregory Neil Shapiro 		    != SASL_OK)
177506f25ae9SGregory Neil Shapiro 			return EX_TEMPFAIL;
177606f25ae9SGregory Neil Shapiro 		addrsize = sizeof(struct sockaddr_in);
177740266059SGregory Neil Shapiro 		if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
177840266059SGregory Neil Shapiro 					      NULL),
1779193538b7SGregory Neil Shapiro 				(struct sockaddr *) &saddr_l, &addrsize) == 0)
178006f25ae9SGregory Neil Shapiro 		{
178106f25ae9SGregory Neil Shapiro 			if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
178206f25ae9SGregory Neil Shapiro 					 &saddr_l) != SASL_OK)
178306f25ae9SGregory Neil Shapiro 				return EX_TEMPFAIL;
178406f25ae9SGregory Neil Shapiro 		}
178506f25ae9SGregory Neil Shapiro 	}
178606f25ae9SGregory Neil Shapiro #  endif /* NETINET */
178706f25ae9SGregory Neil Shapiro 
178806f25ae9SGregory Neil Shapiro 	/* start client side of sasl */
178906f25ae9SGregory Neil Shapiro 	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
179006f25ae9SGregory Neil Shapiro 				       NULL, &client_interact,
179106f25ae9SGregory Neil Shapiro 				       &out, &outlen,
179206f25ae9SGregory Neil Shapiro 				       (const char **) &mechusing);
179394c01205SGregory Neil Shapiro # endif /* SASL >= 20000 */
179406f25ae9SGregory Neil Shapiro 
179506f25ae9SGregory Neil Shapiro 	if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
179606f25ae9SGregory Neil Shapiro 	{
179706f25ae9SGregory Neil Shapiro 		if (saslresult == SASL_NOMECH && LogLevel > 8)
179806f25ae9SGregory Neil Shapiro 		{
179906f25ae9SGregory Neil Shapiro 			sm_syslog(LOG_NOTICE, e->e_id,
18005b0945b5SGregory Neil Shapiro 				  "AUTH=client, available mechanisms=%s do not fulfill requirements", mci->mci_saslcap);
180106f25ae9SGregory Neil Shapiro 		}
180206f25ae9SGregory Neil Shapiro 		return EX_TEMPFAIL;
180306f25ae9SGregory Neil Shapiro 	}
180406f25ae9SGregory Neil Shapiro 
180540266059SGregory Neil Shapiro 	/* just point current mechanism to the data in the sasl library */
180640266059SGregory Neil Shapiro 	(*sai)[SASL_MECH] = mechusing;
180706f25ae9SGregory Neil Shapiro 
180806f25ae9SGregory Neil Shapiro 	/* send the info across the wire */
1809959366dcSGregory Neil Shapiro 	if (out == NULL
1810959366dcSGregory Neil Shapiro 		/* login and digest-md5 up to 1.5.28 set out="" */
1811959366dcSGregory Neil Shapiro 	    || (outlen == 0 &&
18122fb4f839SGregory Neil Shapiro 		(SM_STRCASEEQ(mechusing, "login") ||
18132fb4f839SGregory Neil Shapiro 		 SM_STRCASEEQ(mechusing, "digest-md5")))
1814959366dcSGregory Neil Shapiro 	   )
181594c01205SGregory Neil Shapiro 	{
181694c01205SGregory Neil Shapiro 		/* no initial response */
181794c01205SGregory Neil Shapiro 		smtpmessage("AUTH %s", m, mci, mechusing);
181894c01205SGregory Neil Shapiro 	}
181994c01205SGregory Neil Shapiro 	else if (outlen == 0)
182094c01205SGregory Neil Shapiro 	{
182194c01205SGregory Neil Shapiro 		/*
182294c01205SGregory Neil Shapiro 		**  zero-length initial response, per RFC 2554 4.:
182394c01205SGregory Neil Shapiro 		**  "Unlike a zero-length client answer to a 334 reply, a zero-
182494c01205SGregory Neil Shapiro 		**  length initial response is sent as a single equals sign"
182594c01205SGregory Neil Shapiro 		*/
182694c01205SGregory Neil Shapiro 
182794c01205SGregory Neil Shapiro 		smtpmessage("AUTH %s =", m, mci, mechusing);
182894c01205SGregory Neil Shapiro 	}
182994c01205SGregory Neil Shapiro 	else
183006f25ae9SGregory Neil Shapiro 	{
18319bd497b8SGregory Neil Shapiro 		saslresult = sasl_encode64(out, outlen, in64, sizeof(in64),
18329bd497b8SGregory Neil Shapiro 					   NULL);
183306f25ae9SGregory Neil Shapiro 		if (saslresult != SASL_OK) /* internal error */
183406f25ae9SGregory Neil Shapiro 		{
183506f25ae9SGregory Neil Shapiro 			if (LogLevel > 8)
183606f25ae9SGregory Neil Shapiro 				sm_syslog(LOG_ERR, e->e_id,
183706f25ae9SGregory Neil Shapiro 					"encode64 for AUTH failed");
183806f25ae9SGregory Neil Shapiro 			return EX_TEMPFAIL;
183906f25ae9SGregory Neil Shapiro 		}
184006f25ae9SGregory Neil Shapiro 		smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
184106f25ae9SGregory Neil Shapiro 	}
184294c01205SGregory Neil Shapiro # if SASL < 20000
184340266059SGregory Neil Shapiro 	sm_sasl_free(out); /* XXX only if no rpool is used */
18445b0945b5SGregory Neil Shapiro # endif
184506f25ae9SGregory Neil Shapiro 
184606f25ae9SGregory Neil Shapiro 	/* get the reply */
1847e92d3f3fSGregory Neil Shapiro 	smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL,
1848*d39bd2c1SGregory Neil Shapiro 			XS_AUTH, NULL);
184906f25ae9SGregory Neil Shapiro 
185006f25ae9SGregory Neil Shapiro 	for (;;)
185106f25ae9SGregory Neil Shapiro 	{
185206f25ae9SGregory Neil Shapiro 		/* check return code from server */
185306f25ae9SGregory Neil Shapiro 		if (smtpresult == 235)
185406f25ae9SGregory Neil Shapiro 		{
185540266059SGregory Neil Shapiro 			macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"),
185640266059SGregory Neil Shapiro 				  mechusing);
185706f25ae9SGregory Neil Shapiro 			return EX_OK;
185806f25ae9SGregory Neil Shapiro 		}
185906f25ae9SGregory Neil Shapiro 		if (smtpresult == -1)
186006f25ae9SGregory Neil Shapiro 			return EX_IOERR;
186140266059SGregory Neil Shapiro 		if (REPLYTYPE(smtpresult) == 5)
186240266059SGregory Neil Shapiro 			return EX_NOPERM;	/* ugly, but ... */
186340266059SGregory Neil Shapiro 		if (REPLYTYPE(smtpresult) != 3)
186440266059SGregory Neil Shapiro 		{
186540266059SGregory Neil Shapiro 			/* should we fail deliberately, see RFC 2554 4. ? */
186640266059SGregory Neil Shapiro 			/* smtpmessage("*", m, mci); */
186706f25ae9SGregory Neil Shapiro 			return EX_TEMPFAIL;
186840266059SGregory Neil Shapiro 		}
186906f25ae9SGregory Neil Shapiro 
187006f25ae9SGregory Neil Shapiro 		saslresult = sasl_client_step(mci->mci_conn,
187106f25ae9SGregory Neil Shapiro 					      mci->mci_sasl_string,
187206f25ae9SGregory Neil Shapiro 					      mci->mci_sasl_string_len,
187306f25ae9SGregory Neil Shapiro 					      &client_interact,
187406f25ae9SGregory Neil Shapiro 					      &out, &outlen);
187506f25ae9SGregory Neil Shapiro 
187606f25ae9SGregory Neil Shapiro 		if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
187706f25ae9SGregory Neil Shapiro 		{
187806f25ae9SGregory Neil Shapiro 			if (tTd(95, 5))
187940266059SGregory Neil Shapiro 				sm_dprintf("AUTH FAIL=%s (%d)\n",
188006f25ae9SGregory Neil Shapiro 					sasl_errstring(saslresult, NULL, NULL),
188106f25ae9SGregory Neil Shapiro 					saslresult);
188206f25ae9SGregory Neil Shapiro 
188340266059SGregory Neil Shapiro 			/* fail deliberately, see RFC 2554 4. */
188406f25ae9SGregory Neil Shapiro 			smtpmessage("*", m, mci);
188506f25ae9SGregory Neil Shapiro 
188606f25ae9SGregory Neil Shapiro 			/*
188706f25ae9SGregory Neil Shapiro 			**  but we should only fail for this authentication
188806f25ae9SGregory Neil Shapiro 			**  mechanism; how to do that?
188906f25ae9SGregory Neil Shapiro 			*/
189006f25ae9SGregory Neil Shapiro 
189140266059SGregory Neil Shapiro 			smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1892*d39bd2c1SGregory Neil Shapiro 					   getsasldata, NULL, XS_AUTH, NULL);
189340266059SGregory Neil Shapiro 			return EX_NOPERM;
189406f25ae9SGregory Neil Shapiro 		}
189506f25ae9SGregory Neil Shapiro 
189606f25ae9SGregory Neil Shapiro 		if (outlen > 0)
189706f25ae9SGregory Neil Shapiro 		{
189806f25ae9SGregory Neil Shapiro 			saslresult = sasl_encode64(out, outlen, in64,
18999bd497b8SGregory Neil Shapiro 						   sizeof(in64), NULL);
190006f25ae9SGregory Neil Shapiro 			if (saslresult != SASL_OK)
190106f25ae9SGregory Neil Shapiro 			{
190206f25ae9SGregory Neil Shapiro 				/* give an error reply to the other side! */
190306f25ae9SGregory Neil Shapiro 				smtpmessage("*", m, mci);
190406f25ae9SGregory Neil Shapiro 				return EX_TEMPFAIL;
190506f25ae9SGregory Neil Shapiro 			}
190606f25ae9SGregory Neil Shapiro 		}
190706f25ae9SGregory Neil Shapiro 		else
190806f25ae9SGregory Neil Shapiro 			in64[0] = '\0';
190994c01205SGregory Neil Shapiro # if SASL < 20000
191040266059SGregory Neil Shapiro 		sm_sasl_free(out); /* XXX only if no rpool is used */
19115b0945b5SGregory Neil Shapiro # endif
1912193538b7SGregory Neil Shapiro 		smtpmessage("%s", m, mci, in64);
191340266059SGregory Neil Shapiro 		smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1914*d39bd2c1SGregory Neil Shapiro 				   getsasldata, NULL, XS_AUTH, NULL);
191506f25ae9SGregory Neil Shapiro 	}
191606f25ae9SGregory Neil Shapiro 	/* NOTREACHED */
191706f25ae9SGregory Neil Shapiro }
191840266059SGregory Neil Shapiro /*
191906f25ae9SGregory Neil Shapiro **  SMTPAUTH -- try to AUTHenticate
192006f25ae9SGregory Neil Shapiro **
192106f25ae9SGregory Neil Shapiro **	This will try mechanisms in the order the sasl library decided until:
192206f25ae9SGregory Neil Shapiro **	- there are no more mechanisms
192306f25ae9SGregory Neil Shapiro **	- a mechanism succeeds
192406f25ae9SGregory Neil Shapiro **	- the sasl library fails initializing
192506f25ae9SGregory Neil Shapiro **
192606f25ae9SGregory Neil Shapiro **	Parameters:
192706f25ae9SGregory Neil Shapiro **		m -- the mailer.
192806f25ae9SGregory Neil Shapiro **		mci -- the mailer connection info.
192906f25ae9SGregory Neil Shapiro **		e -- the envelope.
193006f25ae9SGregory Neil Shapiro **
193106f25ae9SGregory Neil Shapiro **	Returns:
193240266059SGregory Neil Shapiro **		EX_OK -- authentication was successful
193340266059SGregory Neil Shapiro **		EX_UNAVAILABLE -- authentication not possible, e.g.,
193440266059SGregory Neil Shapiro **			no data available.
193540266059SGregory Neil Shapiro **		EX_NOPERM -- authentication failed.
193640266059SGregory Neil Shapiro **		EX_TEMPFAIL -- temporary failure.
193740266059SGregory Neil Shapiro **
193840266059SGregory Neil Shapiro **	Notice: AuthInfo is used for all connections, hence we must
193940266059SGregory Neil Shapiro **		return EX_TEMPFAIL only if we really want to retry, i.e.,
194040266059SGregory Neil Shapiro **		iff getauth() tempfailed or getauth() was used and
194140266059SGregory Neil Shapiro **		authentication tempfailed.
194206f25ae9SGregory Neil Shapiro */
194306f25ae9SGregory Neil Shapiro 
194406f25ae9SGregory Neil Shapiro int
smtpauth(m,mci,e)194506f25ae9SGregory Neil Shapiro smtpauth(m, mci, e)
194606f25ae9SGregory Neil Shapiro 	MAILER *m;
194706f25ae9SGregory Neil Shapiro 	MCI *mci;
194806f25ae9SGregory Neil Shapiro 	ENVELOPE *e;
194906f25ae9SGregory Neil Shapiro {
195006f25ae9SGregory Neil Shapiro 	int result;
195140266059SGregory Neil Shapiro 	int i;
195240266059SGregory Neil Shapiro 	bool usedgetauth;
195306f25ae9SGregory Neil Shapiro 
195440266059SGregory Neil Shapiro 	mci->mci_sasl_auth = false;
195540266059SGregory Neil Shapiro 	for (i = 0; i < SASL_MECH ; i++)
195640266059SGregory Neil Shapiro 		mci->mci_sai[i] = NULL;
195706f25ae9SGregory Neil Shapiro 
195840266059SGregory Neil Shapiro 	result = getauth(mci, e, &(mci->mci_sai));
195940266059SGregory Neil Shapiro 	if (result == EX_TEMPFAIL)
196040266059SGregory Neil Shapiro 		return result;
196140266059SGregory Neil Shapiro 	usedgetauth = true;
196240266059SGregory Neil Shapiro 
196340266059SGregory Neil Shapiro 	/* no data available: don't try to authenticate */
196440266059SGregory Neil Shapiro 	if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL)
196540266059SGregory Neil Shapiro 		return result;
196640266059SGregory Neil Shapiro 	if (result != EX_OK)
196706f25ae9SGregory Neil Shapiro 	{
196840266059SGregory Neil Shapiro 		if (SASLInfo == NULL)
196940266059SGregory Neil Shapiro 			return EX_UNAVAILABLE;
197040266059SGregory Neil Shapiro 
197140266059SGregory Neil Shapiro 		/* read authinfo from file */
197240266059SGregory Neil Shapiro 		result = readauth(SASLInfo, true, &(mci->mci_sai),
197340266059SGregory Neil Shapiro 				  mci->mci_rpool);
197440266059SGregory Neil Shapiro 		if (result != EX_OK)
197540266059SGregory Neil Shapiro 			return result;
197640266059SGregory Neil Shapiro 		usedgetauth = false;
197706f25ae9SGregory Neil Shapiro 	}
197840266059SGregory Neil Shapiro 
197940266059SGregory Neil Shapiro 	/* check whether sufficient data is available */
198040266059SGregory Neil Shapiro 	if (mci->mci_sai[SASL_PASSWORD] == NULL ||
198140266059SGregory Neil Shapiro 	    *(mci->mci_sai)[SASL_PASSWORD] == '\0')
198240266059SGregory Neil Shapiro 		return EX_UNAVAILABLE;
198340266059SGregory Neil Shapiro 	if ((mci->mci_sai[SASL_AUTHID] == NULL ||
198440266059SGregory Neil Shapiro 	     *(mci->mci_sai)[SASL_AUTHID] == '\0') &&
198540266059SGregory Neil Shapiro 	    (mci->mci_sai[SASL_USER] == NULL ||
198640266059SGregory Neil Shapiro 	     *(mci->mci_sai)[SASL_USER] == '\0'))
198740266059SGregory Neil Shapiro 		return EX_UNAVAILABLE;
198840266059SGregory Neil Shapiro 
198940266059SGregory Neil Shapiro 	/* set the context for the callback function to sai */
199094c01205SGregory Neil Shapiro # if SASL >= 20000
199194c01205SGregory Neil Shapiro 	callbacks[CB_PASS_IDX].context = (void *) mci;
19925b0945b5SGregory Neil Shapiro # else
199340266059SGregory Neil Shapiro 	callbacks[CB_PASS_IDX].context = (void *) &mci->mci_sai;
19945b0945b5SGregory Neil Shapiro # endif
199540266059SGregory Neil Shapiro 	callbacks[CB_USER_IDX].context = (void *) &mci->mci_sai;
199640266059SGregory Neil Shapiro 	callbacks[CB_AUTHNAME_IDX].context = (void *) &mci->mci_sai;
199740266059SGregory Neil Shapiro 	callbacks[CB_GETREALM_IDX].context = (void *) &mci->mci_sai;
199840266059SGregory Neil Shapiro # if 0
199940266059SGregory Neil Shapiro 	callbacks[CB_SAFESASL_IDX].context = (void *) &mci->mci_sai;
20005b0945b5SGregory Neil Shapiro # endif
200140266059SGregory Neil Shapiro 
200240266059SGregory Neil Shapiro 	/* set default value for realm */
200340266059SGregory Neil Shapiro 	if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
200440266059SGregory Neil Shapiro 		(mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool,
200540266059SGregory Neil Shapiro 							macvalue('j', CurEnv));
200640266059SGregory Neil Shapiro 
200740266059SGregory Neil Shapiro 	/* set default value for list of mechanism to use */
200840266059SGregory Neil Shapiro 	if ((mci->mci_sai)[SASL_MECHLIST] == NULL ||
200940266059SGregory Neil Shapiro 	    *(mci->mci_sai)[SASL_MECHLIST] == '\0')
201040266059SGregory Neil Shapiro 		(mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms;
201140266059SGregory Neil Shapiro 
201240266059SGregory Neil Shapiro 	/* create list of mechanisms to try */
201340266059SGregory Neil Shapiro 	mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST],
201440266059SGregory Neil Shapiro 				     mci->mci_saslcap, mci->mci_rpool);
201506f25ae9SGregory Neil Shapiro 
201606f25ae9SGregory Neil Shapiro 	/* initialize sasl client library */
201740266059SGregory Neil Shapiro 	result = init_sasl_client();
201806f25ae9SGregory Neil Shapiro 	if (result != SASL_OK)
201940266059SGregory Neil Shapiro 		return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE;
202006f25ae9SGregory Neil Shapiro 	do
202106f25ae9SGregory Neil Shapiro 	{
202240266059SGregory Neil Shapiro 		result = attemptauth(m, mci, e, &(mci->mci_sai));
202306f25ae9SGregory Neil Shapiro 		if (result == EX_OK)
202440266059SGregory Neil Shapiro 			mci->mci_sasl_auth = true;
202540266059SGregory Neil Shapiro 		else if (result == EX_TEMPFAIL || result == EX_NOPERM)
202606f25ae9SGregory Neil Shapiro 		{
202740266059SGregory Neil Shapiro 			mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH],
202840266059SGregory Neil Shapiro 						      mci->mci_saslcap,
202940266059SGregory Neil Shapiro 						      mci->mci_rpool);
203006f25ae9SGregory Neil Shapiro 			if (mci->mci_saslcap == NULL ||
203106f25ae9SGregory Neil Shapiro 			    *(mci->mci_saslcap) == '\0')
203240266059SGregory Neil Shapiro 				return usedgetauth ? result
203340266059SGregory Neil Shapiro 						   : EX_UNAVAILABLE;
203406f25ae9SGregory Neil Shapiro 		}
203540266059SGregory Neil Shapiro 		else
203640266059SGregory Neil Shapiro 			return result;
203706f25ae9SGregory Neil Shapiro 	} while (result != EX_OK);
203806f25ae9SGregory Neil Shapiro 	return result;
203906f25ae9SGregory Neil Shapiro }
204006f25ae9SGregory Neil Shapiro #endif /* SASL */
204106f25ae9SGregory Neil Shapiro 
204240266059SGregory Neil Shapiro /*
2043c2aa98e2SPeter Wemm **  SMTPMAILFROM -- send MAIL command
2044c2aa98e2SPeter Wemm **
2045c2aa98e2SPeter Wemm **	Parameters:
2046c2aa98e2SPeter Wemm **		m -- the mailer.
2047c2aa98e2SPeter Wemm **		mci -- the mailer connection structure.
2048c2aa98e2SPeter Wemm **		e -- the envelope (including the sender to specify).
20492fb4f839SGregory Neil Shapiro **
20502fb4f839SGregory Neil Shapiro **	Returns:
20512fb4f839SGregory Neil Shapiro **		exit status corresponding to mail status.
2052c2aa98e2SPeter Wemm */
2053c2aa98e2SPeter Wemm 
2054c2aa98e2SPeter Wemm int
smtpmailfrom(m,mci,e)2055c2aa98e2SPeter Wemm smtpmailfrom(m, mci, e)
2056c2aa98e2SPeter Wemm 	MAILER *m;
2057c2aa98e2SPeter Wemm 	MCI *mci;
2058c2aa98e2SPeter Wemm 	ENVELOPE *e;
2059c2aa98e2SPeter Wemm {
2060c2aa98e2SPeter Wemm 	int r;
2061c2aa98e2SPeter Wemm 	char *bufp;
2062c2aa98e2SPeter Wemm 	char *bodytype;
206340266059SGregory Neil Shapiro 	char *enhsc;
20642fb4f839SGregory Neil Shapiro 	char buf[MAXNAME_I + 1];
2065c2aa98e2SPeter Wemm 	char optbuf[MAXLINE];
20662fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
20672fb4f839SGregory Neil Shapiro 	int len, nlen;
20682fb4f839SGregory Neil Shapiro #endif
2069c2aa98e2SPeter Wemm 
2070c2aa98e2SPeter Wemm 	if (tTd(18, 2))
207140266059SGregory Neil Shapiro 		sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
207206f25ae9SGregory Neil Shapiro 	enhsc = NULL;
2073c2aa98e2SPeter Wemm 
207440266059SGregory Neil Shapiro 	/*
207540266059SGregory Neil Shapiro 	**  Check if connection is gone, if so
207640266059SGregory Neil Shapiro 	**  it's a tempfail and we use mci_errno
207740266059SGregory Neil Shapiro 	**  for the reason.
207840266059SGregory Neil Shapiro 	*/
207940266059SGregory Neil Shapiro 
208040266059SGregory Neil Shapiro 	if (mci->mci_state == MCIS_CLOSED)
208140266059SGregory Neil Shapiro 	{
208240266059SGregory Neil Shapiro 		errno = mci->mci_errno;
208340266059SGregory Neil Shapiro 		return EX_TEMPFAIL;
208440266059SGregory Neil Shapiro 	}
208540266059SGregory Neil Shapiro 
20862fb4f839SGregory Neil Shapiro #if USE_EAI
20872fb4f839SGregory Neil Shapiro 	if (bitset(EF_RESPONSE, e->e_flags) &&
20882fb4f839SGregory Neil Shapiro 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
20892fb4f839SGregory Neil Shapiro 		buf[0] = '\0';
20902fb4f839SGregory Neil Shapiro 	else
20912fb4f839SGregory Neil Shapiro 	{
20922fb4f839SGregory Neil Shapiro 		expand("\201g", buf, sizeof(buf), e);
20932fb4f839SGregory Neil Shapiro 		if (!addr_is_ascii(buf) && !e->e_smtputf8)
20942fb4f839SGregory Neil Shapiro 			e->e_smtputf8 = true;
20952fb4f839SGregory Neil Shapiro 	}
20965b0945b5SGregory Neil Shapiro 
2097*d39bd2c1SGregory Neil Shapiro 	if (e->e_smtputf8 && !SMTP_UTF8)
20982fb4f839SGregory Neil Shapiro 	{
20992fb4f839SGregory Neil Shapiro 		extern char MsgBuf[];
21002fb4f839SGregory Neil Shapiro 
21012fb4f839SGregory Neil Shapiro 		/* XREF: format must be coordinated with giveresponse() */
21022fb4f839SGregory Neil Shapiro 		usrerrenh("5.6.7", "504 SMTPUTF8 required but not enabled");
21032fb4f839SGregory Neil Shapiro 		mci_setstat(mci, EX_NOTSTICKY, "5.6.7", MsgBuf);
21045b0945b5SGregory Neil Shapiro 		return EX_DATAERR;
21055b0945b5SGregory Neil Shapiro 	}
21062fb4f839SGregory Neil Shapiro 
21072fb4f839SGregory Neil Shapiro 	/*
21082fb4f839SGregory Neil Shapiro 	**  Abort right away if the message needs SMTPUTF8
21092fb4f839SGregory Neil Shapiro 	**  but the server does not advertise it.
21102fb4f839SGregory Neil Shapiro 	*/
21112fb4f839SGregory Neil Shapiro 
21122fb4f839SGregory Neil Shapiro 	if (e->e_smtputf8 && !bitset(MCIF_EAI, mci->mci_flags))
21132fb4f839SGregory Neil Shapiro 	{
21142fb4f839SGregory Neil Shapiro 		extern char MsgBuf[];
21152fb4f839SGregory Neil Shapiro 
21162fb4f839SGregory Neil Shapiro 		/* XREF: format must be coordinated with giveresponse() */
21172fb4f839SGregory Neil Shapiro 		usrerrenh("5.6.7", "504 SMTPUTF8 required but not offered");
21182fb4f839SGregory Neil Shapiro 		mci_setstat(mci, EX_NOTSTICKY, "5.6.7", MsgBuf);
21192fb4f839SGregory Neil Shapiro 		return EX_DATAERR;
21202fb4f839SGregory Neil Shapiro 	}
21212fb4f839SGregory Neil Shapiro #endif /* USE_EAI */
21225b0945b5SGregory Neil Shapiro 
2123c2aa98e2SPeter Wemm 	/* set up appropriate options to include */
2124c2aa98e2SPeter Wemm 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
212506f25ae9SGregory Neil Shapiro 	{
2126d0cef73dSGregory Neil Shapiro 		(void) sm_snprintf(optbuf, sizeof(optbuf), " SIZE=%ld",
212740266059SGregory Neil Shapiro 			e->e_msgsize);
21282e43090eSPeter Wemm 		bufp = &optbuf[strlen(optbuf)];
212906f25ae9SGregory Neil Shapiro 	}
213006f25ae9SGregory Neil Shapiro 	else
213106f25ae9SGregory Neil Shapiro 	{
213206f25ae9SGregory Neil Shapiro 		optbuf[0] = '\0';
213306f25ae9SGregory Neil Shapiro 		bufp = optbuf;
213406f25ae9SGregory Neil Shapiro 	}
2135c2aa98e2SPeter Wemm 
21362fb4f839SGregory Neil Shapiro #if USE_EAI
21372fb4f839SGregory Neil Shapiro 	if (e->e_smtputf8)
21382fb4f839SGregory Neil Shapiro 	{
21395b0945b5SGregory Neil Shapiro 		(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
21405b0945b5SGregory Neil Shapiro 				 " SMTPUTF8");
21415b0945b5SGregory Neil Shapiro 		bufp += strlen(bufp);
21425b0945b5SGregory Neil Shapiro 	}
21432fb4f839SGregory Neil Shapiro #endif /* USE_EAI */
21445b0945b5SGregory Neil Shapiro 
2145c2aa98e2SPeter Wemm 	bodytype = e->e_bodytype;
2146c2aa98e2SPeter Wemm 	if (bitset(MCIF_8BITMIME, mci->mci_flags))
2147c2aa98e2SPeter Wemm 	{
2148c2aa98e2SPeter Wemm 		if (bodytype == NULL &&
2149c2aa98e2SPeter Wemm 		    bitset(MM_MIME8BIT, MimeMode) &&
2150c2aa98e2SPeter Wemm 		    bitset(EF_HAS8BIT, e->e_flags) &&
2151c2aa98e2SPeter Wemm 		    !bitset(EF_DONT_MIME, e->e_flags) &&
2152c2aa98e2SPeter Wemm 		    !bitnset(M_8BITS, m->m_flags))
2153c2aa98e2SPeter Wemm 			bodytype = "8BITMIME";
21542e43090eSPeter Wemm 		if (bodytype != NULL &&
21552e43090eSPeter Wemm 		    SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
2156c2aa98e2SPeter Wemm 		{
215740266059SGregory Neil Shapiro 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
21582e43090eSPeter Wemm 				 " BODY=%s", bodytype);
21592e43090eSPeter Wemm 			bufp += strlen(bufp);
2160c2aa98e2SPeter Wemm 		}
2161c2aa98e2SPeter Wemm 	}
2162c2aa98e2SPeter Wemm 	else if (bitnset(M_8BITS, m->m_flags) ||
2163c2aa98e2SPeter Wemm 		 !bitset(EF_HAS8BIT, e->e_flags) ||
2164c2aa98e2SPeter Wemm 		 bitset(MCIF_8BITOK, mci->mci_flags))
2165c2aa98e2SPeter Wemm 	{
216606f25ae9SGregory Neil Shapiro 		/* EMPTY */
2167c2aa98e2SPeter Wemm 		/* just pass it through */
2168c2aa98e2SPeter Wemm 	}
2169c2aa98e2SPeter Wemm #if MIME8TO7
2170c2aa98e2SPeter Wemm 	else if (bitset(MM_CVTMIME, MimeMode) &&
2171c2aa98e2SPeter Wemm 		 !bitset(EF_DONT_MIME, e->e_flags) &&
2172c2aa98e2SPeter Wemm 		 (!bitset(MM_PASS8BIT, MimeMode) ||
2173c2aa98e2SPeter Wemm 		  bitset(EF_IS_MIME, e->e_flags)))
2174c2aa98e2SPeter Wemm 	{
2175c2aa98e2SPeter Wemm 		/* must convert from 8bit MIME format to 7bit encoded */
2176c2aa98e2SPeter Wemm 		mci->mci_flags |= MCIF_CVT8TO7;
2177c2aa98e2SPeter Wemm 	}
217806f25ae9SGregory Neil Shapiro #endif /* MIME8TO7 */
2179c2aa98e2SPeter Wemm 	else if (!bitset(MM_PASS8BIT, MimeMode))
2180c2aa98e2SPeter Wemm 	{
2181c2aa98e2SPeter Wemm 		/* cannot just send a 8-bit version */
2182c2aa98e2SPeter Wemm 		extern char MsgBuf[];
2183c2aa98e2SPeter Wemm 
218406f25ae9SGregory Neil Shapiro 		usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
2185c2aa98e2SPeter Wemm 		mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
2186c2aa98e2SPeter Wemm 		return EX_DATAERR;
2187c2aa98e2SPeter Wemm 	}
2188c2aa98e2SPeter Wemm 
2189c2aa98e2SPeter Wemm 	if (bitset(MCIF_DSN, mci->mci_flags))
2190c2aa98e2SPeter Wemm 	{
21912e43090eSPeter Wemm 		if (e->e_envid != NULL &&
21922e43090eSPeter Wemm 		    SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
2193c2aa98e2SPeter Wemm 		{
219440266059SGregory Neil Shapiro 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
21952e43090eSPeter Wemm 				 " ENVID=%s", e->e_envid);
21962e43090eSPeter Wemm 			bufp += strlen(bufp);
2197c2aa98e2SPeter Wemm 		}
2198c2aa98e2SPeter Wemm 
2199c2aa98e2SPeter Wemm 		/* RET= parameter */
22002e43090eSPeter Wemm 		if (bitset(EF_RET_PARAM, e->e_flags) &&
22012e43090eSPeter Wemm 		    SPACELEFT(optbuf, bufp) > 9)
2202c2aa98e2SPeter Wemm 		{
220340266059SGregory Neil Shapiro 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
22042e43090eSPeter Wemm 				 " RET=%s",
22052e43090eSPeter Wemm 				 bitset(EF_NO_BODY_RETN, e->e_flags) ?
22062e43090eSPeter Wemm 					"HDRS" : "FULL");
22072e43090eSPeter Wemm 			bufp += strlen(bufp);
2208c2aa98e2SPeter Wemm 		}
2209c2aa98e2SPeter Wemm 	}
2210c2aa98e2SPeter Wemm 
221106f25ae9SGregory Neil Shapiro 	if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
221206f25ae9SGregory Neil Shapiro 	    SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
221306f25ae9SGregory Neil Shapiro #if SASL
221406f25ae9SGregory Neil Shapiro 	     && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
22155b0945b5SGregory Neil Shapiro #endif
221606f25ae9SGregory Neil Shapiro 	    )
221706f25ae9SGregory Neil Shapiro 	{
221840266059SGregory Neil Shapiro 		(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
221906f25ae9SGregory Neil Shapiro 			 " AUTH=%s", e->e_auth_param);
222006f25ae9SGregory Neil Shapiro 		bufp += strlen(bufp);
222106f25ae9SGregory Neil Shapiro 	}
222206f25ae9SGregory Neil Shapiro 
2223c2aa98e2SPeter Wemm 	/*
222440266059SGregory Neil Shapiro 	**  17 is the max length required, we could use log() to compute
222540266059SGregory Neil Shapiro 	**  the exact length (and check IS_DLVR_TRACE())
222640266059SGregory Neil Shapiro 	*/
222740266059SGregory Neil Shapiro 
222840266059SGregory Neil Shapiro 	if (bitset(MCIF_DLVR_BY, mci->mci_flags) &&
222940266059SGregory Neil Shapiro 	    IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17)
223040266059SGregory Neil Shapiro 	{
223140266059SGregory Neil Shapiro 		long dby;
223240266059SGregory Neil Shapiro 
223340266059SGregory Neil Shapiro 		/*
223440266059SGregory Neil Shapiro 		**  Avoid problems with delays (for R) since the check
223540266059SGregory Neil Shapiro 		**  in deliver() whether min-deliver-time is sufficient.
223640266059SGregory Neil Shapiro 		**  Alternatively we could pass the computed time to this
223740266059SGregory Neil Shapiro 		**  function.
223840266059SGregory Neil Shapiro 		*/
223940266059SGregory Neil Shapiro 
224040266059SGregory Neil Shapiro 		dby = e->e_deliver_by - (curtime() - e->e_ctime);
224140266059SGregory Neil Shapiro 		if (dby <= 0 && IS_DLVR_RETURN(e))
224240266059SGregory Neil Shapiro 			dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by;
224340266059SGregory Neil Shapiro 		(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
224440266059SGregory Neil Shapiro 			" BY=%ld;%c%s",
224540266059SGregory Neil Shapiro 			dby,
224640266059SGregory Neil Shapiro 			IS_DLVR_RETURN(e) ? 'R' : 'N',
224740266059SGregory Neil Shapiro 			IS_DLVR_TRACE(e) ? "T" : "");
224840266059SGregory Neil Shapiro 		bufp += strlen(bufp);
224940266059SGregory Neil Shapiro 	}
225040266059SGregory Neil Shapiro 
225140266059SGregory Neil Shapiro 	/*
2252c2aa98e2SPeter Wemm 	**  Send the MAIL command.
2253c2aa98e2SPeter Wemm 	**	Designates the sender.
2254c2aa98e2SPeter Wemm 	*/
2255c2aa98e2SPeter Wemm 
2256*d39bd2c1SGregory Neil Shapiro 	maps_reset_chged("client:MAIL");
225740266059SGregory Neil Shapiro 	mci->mci_state = MCIS_MAIL;
2258c2aa98e2SPeter Wemm 
22592fb4f839SGregory Neil Shapiro #if !USE_EAI
2260c2aa98e2SPeter Wemm 	if (bitset(EF_RESPONSE, e->e_flags) &&
2261c2aa98e2SPeter Wemm 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
226206f25ae9SGregory Neil Shapiro 		buf[0] = '\0';
2263c2aa98e2SPeter Wemm 	else
2264d0cef73dSGregory Neil Shapiro 		expand("\201g", buf, sizeof(buf), e);
22652fb4f839SGregory Neil Shapiro #endif /* !USE_EAI */
22662fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
22672fb4f839SGregory Neil Shapiro 	if (tTd(18, 11))
22682fb4f839SGregory Neil Shapiro 		sm_dprintf("mail_expand=%s\n", buf);
22692fb4f839SGregory Neil Shapiro 	len = sizeof(buf);
22702fb4f839SGregory Neil Shapiro 	nlen = dequote_internal_chars(buf, buf, len);
22712fb4f839SGregory Neil Shapiro 	/* check length! but that's a bit late... */
22722fb4f839SGregory Neil Shapiro 	if (nlen > MAXNAME)
22732fb4f839SGregory Neil Shapiro 		sm_syslog(LOG_ERR, e->e_id, "MAIL too long: %d", nlen);
22742fb4f839SGregory Neil Shapiro 	if (tTd(18, 11))
22752fb4f839SGregory Neil Shapiro 		sm_dprintf("mail2=%s\n", buf);
22762fb4f839SGregory Neil Shapiro #endif /* _FFR_8BITENVADDR */
2277c2aa98e2SPeter Wemm 	if (buf[0] == '<')
2278c2aa98e2SPeter Wemm 	{
2279c2aa98e2SPeter Wemm 		/* strip off <angle brackets> (put back on below) */
2280c2aa98e2SPeter Wemm 		bufp = &buf[strlen(buf) - 1];
2281c2aa98e2SPeter Wemm 		if (*bufp == '>')
2282c2aa98e2SPeter Wemm 			*bufp = '\0';
2283c2aa98e2SPeter Wemm 		bufp = &buf[1];
2284c2aa98e2SPeter Wemm 	}
2285c2aa98e2SPeter Wemm 	else
2286c2aa98e2SPeter Wemm 		bufp = buf;
2287c2aa98e2SPeter Wemm 	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
2288c2aa98e2SPeter Wemm 	    !bitnset(M_FROMPATH, m->m_flags))
2289c2aa98e2SPeter Wemm 	{
2290c2aa98e2SPeter Wemm 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
2291c2aa98e2SPeter Wemm 	}
2292c2aa98e2SPeter Wemm 	else
2293c2aa98e2SPeter Wemm 	{
2294c2aa98e2SPeter Wemm 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
2295c2aa98e2SPeter Wemm 			    *bufp == '@' ? ',' : ':', bufp, optbuf);
2296c2aa98e2SPeter Wemm 	}
2297c2aa98e2SPeter Wemm 	SmtpPhase = mci->mci_phase = "client MAIL";
229840266059SGregory Neil Shapiro 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
229906f25ae9SGregory Neil Shapiro 			CurHostName, mci->mci_phase);
2300*d39bd2c1SGregory Neil Shapiro 	r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc, XS_MAIL, NULL);
2301c2aa98e2SPeter Wemm 	if (r < 0)
2302c2aa98e2SPeter Wemm 	{
2303c2aa98e2SPeter Wemm 		/* communications failure */
2304c2aa98e2SPeter Wemm 		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
2305c2aa98e2SPeter Wemm 		return EX_TEMPFAIL;
2306c2aa98e2SPeter Wemm 	}
2307193538b7SGregory Neil Shapiro 	else if (r == SMTPCLOSING)
2308c2aa98e2SPeter Wemm 	{
2309605302a5SGregory Neil Shapiro 		/* service shutting down: handled by reply() */
2310c2aa98e2SPeter Wemm 		return EX_TEMPFAIL;
2311c2aa98e2SPeter Wemm 	}
2312c2aa98e2SPeter Wemm 	else if (REPLYTYPE(r) == 4)
2313c2aa98e2SPeter Wemm 	{
231406f25ae9SGregory Neil Shapiro 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
231506f25ae9SGregory Neil Shapiro 			    SmtpReplyBuffer);
2316c2aa98e2SPeter Wemm 		return EX_TEMPFAIL;
2317c2aa98e2SPeter Wemm 	}
2318c2aa98e2SPeter Wemm 	else if (REPLYTYPE(r) == 2)
2319c2aa98e2SPeter Wemm 	{
2320c2aa98e2SPeter Wemm 		return EX_OK;
2321c2aa98e2SPeter Wemm 	}
2322c2aa98e2SPeter Wemm 	else if (r == 501)
2323c2aa98e2SPeter Wemm 	{
2324c2aa98e2SPeter Wemm 		/* syntax error in arguments */
232506f25ae9SGregory Neil Shapiro 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
232606f25ae9SGregory Neil Shapiro 			    SmtpReplyBuffer);
2327c2aa98e2SPeter Wemm 		return EX_DATAERR;
2328c2aa98e2SPeter Wemm 	}
2329c2aa98e2SPeter Wemm 	else if (r == 553)
2330c2aa98e2SPeter Wemm 	{
2331c2aa98e2SPeter Wemm 		/* mailbox name not allowed */
233206f25ae9SGregory Neil Shapiro 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
233306f25ae9SGregory Neil Shapiro 			    SmtpReplyBuffer);
2334c2aa98e2SPeter Wemm 		return EX_DATAERR;
2335c2aa98e2SPeter Wemm 	}
2336c2aa98e2SPeter Wemm 	else if (r == 552)
2337c2aa98e2SPeter Wemm 	{
2338c2aa98e2SPeter Wemm 		/* exceeded storage allocation */
233906f25ae9SGregory Neil Shapiro 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
234006f25ae9SGregory Neil Shapiro 			    SmtpReplyBuffer);
2341c2aa98e2SPeter Wemm 		if (bitset(MCIF_SIZE, mci->mci_flags))
2342c2aa98e2SPeter Wemm 			e->e_flags |= EF_NO_BODY_RETN;
2343c2aa98e2SPeter Wemm 		return EX_UNAVAILABLE;
2344c2aa98e2SPeter Wemm 	}
2345c2aa98e2SPeter Wemm 	else if (REPLYTYPE(r) == 5)
2346c2aa98e2SPeter Wemm 	{
2347c2aa98e2SPeter Wemm 		/* unknown error */
234806f25ae9SGregory Neil Shapiro 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
234906f25ae9SGregory Neil Shapiro 			    SmtpReplyBuffer);
2350c2aa98e2SPeter Wemm 		return EX_UNAVAILABLE;
2351c2aa98e2SPeter Wemm 	}
2352c2aa98e2SPeter Wemm 
2353c2aa98e2SPeter Wemm 	if (LogLevel > 1)
2354c2aa98e2SPeter Wemm 	{
2355c2aa98e2SPeter Wemm 		sm_syslog(LOG_CRIT, e->e_id,
2356c2aa98e2SPeter Wemm 			  "%.100s: SMTP MAIL protocol error: %s",
2357c2aa98e2SPeter Wemm 			  CurHostName,
2358c2aa98e2SPeter Wemm 			  shortenstring(SmtpReplyBuffer, 403));
2359c2aa98e2SPeter Wemm 	}
2360c2aa98e2SPeter Wemm 
2361c2aa98e2SPeter Wemm 	/* protocol error -- close up */
236206f25ae9SGregory Neil Shapiro 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
236306f25ae9SGregory Neil Shapiro 		    SmtpReplyBuffer);
2364c2aa98e2SPeter Wemm 	smtpquit(m, mci, e);
2365c2aa98e2SPeter Wemm 	return EX_PROTOCOL;
2366c2aa98e2SPeter Wemm }
236740266059SGregory Neil Shapiro /*
2368c2aa98e2SPeter Wemm **  SMTPRCPT -- designate recipient.
2369c2aa98e2SPeter Wemm **
2370c2aa98e2SPeter Wemm **	Parameters:
2371c2aa98e2SPeter Wemm **		to -- address of recipient.
2372c2aa98e2SPeter Wemm **		m -- the mailer we are sending to.
2373c2aa98e2SPeter Wemm **		mci -- the connection info for this transaction.
2374c2aa98e2SPeter Wemm **		e -- the envelope for this transaction.
2375c2aa98e2SPeter Wemm **
2376c2aa98e2SPeter Wemm **	Returns:
2377c2aa98e2SPeter Wemm **		exit status corresponding to recipient status.
2378c2aa98e2SPeter Wemm **
2379c2aa98e2SPeter Wemm **	Side Effects:
2380c2aa98e2SPeter Wemm **		Sends the mail via SMTP.
2381c2aa98e2SPeter Wemm */
2382c2aa98e2SPeter Wemm 
2383c2aa98e2SPeter Wemm int
smtprcpt(to,m,mci,e,ctladdr,xstart)238440266059SGregory Neil Shapiro smtprcpt(to, m, mci, e, ctladdr, xstart)
2385c2aa98e2SPeter Wemm 	ADDRESS *to;
2386c2aa98e2SPeter Wemm 	register MAILER *m;
2387c2aa98e2SPeter Wemm 	MCI *mci;
2388c2aa98e2SPeter Wemm 	ENVELOPE *e;
238940266059SGregory Neil Shapiro 	ADDRESS *ctladdr;
239040266059SGregory Neil Shapiro 	time_t xstart;
2391c2aa98e2SPeter Wemm {
23922e43090eSPeter Wemm 	char *bufp;
2393c2aa98e2SPeter Wemm 	char optbuf[MAXLINE];
23942fb4f839SGregory Neil Shapiro 	char *rcpt;
23952fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
23962fb4f839SGregory Neil Shapiro 	char buf[MAXNAME + 1];	/* EAI:ok */
23972fb4f839SGregory Neil Shapiro 	int len, nlen;
23982fb4f839SGregory Neil Shapiro #endif
2399*d39bd2c1SGregory Neil Shapiro #if PIPELINING
2400*d39bd2c1SGregory Neil Shapiro 	char *oldto;
2401*d39bd2c1SGregory Neil Shapiro #endif
2402c2aa98e2SPeter Wemm 
240340266059SGregory Neil Shapiro #if PIPELINING
240440266059SGregory Neil Shapiro 	/*
240540266059SGregory Neil Shapiro 	**  If there is status waiting from the other end, read it.
240640266059SGregory Neil Shapiro 	**  This should normally happen because of SMTP pipelining.
240740266059SGregory Neil Shapiro 	*/
240840266059SGregory Neil Shapiro 
2409*d39bd2c1SGregory Neil Shapiro 	oldto = e->e_to;
241040266059SGregory Neil Shapiro 	while (mci->mci_nextaddr != NULL &&
241113bd1963SGregory Neil Shapiro 	       sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
241240266059SGregory Neil Shapiro 	{
241340266059SGregory Neil Shapiro 		int r;
241440266059SGregory Neil Shapiro 
2415*d39bd2c1SGregory Neil Shapiro 		e->e_to = mci->mci_nextaddr->q_paddr;
241640266059SGregory Neil Shapiro 		r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
241740266059SGregory Neil Shapiro 		if (r != EX_OK)
241840266059SGregory Neil Shapiro 		{
241940266059SGregory Neil Shapiro 			markfailure(e, mci->mci_nextaddr, mci, r, false);
242040266059SGregory Neil Shapiro 			giveresponse(r, mci->mci_nextaddr->q_status, m, mci,
2421*d39bd2c1SGregory Neil Shapiro 				     ctladdr, xstart, e, mci->mci_nextaddr);
242240266059SGregory Neil Shapiro 		}
242340266059SGregory Neil Shapiro 		mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2424*d39bd2c1SGregory Neil Shapiro 		e->e_to = oldto;
242540266059SGregory Neil Shapiro 	}
2426*d39bd2c1SGregory Neil Shapiro 	e->e_to = oldto;
242740266059SGregory Neil Shapiro #endif /* PIPELINING */
242840266059SGregory Neil Shapiro 
242940266059SGregory Neil Shapiro 	/*
243040266059SGregory Neil Shapiro 	**  Check if connection is gone, if so
243140266059SGregory Neil Shapiro 	**  it's a tempfail and we use mci_errno
243240266059SGregory Neil Shapiro 	**  for the reason.
243340266059SGregory Neil Shapiro 	*/
243440266059SGregory Neil Shapiro 
243540266059SGregory Neil Shapiro 	if (mci->mci_state == MCIS_CLOSED)
243640266059SGregory Neil Shapiro 	{
243740266059SGregory Neil Shapiro 		errno = mci->mci_errno;
243840266059SGregory Neil Shapiro 		return EX_TEMPFAIL;
243940266059SGregory Neil Shapiro 	}
244040266059SGregory Neil Shapiro 
244106f25ae9SGregory Neil Shapiro 	optbuf[0] = '\0';
244206f25ae9SGregory Neil Shapiro 	bufp = optbuf;
244306f25ae9SGregory Neil Shapiro 
244406f25ae9SGregory Neil Shapiro 	/*
244540266059SGregory Neil Shapiro 	**  Warning: in the following it is assumed that the free space
2446d0cef73dSGregory Neil Shapiro 	**  in bufp is sizeof(optbuf)
244706f25ae9SGregory Neil Shapiro 	*/
244840266059SGregory Neil Shapiro 
2449c2aa98e2SPeter Wemm 	if (bitset(MCIF_DSN, mci->mci_flags))
2450c2aa98e2SPeter Wemm 	{
245140266059SGregory Neil Shapiro 		if (IS_DLVR_NOTIFY(e) &&
245240266059SGregory Neil Shapiro 		    !bitset(MCIF_DLVR_BY, mci->mci_flags))
245340266059SGregory Neil Shapiro 		{
245440266059SGregory Neil Shapiro 			/* RFC 2852: 4.1.4.2 */
245540266059SGregory Neil Shapiro 			if (!bitset(QHASNOTIFY, to->q_flags))
245640266059SGregory Neil Shapiro 				to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY;
245740266059SGregory Neil Shapiro 			else if (bitset(QPINGONSUCCESS, to->q_flags) ||
245840266059SGregory Neil Shapiro 				 bitset(QPINGONFAILURE, to->q_flags) ||
245940266059SGregory Neil Shapiro 				 bitset(QPINGONDELAY, to->q_flags))
246040266059SGregory Neil Shapiro 				to->q_flags |= QPINGONDELAY;
246140266059SGregory Neil Shapiro 		}
246240266059SGregory Neil Shapiro 
2463c2aa98e2SPeter Wemm 		/* NOTIFY= parameter */
2464c2aa98e2SPeter Wemm 		if (bitset(QHASNOTIFY, to->q_flags) &&
2465c2aa98e2SPeter Wemm 		    bitset(QPRIMARY, to->q_flags) &&
2466c2aa98e2SPeter Wemm 		    !bitnset(M_LOCALMAILER, m->m_flags))
2467c2aa98e2SPeter Wemm 		{
246840266059SGregory Neil Shapiro 			bool firstone = true;
2469c2aa98e2SPeter Wemm 
2470d0cef73dSGregory Neil Shapiro 			(void) sm_strlcat(bufp, " NOTIFY=", sizeof(optbuf));
2471c2aa98e2SPeter Wemm 			if (bitset(QPINGONSUCCESS, to->q_flags))
2472c2aa98e2SPeter Wemm 			{
2473d0cef73dSGregory Neil Shapiro 				(void) sm_strlcat(bufp, "SUCCESS", sizeof(optbuf));
247440266059SGregory Neil Shapiro 				firstone = false;
2475c2aa98e2SPeter Wemm 			}
2476c2aa98e2SPeter Wemm 			if (bitset(QPINGONFAILURE, to->q_flags))
2477c2aa98e2SPeter Wemm 			{
2478c2aa98e2SPeter Wemm 				if (!firstone)
247940266059SGregory Neil Shapiro 					(void) sm_strlcat(bufp, ",",
2480d0cef73dSGregory Neil Shapiro 						       sizeof(optbuf));
2481d0cef73dSGregory Neil Shapiro 				(void) sm_strlcat(bufp, "FAILURE", sizeof(optbuf));
248240266059SGregory Neil Shapiro 				firstone = false;
2483c2aa98e2SPeter Wemm 			}
2484c2aa98e2SPeter Wemm 			if (bitset(QPINGONDELAY, to->q_flags))
2485c2aa98e2SPeter Wemm 			{
2486c2aa98e2SPeter Wemm 				if (!firstone)
248740266059SGregory Neil Shapiro 					(void) sm_strlcat(bufp, ",",
2488d0cef73dSGregory Neil Shapiro 						       sizeof(optbuf));
2489d0cef73dSGregory Neil Shapiro 				(void) sm_strlcat(bufp, "DELAY", sizeof(optbuf));
249040266059SGregory Neil Shapiro 				firstone = false;
2491c2aa98e2SPeter Wemm 			}
2492c2aa98e2SPeter Wemm 			if (firstone)
2493d0cef73dSGregory Neil Shapiro 				(void) sm_strlcat(bufp, "NEVER", sizeof(optbuf));
24942e43090eSPeter Wemm 			bufp += strlen(bufp);
2495c2aa98e2SPeter Wemm 		}
2496c2aa98e2SPeter Wemm 
2497c2aa98e2SPeter Wemm 		/* ORCPT= parameter */
24982e43090eSPeter Wemm 		if (to->q_orcpt != NULL &&
24992e43090eSPeter Wemm 		    SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
2500c2aa98e2SPeter Wemm 		{
250140266059SGregory Neil Shapiro 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
25022e43090eSPeter Wemm 				 " ORCPT=%s", to->q_orcpt);
25032e43090eSPeter Wemm 			bufp += strlen(bufp);
2504c2aa98e2SPeter Wemm 		}
2505c2aa98e2SPeter Wemm 	}
2506c2aa98e2SPeter Wemm 
25072fb4f839SGregory Neil Shapiro 	rcpt = to->q_user;
25082fb4f839SGregory Neil Shapiro #if _FFR_8BITENVADDR
25092fb4f839SGregory Neil Shapiro 	if (tTd(18, 11))
25102fb4f839SGregory Neil Shapiro 		sm_dprintf("rcpt=%s\n", rcpt);
25112fb4f839SGregory Neil Shapiro 	len = sizeof(buf);
25122fb4f839SGregory Neil Shapiro 	nlen = dequote_internal_chars(rcpt, buf, len);
25132fb4f839SGregory Neil Shapiro 	rcpt = buf;
25142fb4f839SGregory Neil Shapiro 	/* check length! but that's a bit late... */
25152fb4f839SGregory Neil Shapiro 	if (nlen > MAXNAME)
25162fb4f839SGregory Neil Shapiro 		sm_syslog(LOG_ERR, e->e_id, "RCPT too long: %d", nlen);
25172fb4f839SGregory Neil Shapiro 	if (tTd(18, 11))
25182fb4f839SGregory Neil Shapiro 		sm_dprintf("rcpt2=%s\n", rcpt);
25192fb4f839SGregory Neil Shapiro #endif /* _FFR_8BITENVADDR */
25202fb4f839SGregory Neil Shapiro 
25212fb4f839SGregory Neil Shapiro 	smtpmessage("RCPT To:<%s>%s", m, mci, rcpt, optbuf);
252240266059SGregory Neil Shapiro 	mci->mci_state = MCIS_RCPT;
2523c2aa98e2SPeter Wemm 
2524c2aa98e2SPeter Wemm 	SmtpPhase = mci->mci_phase = "client RCPT";
252540266059SGregory Neil Shapiro 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
252606f25ae9SGregory Neil Shapiro 			CurHostName, mci->mci_phase);
252740266059SGregory Neil Shapiro 
252840266059SGregory Neil Shapiro #if PIPELINING
252940266059SGregory Neil Shapiro 	/*
253040266059SGregory Neil Shapiro 	**  If running SMTP pipelining, we will pick up status later
253140266059SGregory Neil Shapiro 	*/
253240266059SGregory Neil Shapiro 
253340266059SGregory Neil Shapiro 	if (bitset(MCIF_PIPELINED, mci->mci_flags))
253440266059SGregory Neil Shapiro 		return EX_OK;
253540266059SGregory Neil Shapiro #endif /* PIPELINING */
253640266059SGregory Neil Shapiro 
253740266059SGregory Neil Shapiro 	return smtprcptstat(to, m, mci, e);
253840266059SGregory Neil Shapiro }
253940266059SGregory Neil Shapiro /*
254040266059SGregory Neil Shapiro **  SMTPRCPTSTAT -- get recipient status
254140266059SGregory Neil Shapiro **
254240266059SGregory Neil Shapiro **	This is only called during SMTP pipelining
254340266059SGregory Neil Shapiro **
254440266059SGregory Neil Shapiro **	Parameters:
254540266059SGregory Neil Shapiro **		to -- address of recipient.
254640266059SGregory Neil Shapiro **		m -- mailer being sent to.
254740266059SGregory Neil Shapiro **		mci -- the mailer connection information.
254840266059SGregory Neil Shapiro **		e -- the envelope for this message.
254940266059SGregory Neil Shapiro **
255040266059SGregory Neil Shapiro **	Returns:
255140266059SGregory Neil Shapiro **		EX_* -- protocol status
255240266059SGregory Neil Shapiro */
255340266059SGregory Neil Shapiro 
255440266059SGregory Neil Shapiro static int
smtprcptstat(to,m,mci,e)255540266059SGregory Neil Shapiro smtprcptstat(to, m, mci, e)
255640266059SGregory Neil Shapiro 	ADDRESS *to;
255740266059SGregory Neil Shapiro 	MAILER *m;
255840266059SGregory Neil Shapiro 	register MCI *mci;
255940266059SGregory Neil Shapiro 	register ENVELOPE *e;
256040266059SGregory Neil Shapiro {
256140266059SGregory Neil Shapiro 	int r;
256240266059SGregory Neil Shapiro 	int save_errno;
256340266059SGregory Neil Shapiro 	char *enhsc;
256440266059SGregory Neil Shapiro 
256540266059SGregory Neil Shapiro 	/*
256640266059SGregory Neil Shapiro 	**  Check if connection is gone, if so
256740266059SGregory Neil Shapiro 	**  it's a tempfail and we use mci_errno
256840266059SGregory Neil Shapiro 	**  for the reason.
256940266059SGregory Neil Shapiro 	*/
257040266059SGregory Neil Shapiro 
257140266059SGregory Neil Shapiro 	if (mci->mci_state == MCIS_CLOSED)
257240266059SGregory Neil Shapiro 	{
257340266059SGregory Neil Shapiro 		errno = mci->mci_errno;
257440266059SGregory Neil Shapiro 		return EX_TEMPFAIL;
257540266059SGregory Neil Shapiro 	}
257640266059SGregory Neil Shapiro 
257740266059SGregory Neil Shapiro 	enhsc = NULL;
2578*d39bd2c1SGregory Neil Shapiro 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc, XS_RCPT,
2579*d39bd2c1SGregory Neil Shapiro 		  &to->q_rstatus);
258040266059SGregory Neil Shapiro 	save_errno = errno;
258140266059SGregory Neil Shapiro 	to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool);
258206f25ae9SGregory Neil Shapiro 	if (!bitnset(M_LMTP, m->m_flags))
2583c2aa98e2SPeter Wemm 		to->q_statmta = mci->mci_host;
2584c2aa98e2SPeter Wemm 	if (r < 0 || REPLYTYPE(r) == 4)
258540266059SGregory Neil Shapiro 	{
258640266059SGregory Neil Shapiro 		mci->mci_retryrcpt = true;
258740266059SGregory Neil Shapiro 		errno = save_errno;
2588c2aa98e2SPeter Wemm 		return EX_TEMPFAIL;
258940266059SGregory Neil Shapiro 	}
2590c2aa98e2SPeter Wemm 	else if (REPLYTYPE(r) == 2)
259140266059SGregory Neil Shapiro 	{
259240266059SGregory Neil Shapiro 		char *t;
259340266059SGregory Neil Shapiro 
259440266059SGregory Neil Shapiro 		if ((t = mci->mci_tolist) != NULL)
259540266059SGregory Neil Shapiro 		{
259640266059SGregory Neil Shapiro 			char *p;
259740266059SGregory Neil Shapiro 
259840266059SGregory Neil Shapiro 			*t++ = ',';
259940266059SGregory Neil Shapiro 			for (p = to->q_paddr; *p != '\0'; *t++ = *p++)
260040266059SGregory Neil Shapiro 				continue;
260140266059SGregory Neil Shapiro 			*t = '\0';
260240266059SGregory Neil Shapiro 			mci->mci_tolist = t;
260340266059SGregory Neil Shapiro 		}
260440266059SGregory Neil Shapiro 		mci->mci_okrcpts++;
2605c2aa98e2SPeter Wemm 		return EX_OK;
260640266059SGregory Neil Shapiro 	}
2607c2aa98e2SPeter Wemm 	else if (r == 550)
2608c2aa98e2SPeter Wemm 	{
260940266059SGregory Neil Shapiro 		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool);
2610c2aa98e2SPeter Wemm 		return EX_NOUSER;
2611c2aa98e2SPeter Wemm 	}
2612c2aa98e2SPeter Wemm 	else if (r == 551)
2613c2aa98e2SPeter Wemm 	{
261440266059SGregory Neil Shapiro 		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool);
2615c2aa98e2SPeter Wemm 		return EX_NOUSER;
2616c2aa98e2SPeter Wemm 	}
2617c2aa98e2SPeter Wemm 	else if (r == 553)
2618c2aa98e2SPeter Wemm 	{
261940266059SGregory Neil Shapiro 		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool);
2620c2aa98e2SPeter Wemm 		return EX_NOUSER;
2621c2aa98e2SPeter Wemm 	}
2622c2aa98e2SPeter Wemm 	else if (REPLYTYPE(r) == 5)
2623c2aa98e2SPeter Wemm 	{
2624c2aa98e2SPeter Wemm 		return EX_UNAVAILABLE;
2625c2aa98e2SPeter Wemm 	}
2626c2aa98e2SPeter Wemm 
2627c2aa98e2SPeter Wemm 	if (LogLevel > 1)
2628c2aa98e2SPeter Wemm 	{
2629c2aa98e2SPeter Wemm 		sm_syslog(LOG_CRIT, e->e_id,
2630c2aa98e2SPeter Wemm 			  "%.100s: SMTP RCPT protocol error: %s",
2631c2aa98e2SPeter Wemm 			  CurHostName,
2632c2aa98e2SPeter Wemm 			  shortenstring(SmtpReplyBuffer, 403));
2633c2aa98e2SPeter Wemm 	}
2634c2aa98e2SPeter Wemm 
263506f25ae9SGregory Neil Shapiro 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
263606f25ae9SGregory Neil Shapiro 		    SmtpReplyBuffer);
2637c2aa98e2SPeter Wemm 	return EX_PROTOCOL;
2638c2aa98e2SPeter Wemm }
263940266059SGregory Neil Shapiro /*
2640c2aa98e2SPeter Wemm **  SMTPDATA -- send the data and clean up the transaction.
2641c2aa98e2SPeter Wemm **
2642c2aa98e2SPeter Wemm **	Parameters:
2643c2aa98e2SPeter Wemm **		m -- mailer being sent to.
2644c2aa98e2SPeter Wemm **		mci -- the mailer connection information.
2645c2aa98e2SPeter Wemm **		e -- the envelope for this message.
2646c2aa98e2SPeter Wemm **
2647c2aa98e2SPeter Wemm **	Returns:
2648c2aa98e2SPeter Wemm **		exit status corresponding to DATA command.
2649c2aa98e2SPeter Wemm */
2650c2aa98e2SPeter Wemm 
2651c2aa98e2SPeter Wemm int
smtpdata(m,mci,e,ctladdr,xstart)265240266059SGregory Neil Shapiro smtpdata(m, mci, e, ctladdr, xstart)
2653c2aa98e2SPeter Wemm 	MAILER *m;
2654c2aa98e2SPeter Wemm 	register MCI *mci;
2655c2aa98e2SPeter Wemm 	register ENVELOPE *e;
265640266059SGregory Neil Shapiro 	ADDRESS *ctladdr;
265740266059SGregory Neil Shapiro 	time_t xstart;
2658c2aa98e2SPeter Wemm {
2659c2aa98e2SPeter Wemm 	register int r;
2660c2aa98e2SPeter Wemm 	int rstat;
2661c2aa98e2SPeter Wemm 	int xstat;
26624e4196cbSGregory Neil Shapiro 	int timeout;
266306f25ae9SGregory Neil Shapiro 	char *enhsc;
2664c2aa98e2SPeter Wemm 
266540266059SGregory Neil Shapiro 	/*
266640266059SGregory Neil Shapiro 	**  Check if connection is gone, if so
266740266059SGregory Neil Shapiro 	**  it's a tempfail and we use mci_errno
266840266059SGregory Neil Shapiro 	**  for the reason.
266940266059SGregory Neil Shapiro 	*/
267040266059SGregory Neil Shapiro 
267140266059SGregory Neil Shapiro 	if (mci->mci_state == MCIS_CLOSED)
267240266059SGregory Neil Shapiro 	{
267340266059SGregory Neil Shapiro 		errno = mci->mci_errno;
267440266059SGregory Neil Shapiro 		return EX_TEMPFAIL;
267540266059SGregory Neil Shapiro 	}
267640266059SGregory Neil Shapiro 
267706f25ae9SGregory Neil Shapiro 	enhsc = NULL;
26788774250cSGregory Neil Shapiro 
2679c2aa98e2SPeter Wemm 	/*
2680c2aa98e2SPeter Wemm 	**  Send the data.
2681c2aa98e2SPeter Wemm 	**	First send the command and check that it is ok.
268240266059SGregory Neil Shapiro 	**	Then send the data (if there are valid recipients).
2683c2aa98e2SPeter Wemm 	**	Follow it up with a dot to terminate.
2684c2aa98e2SPeter Wemm 	**	Finally get the results of the transaction.
2685c2aa98e2SPeter Wemm 	*/
2686c2aa98e2SPeter Wemm 
2687c2aa98e2SPeter Wemm 	/* send the command and check ok to proceed */
2688c2aa98e2SPeter Wemm 	smtpmessage("DATA", m, mci);
268940266059SGregory Neil Shapiro 
269040266059SGregory Neil Shapiro #if PIPELINING
269140266059SGregory Neil Shapiro 	if (mci->mci_nextaddr != NULL)
269240266059SGregory Neil Shapiro 	{
269340266059SGregory Neil Shapiro 		char *oldto = e->e_to;
269440266059SGregory Neil Shapiro 
269540266059SGregory Neil Shapiro 		/* pick up any pending RCPT responses for SMTP pipelining */
269640266059SGregory Neil Shapiro 		while (mci->mci_nextaddr != NULL)
269740266059SGregory Neil Shapiro 		{
269840266059SGregory Neil Shapiro 			e->e_to = mci->mci_nextaddr->q_paddr;
269940266059SGregory Neil Shapiro 			r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
270040266059SGregory Neil Shapiro 			if (r != EX_OK)
270140266059SGregory Neil Shapiro 			{
270240266059SGregory Neil Shapiro 				markfailure(e, mci->mci_nextaddr, mci, r,
270340266059SGregory Neil Shapiro 					    false);
270440266059SGregory Neil Shapiro 				giveresponse(r, mci->mci_nextaddr->q_status, m,
270540266059SGregory Neil Shapiro 					     mci, ctladdr, xstart, e,
270640266059SGregory Neil Shapiro 					     mci->mci_nextaddr);
270740266059SGregory Neil Shapiro 				if (r == EX_TEMPFAIL)
270840266059SGregory Neil Shapiro 					mci->mci_nextaddr->q_state = QS_RETRY;
270940266059SGregory Neil Shapiro 			}
271040266059SGregory Neil Shapiro 			mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
271140266059SGregory Neil Shapiro 		}
271240266059SGregory Neil Shapiro 		e->e_to = oldto;
2713b6bacd31SGregory Neil Shapiro 
2714b6bacd31SGregory Neil Shapiro 		/*
2715b6bacd31SGregory Neil Shapiro 		**  Connection might be closed in response to a RCPT command,
2716b6bacd31SGregory Neil Shapiro 		**  i.e., the server responded with 421. In that case (at
2717b6bacd31SGregory Neil Shapiro 		**  least) one RCPT has a temporary failure, hence we don't
2718b6bacd31SGregory Neil Shapiro 		**  need to check mci_okrcpts (as it is done below) to figure
2719b6bacd31SGregory Neil Shapiro 		**  out which error to return.
2720b6bacd31SGregory Neil Shapiro 		*/
2721b6bacd31SGregory Neil Shapiro 
2722b6bacd31SGregory Neil Shapiro 		if (mci->mci_state == MCIS_CLOSED)
2723b6bacd31SGregory Neil Shapiro 		{
2724b6bacd31SGregory Neil Shapiro 			errno = mci->mci_errno;
2725b6bacd31SGregory Neil Shapiro 			return EX_TEMPFAIL;
2726b6bacd31SGregory Neil Shapiro 		}
272740266059SGregory Neil Shapiro 	}
272840266059SGregory Neil Shapiro #endif /* PIPELINING */
272940266059SGregory Neil Shapiro 
273040266059SGregory Neil Shapiro 	/* now proceed with DATA phase */
2731c2aa98e2SPeter Wemm 	SmtpPhase = mci->mci_phase = "client DATA 354";
273240266059SGregory Neil Shapiro 	mci->mci_state = MCIS_DATA;
273340266059SGregory Neil Shapiro 	sm_setproctitle(true, e, "%s %s: %s",
273406f25ae9SGregory Neil Shapiro 			qid_printname(e), CurHostName, mci->mci_phase);
2735*d39bd2c1SGregory Neil Shapiro 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc, XS_DATA, NULL);
2736c2aa98e2SPeter Wemm 	if (r < 0 || REPLYTYPE(r) == 4)
2737c2aa98e2SPeter Wemm 	{
2738605302a5SGregory Neil Shapiro 		if (r >= 0)
2739c2aa98e2SPeter Wemm 			smtpquit(m, mci, e);
274040266059SGregory Neil Shapiro 		errno = mci->mci_errno;
2741c2aa98e2SPeter Wemm 		return EX_TEMPFAIL;
2742c2aa98e2SPeter Wemm 	}
2743c2aa98e2SPeter Wemm 	else if (REPLYTYPE(r) == 5)
2744c2aa98e2SPeter Wemm 	{
2745c2aa98e2SPeter Wemm 		smtprset(m, mci, e);
27462fb4f839SGregory Neil Shapiro 		if (mci->mci_okrcpts <= 0 && mci->mci_retryrcpt)
27472fb4f839SGregory Neil Shapiro 			return EX_TEMPFAIL;
2748c2aa98e2SPeter Wemm 		return EX_UNAVAILABLE;
2749c2aa98e2SPeter Wemm 	}
27502e43090eSPeter Wemm 	else if (REPLYTYPE(r) != 3)
2751c2aa98e2SPeter Wemm 	{
2752c2aa98e2SPeter Wemm 		if (LogLevel > 1)
2753c2aa98e2SPeter Wemm 		{
2754c2aa98e2SPeter Wemm 			sm_syslog(LOG_CRIT, e->e_id,
2755c2aa98e2SPeter Wemm 				  "%.100s: SMTP DATA-1 protocol error: %s",
2756c2aa98e2SPeter Wemm 				  CurHostName,
2757c2aa98e2SPeter Wemm 				  shortenstring(SmtpReplyBuffer, 403));
2758c2aa98e2SPeter Wemm 		}
2759c2aa98e2SPeter Wemm 		smtprset(m, mci, e);
276006f25ae9SGregory Neil Shapiro 		mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
276106f25ae9SGregory Neil Shapiro 			    SmtpReplyBuffer);
27622fb4f839SGregory Neil Shapiro 		if (mci->mci_okrcpts <= 0 && mci->mci_retryrcpt)
27632fb4f839SGregory Neil Shapiro 			return EX_TEMPFAIL;
276406f25ae9SGregory Neil Shapiro 		return EX_PROTOCOL;
2765c2aa98e2SPeter Wemm 	}
2766c2aa98e2SPeter Wemm 
276740266059SGregory Neil Shapiro 	if (mci->mci_okrcpts > 0)
276840266059SGregory Neil Shapiro 	{
2769c2aa98e2SPeter Wemm 		/*
27702fb4f839SGregory Neil Shapiro 		**  Set timeout around data writes.  Make it at least
27712fb4f839SGregory Neil Shapiro 		**  large enough for DNS timeouts on all recipients
27722fb4f839SGregory Neil Shapiro 		**  plus some fudge factor.  The main thing is
27732fb4f839SGregory Neil Shapiro 		**  that it should not be infinite.
2774c2aa98e2SPeter Wemm 		*/
2775c2aa98e2SPeter Wemm 
277606f25ae9SGregory Neil Shapiro 		if (tTd(18, 101))
277706f25ae9SGregory Neil Shapiro 		{
277806f25ae9SGregory Neil Shapiro 			/* simulate a DATA timeout */
27794e4196cbSGregory Neil Shapiro 			timeout = 10;
278006f25ae9SGregory Neil Shapiro 		}
278106f25ae9SGregory Neil Shapiro 		else
27824e4196cbSGregory Neil Shapiro 			timeout = DATA_PROGRESS_TIMEOUT * 1000;
27834e4196cbSGregory Neil Shapiro 		sm_io_setinfo(mci->mci_out, SM_IO_WHAT_TIMEOUT, &timeout);
2784c2aa98e2SPeter Wemm 
2785c2aa98e2SPeter Wemm 		/*
2786c2aa98e2SPeter Wemm 		**  Output the actual message.
2787c2aa98e2SPeter Wemm 		*/
2788c2aa98e2SPeter Wemm 
27894e4196cbSGregory Neil Shapiro 		if (!(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER))
27904e4196cbSGregory Neil Shapiro 			goto writeerr;
27918774250cSGregory Neil Shapiro 
27928774250cSGregory Neil Shapiro 		if (tTd(18, 101))
27938774250cSGregory Neil Shapiro 		{
27948774250cSGregory Neil Shapiro 			/* simulate a DATA timeout */
27958774250cSGregory Neil Shapiro 			(void) sleep(2);
27968774250cSGregory Neil Shapiro 		}
27978774250cSGregory Neil Shapiro 
27984e4196cbSGregory Neil Shapiro 		if (!(*e->e_putbody)(mci, e, NULL))
27994e4196cbSGregory Neil Shapiro 			goto writeerr;
2800c2aa98e2SPeter Wemm 
2801c2aa98e2SPeter Wemm 		/*
2802c2aa98e2SPeter Wemm 		**  Cleanup after sending message.
2803c2aa98e2SPeter Wemm 		*/
280440266059SGregory Neil Shapiro 	}
2805c2aa98e2SPeter Wemm 
280606f25ae9SGregory Neil Shapiro #if _FFR_CATCH_BROKEN_MTAS
280713bd1963SGregory Neil Shapiro 	if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
280806f25ae9SGregory Neil Shapiro 	{
280906f25ae9SGregory Neil Shapiro 		/* terminate the message */
281040266059SGregory Neil Shapiro 		(void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s",
281140266059SGregory Neil Shapiro 				     m->m_eol);
281206f25ae9SGregory Neil Shapiro 		if (TrafficLogFile != NULL)
281340266059SGregory Neil Shapiro 			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
281440266059SGregory Neil Shapiro 					     "%05d >>> .\n", (int) CurrentPid);
281506f25ae9SGregory Neil Shapiro 		if (Verbose)
281606f25ae9SGregory Neil Shapiro 			nmessage(">>> .");
281706f25ae9SGregory Neil Shapiro 
281813058a91SGregory Neil Shapiro 		sm_syslog(LOG_CRIT, e->e_id,
281913058a91SGregory Neil Shapiro 			  "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
282013058a91SGregory Neil Shapiro 			  CurHostName);
282106f25ae9SGregory Neil Shapiro 		mci->mci_errno = EIO;
282206f25ae9SGregory Neil Shapiro 		mci->mci_state = MCIS_ERROR;
282306f25ae9SGregory Neil Shapiro 		mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
282406f25ae9SGregory Neil Shapiro 		smtpquit(m, mci, e);
282506f25ae9SGregory Neil Shapiro 		return EX_PROTOCOL;
282606f25ae9SGregory Neil Shapiro 	}
282706f25ae9SGregory Neil Shapiro #endif /* _FFR_CATCH_BROKEN_MTAS */
282806f25ae9SGregory Neil Shapiro 
282940266059SGregory Neil Shapiro 	if (sm_io_error(mci->mci_out))
2830c2aa98e2SPeter Wemm 	{
2831c2aa98e2SPeter Wemm 		/* error during processing -- don't send the dot */
2832c2aa98e2SPeter Wemm 		mci->mci_errno = EIO;
2833c2aa98e2SPeter Wemm 		mci->mci_state = MCIS_ERROR;
2834c2aa98e2SPeter Wemm 		mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
2835c2aa98e2SPeter Wemm 		smtpquit(m, mci, e);
2836c2aa98e2SPeter Wemm 		return EX_IOERR;
2837c2aa98e2SPeter Wemm 	}
2838c2aa98e2SPeter Wemm 
2839c2aa98e2SPeter Wemm 	/* terminate the message */
2840ffb83623SGregory Neil Shapiro 	if (sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s.%s",
2841ffb83623SGregory Neil Shapiro 			bitset(MCIF_INLONGLINE, mci->mci_flags) ? m->m_eol : "",
2842ffb83623SGregory Neil Shapiro 			m->m_eol) == SM_IO_EOF)
28434e4196cbSGregory Neil Shapiro 		goto writeerr;
2844c2aa98e2SPeter Wemm 	if (TrafficLogFile != NULL)
284540266059SGregory Neil Shapiro 		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
284640266059SGregory Neil Shapiro 				     "%05d >>> .\n", (int) CurrentPid);
2847c2aa98e2SPeter Wemm 	if (Verbose)
2848c2aa98e2SPeter Wemm 		nmessage(">>> .");
2849c2aa98e2SPeter Wemm 
2850c2aa98e2SPeter Wemm 	/* check for the results of the transaction */
2851c2aa98e2SPeter Wemm 	SmtpPhase = mci->mci_phase = "client DATA status";
285240266059SGregory Neil Shapiro 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
285306f25ae9SGregory Neil Shapiro 			CurHostName, mci->mci_phase);
2854c2aa98e2SPeter Wemm 	if (bitnset(M_LMTP, m->m_flags))
2855c2aa98e2SPeter Wemm 		return EX_OK;
2856*d39bd2c1SGregory Neil Shapiro 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_EOM, NULL);
2857c2aa98e2SPeter Wemm 	if (r < 0)
2858c2aa98e2SPeter Wemm 		return EX_TEMPFAIL;
2859b6bacd31SGregory Neil Shapiro 	if (mci->mci_state == MCIS_DATA)
2860c2aa98e2SPeter Wemm 		mci->mci_state = MCIS_OPEN;
2861c2aa98e2SPeter Wemm 	xstat = EX_NOTSTICKY;
2862c2aa98e2SPeter Wemm 	if (r == 452)
2863c2aa98e2SPeter Wemm 		rstat = EX_TEMPFAIL;
2864c2aa98e2SPeter Wemm 	else if (REPLYTYPE(r) == 4)
2865c2aa98e2SPeter Wemm 		rstat = xstat = EX_TEMPFAIL;
2866c2aa98e2SPeter Wemm 	else if (REPLYTYPE(r) == 2)
2867c2aa98e2SPeter Wemm 		rstat = xstat = EX_OK;
286840266059SGregory Neil Shapiro 	else if (REPLYCLASS(r) != 5)
286940266059SGregory Neil Shapiro 		rstat = xstat = EX_PROTOCOL;
2870c2aa98e2SPeter Wemm 	else if (REPLYTYPE(r) == 5)
2871c2aa98e2SPeter Wemm 		rstat = EX_UNAVAILABLE;
2872c2aa98e2SPeter Wemm 	else
2873c2aa98e2SPeter Wemm 		rstat = EX_PROTOCOL;
287406f25ae9SGregory Neil Shapiro 	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
287506f25ae9SGregory Neil Shapiro 		    SmtpReplyBuffer);
287606f25ae9SGregory Neil Shapiro 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
287706f25ae9SGregory Neil Shapiro 	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
287806f25ae9SGregory Neil Shapiro 		r += 5;
287906f25ae9SGregory Neil Shapiro 	else
288006f25ae9SGregory Neil Shapiro 		r = 4;
288140266059SGregory Neil Shapiro 	e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
2882602a2b1bSGregory Neil Shapiro 	SmtpPhase = mci->mci_phase = "idle";
288340266059SGregory Neil Shapiro 	sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase);
2884c2aa98e2SPeter Wemm 	if (rstat != EX_PROTOCOL)
2885c2aa98e2SPeter Wemm 		return rstat;
2886c2aa98e2SPeter Wemm 	if (LogLevel > 1)
2887c2aa98e2SPeter Wemm 	{
2888c2aa98e2SPeter Wemm 		sm_syslog(LOG_CRIT, e->e_id,
2889c2aa98e2SPeter Wemm 			  "%.100s: SMTP DATA-2 protocol error: %s",
2890c2aa98e2SPeter Wemm 			  CurHostName,
2891c2aa98e2SPeter Wemm 			  shortenstring(SmtpReplyBuffer, 403));
2892c2aa98e2SPeter Wemm 	}
2893c2aa98e2SPeter Wemm 	return rstat;
2894c2aa98e2SPeter Wemm 
28954e4196cbSGregory Neil Shapiro   writeerr:
28964e4196cbSGregory Neil Shapiro 	mci->mci_errno = errno;
28974e4196cbSGregory Neil Shapiro 	mci->mci_state = MCIS_ERROR;
2898da7d7b9cSGregory Neil Shapiro 	mci_setstat(mci, bitset(MCIF_NOTSTICKY, mci->mci_flags)
2899da7d7b9cSGregory Neil Shapiro 			 ? EX_NOTSTICKY: EX_TEMPFAIL,
2900da7d7b9cSGregory Neil Shapiro 		    "4.4.2", NULL);
2901da7d7b9cSGregory Neil Shapiro 	mci->mci_flags &= ~MCIF_NOTSTICKY;
29028774250cSGregory Neil Shapiro 
29038774250cSGregory Neil Shapiro 	/*
29044e4196cbSGregory Neil Shapiro 	**  If putbody() couldn't finish due to a timeout,
29054e4196cbSGregory Neil Shapiro 	**  rewind it here in the timeout handler.  See
29064e4196cbSGregory Neil Shapiro 	**  comments at the end of putbody() for reasoning.
29078774250cSGregory Neil Shapiro 	*/
29088774250cSGregory Neil Shapiro 
29094e4196cbSGregory Neil Shapiro 	if (e->e_dfp != NULL)
29104e4196cbSGregory Neil Shapiro 		(void) bfrewind(e->e_dfp);
291106f25ae9SGregory Neil Shapiro 
29124e4196cbSGregory Neil Shapiro 	errno = mci->mci_errno;
2913da7d7b9cSGregory Neil Shapiro 	syserr("+451 4.4.1 timeout writing message to %s", CurHostName);
29144e4196cbSGregory Neil Shapiro 	smtpquit(m, mci, e);
29154e4196cbSGregory Neil Shapiro 	return EX_TEMPFAIL;
29168774250cSGregory Neil Shapiro }
29178774250cSGregory Neil Shapiro 
291840266059SGregory Neil Shapiro /*
2919c2aa98e2SPeter Wemm **  SMTPGETSTAT -- get status code from DATA in LMTP
2920c2aa98e2SPeter Wemm **
2921c2aa98e2SPeter Wemm **	Parameters:
2922c2aa98e2SPeter Wemm **		m -- the mailer to which we are sending the message.
2923c2aa98e2SPeter Wemm **		mci -- the mailer connection structure.
2924c2aa98e2SPeter Wemm **		e -- the current envelope.
2925c2aa98e2SPeter Wemm **
2926c2aa98e2SPeter Wemm **	Returns:
2927c2aa98e2SPeter Wemm **		The exit status corresponding to the reply code.
2928c2aa98e2SPeter Wemm */
2929c2aa98e2SPeter Wemm 
2930c2aa98e2SPeter Wemm int
smtpgetstat(m,mci,e)2931c2aa98e2SPeter Wemm smtpgetstat(m, mci, e)
2932c2aa98e2SPeter Wemm 	MAILER *m;
2933c2aa98e2SPeter Wemm 	MCI *mci;
2934c2aa98e2SPeter Wemm 	ENVELOPE *e;
2935c2aa98e2SPeter Wemm {
2936c2aa98e2SPeter Wemm 	int r;
29375ef517c0SGregory Neil Shapiro 	int off;
293840266059SGregory Neil Shapiro 	int status, xstat;
293906f25ae9SGregory Neil Shapiro 	char *enhsc;
2940c2aa98e2SPeter Wemm 
294106f25ae9SGregory Neil Shapiro 	enhsc = NULL;
294240266059SGregory Neil Shapiro 
2943c2aa98e2SPeter Wemm 	/* check for the results of the transaction */
2944*d39bd2c1SGregory Neil Shapiro 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DATA2,
2945*d39bd2c1SGregory Neil Shapiro 		  NULL);
2946c2aa98e2SPeter Wemm 	if (r < 0)
2947c2aa98e2SPeter Wemm 		return EX_TEMPFAIL;
294840266059SGregory Neil Shapiro 	xstat = EX_NOTSTICKY;
294906f25ae9SGregory Neil Shapiro 	if (REPLYTYPE(r) == 4)
295006f25ae9SGregory Neil Shapiro 		status = EX_TEMPFAIL;
295106f25ae9SGregory Neil Shapiro 	else if (REPLYTYPE(r) == 2)
295240266059SGregory Neil Shapiro 		status = xstat = EX_OK;
295340266059SGregory Neil Shapiro 	else if (REPLYCLASS(r) != 5)
295440266059SGregory Neil Shapiro 		status = xstat = EX_PROTOCOL;
295506f25ae9SGregory Neil Shapiro 	else if (REPLYTYPE(r) == 5)
295606f25ae9SGregory Neil Shapiro 		status = EX_UNAVAILABLE;
295706f25ae9SGregory Neil Shapiro 	else
295806f25ae9SGregory Neil Shapiro 		status = EX_PROTOCOL;
295906f25ae9SGregory Neil Shapiro 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
29605ef517c0SGregory Neil Shapiro 	    (off = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
29615ef517c0SGregory Neil Shapiro 		off += 5;
2962c2aa98e2SPeter Wemm 	else
29635ef517c0SGregory Neil Shapiro 		off = 4;
29645ef517c0SGregory Neil Shapiro 	e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[off]);
29655ef517c0SGregory Neil Shapiro 	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)), SmtpReplyBuffer);
296606f25ae9SGregory Neil Shapiro 	if (LogLevel > 1 && status == EX_PROTOCOL)
2967c2aa98e2SPeter Wemm 	{
2968c2aa98e2SPeter Wemm 		sm_syslog(LOG_CRIT, e->e_id,
2969c2aa98e2SPeter Wemm 			  "%.100s: SMTP DATA-3 protocol error: %s",
2970c2aa98e2SPeter Wemm 			  CurHostName,
2971c2aa98e2SPeter Wemm 			  shortenstring(SmtpReplyBuffer, 403));
2972c2aa98e2SPeter Wemm 	}
297306f25ae9SGregory Neil Shapiro 	return status;
2974c2aa98e2SPeter Wemm }
297540266059SGregory Neil Shapiro /*
2976c2aa98e2SPeter Wemm **  SMTPQUIT -- close the SMTP connection.
2977c2aa98e2SPeter Wemm **
2978c2aa98e2SPeter Wemm **	Parameters:
2979c2aa98e2SPeter Wemm **		m -- a pointer to the mailer.
2980c2aa98e2SPeter Wemm **		mci -- the mailer connection information.
2981c2aa98e2SPeter Wemm **		e -- the current envelope.
2982c2aa98e2SPeter Wemm **
2983c2aa98e2SPeter Wemm **	Returns:
2984c2aa98e2SPeter Wemm **		none.
2985c2aa98e2SPeter Wemm **
2986c2aa98e2SPeter Wemm **	Side Effects:
2987c2aa98e2SPeter Wemm **		sends the final protocol and closes the connection.
2988c2aa98e2SPeter Wemm */
2989c2aa98e2SPeter Wemm 
2990c2aa98e2SPeter Wemm void
smtpquit(m,mci,e)2991c2aa98e2SPeter Wemm smtpquit(m, mci, e)
2992c2aa98e2SPeter Wemm 	register MAILER *m;
2993c2aa98e2SPeter Wemm 	register MCI *mci;
2994c2aa98e2SPeter Wemm 	ENVELOPE *e;
2995c2aa98e2SPeter Wemm {
2996c2aa98e2SPeter Wemm 	bool oldSuprErrs = SuprErrs;
299706f25ae9SGregory Neil Shapiro 	int rcode;
299840266059SGregory Neil Shapiro 	char *oldcurhost;
299906f25ae9SGregory Neil Shapiro 
3000605302a5SGregory Neil Shapiro 	if (mci->mci_state == MCIS_CLOSED)
300127bec481SGregory Neil Shapiro 	{
300227bec481SGregory Neil Shapiro 		mci_close(mci, "smtpquit:1");
3003605302a5SGregory Neil Shapiro 		return;
300427bec481SGregory Neil Shapiro 	}
3005605302a5SGregory Neil Shapiro 
300640266059SGregory Neil Shapiro 	oldcurhost = CurHostName;
300706f25ae9SGregory Neil Shapiro 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
300806f25ae9SGregory Neil Shapiro 	if (CurHostName == NULL)
300906f25ae9SGregory Neil Shapiro 		CurHostName = MyHostName;
3010c2aa98e2SPeter Wemm 
3011c2aa98e2SPeter Wemm 	/*
3012c2aa98e2SPeter Wemm 	**	Suppress errors here -- we may be processing a different
3013c2aa98e2SPeter Wemm 	**	job when we do the quit connection, and we don't want the
3014c2aa98e2SPeter Wemm 	**	new job to be penalized for something that isn't it's
3015c2aa98e2SPeter Wemm 	**	problem.
3016c2aa98e2SPeter Wemm 	*/
3017c2aa98e2SPeter Wemm 
301840266059SGregory Neil Shapiro 	SuprErrs = true;
3019c2aa98e2SPeter Wemm 
3020c2aa98e2SPeter Wemm 	/* send the quit message if we haven't gotten I/O error */
302106f25ae9SGregory Neil Shapiro 	if (mci->mci_state != MCIS_ERROR &&
302206f25ae9SGregory Neil Shapiro 	    mci->mci_state != MCIS_QUITING)
3023c2aa98e2SPeter Wemm 	{
3024c2aa98e2SPeter Wemm 		SmtpPhase = "client QUIT";
302506f25ae9SGregory Neil Shapiro 		mci->mci_state = MCIS_QUITING;
3026c2aa98e2SPeter Wemm 		smtpmessage("QUIT", m, mci);
3027*d39bd2c1SGregory Neil Shapiro 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL, XS_QUIT,
3028*d39bd2c1SGregory Neil Shapiro 			     NULL);
3029c2aa98e2SPeter Wemm 		SuprErrs = oldSuprErrs;
3030605302a5SGregory Neil Shapiro 		if (mci->mci_state == MCIS_CLOSED)
303140266059SGregory Neil Shapiro 			goto end;
3032c2aa98e2SPeter Wemm 	}
3033c2aa98e2SPeter Wemm 
3034c2aa98e2SPeter Wemm 	/* now actually close the connection and pick up the zombie */
303506f25ae9SGregory Neil Shapiro 	rcode = endmailer(mci, e, NULL);
303606f25ae9SGregory Neil Shapiro 	if (rcode != EX_OK)
303706f25ae9SGregory Neil Shapiro 	{
303806f25ae9SGregory Neil Shapiro 		char *mailer = NULL;
303906f25ae9SGregory Neil Shapiro 
304006f25ae9SGregory Neil Shapiro 		if (mci->mci_mailer != NULL &&
304106f25ae9SGregory Neil Shapiro 		    mci->mci_mailer->m_name != NULL)
304206f25ae9SGregory Neil Shapiro 			mailer = mci->mci_mailer->m_name;
304306f25ae9SGregory Neil Shapiro 
304406f25ae9SGregory Neil Shapiro 		/* look for naughty mailers */
304506f25ae9SGregory Neil Shapiro 		sm_syslog(LOG_ERR, e->e_id,
3046193538b7SGregory Neil Shapiro 			  "smtpquit: mailer%s%s exited with exit value %d",
304706f25ae9SGregory Neil Shapiro 			  mailer == NULL ? "" : " ",
304806f25ae9SGregory Neil Shapiro 			  mailer == NULL ? "" : mailer,
304906f25ae9SGregory Neil Shapiro 			  rcode);
305006f25ae9SGregory Neil Shapiro 	}
3051c2aa98e2SPeter Wemm 
3052c2aa98e2SPeter Wemm 	SuprErrs = oldSuprErrs;
305340266059SGregory Neil Shapiro 
305440266059SGregory Neil Shapiro   end:
305540266059SGregory Neil Shapiro 	CurHostName = oldcurhost;
305640266059SGregory Neil Shapiro 	return;
3057c2aa98e2SPeter Wemm }
305840266059SGregory Neil Shapiro /*
3059c2aa98e2SPeter Wemm **  SMTPRSET -- send a RSET (reset) command
3060602a2b1bSGregory Neil Shapiro **
3061602a2b1bSGregory Neil Shapiro **	Parameters:
3062602a2b1bSGregory Neil Shapiro **		m -- a pointer to the mailer.
3063602a2b1bSGregory Neil Shapiro **		mci -- the mailer connection information.
3064602a2b1bSGregory Neil Shapiro **		e -- the current envelope.
3065602a2b1bSGregory Neil Shapiro **
3066602a2b1bSGregory Neil Shapiro **	Returns:
3067602a2b1bSGregory Neil Shapiro **		none.
3068602a2b1bSGregory Neil Shapiro **
3069602a2b1bSGregory Neil Shapiro **	Side Effects:
3070602a2b1bSGregory Neil Shapiro **		closes the connection if there is no reply to RSET.
3071c2aa98e2SPeter Wemm */
3072c2aa98e2SPeter Wemm 
3073c2aa98e2SPeter Wemm void
smtprset(m,mci,e)3074c2aa98e2SPeter Wemm smtprset(m, mci, e)
3075c2aa98e2SPeter Wemm 	register MAILER *m;
3076c2aa98e2SPeter Wemm 	register MCI *mci;
3077c2aa98e2SPeter Wemm 	ENVELOPE *e;
3078c2aa98e2SPeter Wemm {
3079c2aa98e2SPeter Wemm 	int r;
3080c2aa98e2SPeter Wemm 
308106f25ae9SGregory Neil Shapiro 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
308206f25ae9SGregory Neil Shapiro 	if (CurHostName == NULL)
308306f25ae9SGregory Neil Shapiro 		CurHostName = MyHostName;
308406f25ae9SGregory Neil Shapiro 
308540266059SGregory Neil Shapiro 	/*
308640266059SGregory Neil Shapiro 	**  Check if connection is gone, if so
308740266059SGregory Neil Shapiro 	**  it's a tempfail and we use mci_errno
308840266059SGregory Neil Shapiro 	**  for the reason.
308940266059SGregory Neil Shapiro 	*/
309040266059SGregory Neil Shapiro 
309140266059SGregory Neil Shapiro 	if (mci->mci_state == MCIS_CLOSED)
309240266059SGregory Neil Shapiro 	{
309340266059SGregory Neil Shapiro 		errno = mci->mci_errno;
309440266059SGregory Neil Shapiro 		return;
309540266059SGregory Neil Shapiro 	}
309640266059SGregory Neil Shapiro 
3097c2aa98e2SPeter Wemm 	SmtpPhase = "client RSET";
3098c2aa98e2SPeter Wemm 	smtpmessage("RSET", m, mci);
3099*d39bd2c1SGregory Neil Shapiro 	r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL, XS_DEFAULT, NULL);
3100c2aa98e2SPeter Wemm 	if (r < 0)
3101605302a5SGregory Neil Shapiro 		return;
3102605302a5SGregory Neil Shapiro 
310306f25ae9SGregory Neil Shapiro 	/*
310406f25ae9SGregory Neil Shapiro 	**  Any response is deemed to be acceptable.
310506f25ae9SGregory Neil Shapiro 	**  The standard does not state the proper action
310606f25ae9SGregory Neil Shapiro 	**  to take when a value other than 250 is received.
3107193538b7SGregory Neil Shapiro 	**
3108193538b7SGregory Neil Shapiro 	**  However, if 421 is returned for the RSET, leave
3109b6bacd31SGregory Neil Shapiro 	**  mci_state alone (MCIS_SSD can be set in reply()
3110b6bacd31SGregory Neil Shapiro 	**  and MCIS_CLOSED can be set in smtpquit() if
3111b6bacd31SGregory Neil Shapiro 	**  reply() gets a 421 and calls smtpquit()).
311206f25ae9SGregory Neil Shapiro 	*/
311306f25ae9SGregory Neil Shapiro 
3114b6bacd31SGregory Neil Shapiro 	if (mci->mci_state != MCIS_SSD && mci->mci_state != MCIS_CLOSED)
3115c2aa98e2SPeter Wemm 		mci->mci_state = MCIS_OPEN;
31164e4196cbSGregory Neil Shapiro 	else if (mci->mci_exitstat == EX_OK)
31174e4196cbSGregory Neil Shapiro 		mci_setstat(mci, EX_TEMPFAIL, "4.5.0", NULL);
3118c2aa98e2SPeter Wemm }
311940266059SGregory Neil Shapiro /*
3120c2aa98e2SPeter Wemm **  SMTPPROBE -- check the connection state
3121602a2b1bSGregory Neil Shapiro **
3122602a2b1bSGregory Neil Shapiro **	Parameters:
3123602a2b1bSGregory Neil Shapiro **		mci -- the mailer connection information.
3124602a2b1bSGregory Neil Shapiro **
3125602a2b1bSGregory Neil Shapiro **	Returns:
3126602a2b1bSGregory Neil Shapiro **		none.
3127602a2b1bSGregory Neil Shapiro **
3128602a2b1bSGregory Neil Shapiro **	Side Effects:
3129602a2b1bSGregory Neil Shapiro **		closes the connection if there is no reply to RSET.
3130c2aa98e2SPeter Wemm */
3131c2aa98e2SPeter Wemm 
3132c2aa98e2SPeter Wemm int
smtpprobe(mci)3133c2aa98e2SPeter Wemm smtpprobe(mci)
3134c2aa98e2SPeter Wemm 	register MCI *mci;
3135c2aa98e2SPeter Wemm {
3136c2aa98e2SPeter Wemm 	int r;
3137c2aa98e2SPeter Wemm 	MAILER *m = mci->mci_mailer;
313806f25ae9SGregory Neil Shapiro 	ENVELOPE *e;
3139c2aa98e2SPeter Wemm 	extern ENVELOPE BlankEnvelope;
3140c2aa98e2SPeter Wemm 
314106f25ae9SGregory Neil Shapiro 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
314206f25ae9SGregory Neil Shapiro 	if (CurHostName == NULL)
314306f25ae9SGregory Neil Shapiro 		CurHostName = MyHostName;
314406f25ae9SGregory Neil Shapiro 
314506f25ae9SGregory Neil Shapiro 	e = &BlankEnvelope;
3146c2aa98e2SPeter Wemm 	SmtpPhase = "client probe";
3147c2aa98e2SPeter Wemm 	smtpmessage("RSET", m, mci);
3148*d39bd2c1SGregory Neil Shapiro 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL, XS_DEFAULT,
3149*d39bd2c1SGregory Neil Shapiro 		  NULL);
3150605302a5SGregory Neil Shapiro 	if (REPLYTYPE(r) != 2)
3151c2aa98e2SPeter Wemm 		smtpquit(m, mci, e);
3152c2aa98e2SPeter Wemm 	return r;
3153c2aa98e2SPeter Wemm }
315440266059SGregory Neil Shapiro /*
3155c2aa98e2SPeter Wemm **  REPLY -- read arpanet reply
3156c2aa98e2SPeter Wemm **
3157c2aa98e2SPeter Wemm **	Parameters:
3158c2aa98e2SPeter Wemm **		m -- the mailer we are reading the reply from.
3159c2aa98e2SPeter Wemm **		mci -- the mailer connection info structure.
3160c2aa98e2SPeter Wemm **		e -- the current envelope.
3161c2aa98e2SPeter Wemm **		timeout -- the timeout for reads.
3162c2aa98e2SPeter Wemm **		pfunc -- processing function called on each line of response.
3163c2aa98e2SPeter Wemm **			If null, no special processing is done.
316440266059SGregory Neil Shapiro **		enhstat -- optional, returns enhanced error code string (if set)
3165e92d3f3fSGregory Neil Shapiro **		rtype -- type of SmtpMsgBuffer: does it contains secret data?
3166*d39bd2c1SGregory Neil Shapiro **		rtext -- pointer to where to save first line of reply (if set)
3167c2aa98e2SPeter Wemm **
3168c2aa98e2SPeter Wemm **	Returns:
3169c2aa98e2SPeter Wemm **		reply code it reads.
3170*d39bd2c1SGregory Neil Shapiro **		-1 on I/O errors etc.
3171c2aa98e2SPeter Wemm **
3172c2aa98e2SPeter Wemm **	Side Effects:
3173c2aa98e2SPeter Wemm **		flushes the mail file.
3174c2aa98e2SPeter Wemm */
3175c2aa98e2SPeter Wemm 
3176c2aa98e2SPeter Wemm int
reply(m,mci,e,timeout,pfunc,enhstat,rtype,rtext)3177*d39bd2c1SGregory Neil Shapiro reply(m, mci, e, timeout, pfunc, enhstat, rtype, rtext)
3178c2aa98e2SPeter Wemm 	MAILER *m;
3179c2aa98e2SPeter Wemm 	MCI *mci;
3180c2aa98e2SPeter Wemm 	ENVELOPE *e;
3181c2aa98e2SPeter Wemm 	time_t timeout;
3182b6bacd31SGregory Neil Shapiro 	void (*pfunc) __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
318306f25ae9SGregory Neil Shapiro 	char **enhstat;
3184e92d3f3fSGregory Neil Shapiro 	int rtype;
3185*d39bd2c1SGregory Neil Shapiro 	char **rtext;
3186c2aa98e2SPeter Wemm {
3187c2aa98e2SPeter Wemm 	register char *bufp;
3188c2aa98e2SPeter Wemm 	register int r;
318940266059SGregory Neil Shapiro 	bool firstline = true;
3190c2aa98e2SPeter Wemm 	char junkbuf[MAXLINE];
319106f25ae9SGregory Neil Shapiro 	static char enhstatcode[ENHSCLEN];
319206f25ae9SGregory Neil Shapiro 	int save_errno;
3193c2aa98e2SPeter Wemm 
319440266059SGregory Neil Shapiro 	/*
319540266059SGregory Neil Shapiro 	**  Flush the output before reading response.
319640266059SGregory Neil Shapiro 	**
319740266059SGregory Neil Shapiro 	**	For SMTP pipelining, it would be better if we didn't do
319840266059SGregory Neil Shapiro 	**	this if there was already data waiting to be read.  But
319940266059SGregory Neil Shapiro 	**	to do it properly means pushing it to the I/O library,
320040266059SGregory Neil Shapiro 	**	since it really needs to be done below the buffer layer.
320140266059SGregory Neil Shapiro 	*/
320240266059SGregory Neil Shapiro 
3203c2aa98e2SPeter Wemm 	if (mci->mci_out != NULL)
320440266059SGregory Neil Shapiro 		(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3205c2aa98e2SPeter Wemm 
3206c2aa98e2SPeter Wemm 	if (tTd(18, 1))
32075b0945b5SGregory Neil Shapiro 	{
32085b0945b5SGregory Neil Shapiro 		char *what;
32095b0945b5SGregory Neil Shapiro 
32105b0945b5SGregory Neil Shapiro 		if (SmtpMsgBuffer[0] != '\0')
32115b0945b5SGregory Neil Shapiro 			what = SmtpMsgBuffer;
32125b0945b5SGregory Neil Shapiro 		else if (SmtpPhase != NULL && SmtpPhase[0] != '\0')
32135b0945b5SGregory Neil Shapiro 			what = SmtpPhase;
32145b0945b5SGregory Neil Shapiro 		else if (XS_GREET == rtype)
32155b0945b5SGregory Neil Shapiro 			what = "greeting";
32165b0945b5SGregory Neil Shapiro 		else
32175b0945b5SGregory Neil Shapiro 			what = "unknown";
3218*d39bd2c1SGregory Neil Shapiro #if PIPELINING
3219*d39bd2c1SGregory Neil Shapiro 		if (mci->mci_flags & MCIF_PIPELINED)
3220*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("reply to %s:%d [but PIPELINED]\n", what, rtype);
3221*d39bd2c1SGregory Neil Shapiro 		else
3222*d39bd2c1SGregory Neil Shapiro #endif
3223*d39bd2c1SGregory Neil Shapiro 		/* "else" in #if code above */
3224*d39bd2c1SGregory Neil Shapiro 		sm_dprintf("reply to %s:%d\n", what, rtype);
32255b0945b5SGregory Neil Shapiro 	}
3226c2aa98e2SPeter Wemm 
3227c2aa98e2SPeter Wemm 	/*
3228c2aa98e2SPeter Wemm 	**  Read the input line, being careful not to hang.
3229c2aa98e2SPeter Wemm 	*/
3230c2aa98e2SPeter Wemm 
3231c2aa98e2SPeter Wemm 	bufp = SmtpReplyBuffer;
3232da7d7b9cSGregory Neil Shapiro 	(void) set_tls_rd_tmo(timeout);
3233c2aa98e2SPeter Wemm 	for (;;)
3234c2aa98e2SPeter Wemm 	{
3235c2aa98e2SPeter Wemm 		register char *p;
3236c2aa98e2SPeter Wemm 
3237c2aa98e2SPeter Wemm 		/* actually do the read */
323840266059SGregory Neil Shapiro 		if (e->e_xfp != NULL)	/* for debugging */
323940266059SGregory Neil Shapiro 			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
3240c2aa98e2SPeter Wemm 
3241c2aa98e2SPeter Wemm 		/* if we are in the process of closing just give the code */
3242c2aa98e2SPeter Wemm 		if (mci->mci_state == MCIS_CLOSED)
324306f25ae9SGregory Neil Shapiro 			return SMTPCLOSING;
3244c2aa98e2SPeter Wemm 
3245b6bacd31SGregory Neil Shapiro 		/* don't try to read from a non-existent fd */
324640266059SGregory Neil Shapiro 		if (mci->mci_in == NULL)
324740266059SGregory Neil Shapiro 		{
324840266059SGregory Neil Shapiro 			if (mci->mci_errno == 0)
324940266059SGregory Neil Shapiro 				mci->mci_errno = EBADF;
3250605302a5SGregory Neil Shapiro 
3251605302a5SGregory Neil Shapiro 			/* errors on QUIT should be ignored */
3252605302a5SGregory Neil Shapiro 			if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3253605302a5SGregory Neil Shapiro 			{
3254605302a5SGregory Neil Shapiro 				errno = mci->mci_errno;
325527bec481SGregory Neil Shapiro 				mci_close(mci, "reply:1");
3256605302a5SGregory Neil Shapiro 				return -1;
3257605302a5SGregory Neil Shapiro 			}
3258605302a5SGregory Neil Shapiro 			mci->mci_state = MCIS_ERROR;
3259605302a5SGregory Neil Shapiro 			smtpquit(m, mci, e);
326040266059SGregory Neil Shapiro 			errno = mci->mci_errno;
326140266059SGregory Neil Shapiro 			return -1;
326240266059SGregory Neil Shapiro 		}
326340266059SGregory Neil Shapiro 
3264c2aa98e2SPeter Wemm 		if (mci->mci_out != NULL)
326540266059SGregory Neil Shapiro 			(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3266c2aa98e2SPeter Wemm 
3267c2aa98e2SPeter Wemm 		/* get the line from the other side */
3268c2aa98e2SPeter Wemm 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
326940266059SGregory Neil Shapiro 		save_errno = errno;
3270c2aa98e2SPeter Wemm 		mci->mci_lastuse = curtime();
3271c2aa98e2SPeter Wemm 
3272c2aa98e2SPeter Wemm 		if (p == NULL)
3273c2aa98e2SPeter Wemm 		{
3274c2aa98e2SPeter Wemm 			bool oldholderrs;
3275c2aa98e2SPeter Wemm 			extern char MsgBuf[];
3276c2aa98e2SPeter Wemm 
3277605302a5SGregory Neil Shapiro 			/* errors on QUIT should be ignored */
3278605302a5SGregory Neil Shapiro 			if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3279b6bacd31SGregory Neil Shapiro 			{
328027bec481SGregory Neil Shapiro 				mci_close(mci, "reply:2");
3281605302a5SGregory Neil Shapiro 				return -1;
3282b6bacd31SGregory Neil Shapiro 			}
3283605302a5SGregory Neil Shapiro 
3284c2aa98e2SPeter Wemm 			/* if the remote end closed early, fake an error */
328540266059SGregory Neil Shapiro 			errno = save_errno;
3286c2aa98e2SPeter Wemm 			if (errno == 0)
328740266059SGregory Neil Shapiro 			{
328840266059SGregory Neil Shapiro 				(void) sm_snprintf(SmtpReplyBuffer,
3289d0cef73dSGregory Neil Shapiro 						   sizeof(SmtpReplyBuffer),
329040266059SGregory Neil Shapiro 						   "421 4.4.1 Connection reset by %s",
329140266059SGregory Neil Shapiro 						   CURHOSTNAME);
3292c2aa98e2SPeter Wemm #ifdef ECONNRESET
3293c2aa98e2SPeter Wemm 				errno = ECONNRESET;
32945b0945b5SGregory Neil Shapiro #else
3295c2aa98e2SPeter Wemm 				errno = EPIPE;
32965b0945b5SGregory Neil Shapiro #endif
329740266059SGregory Neil Shapiro 			}
3298c2aa98e2SPeter Wemm 
3299c2aa98e2SPeter Wemm 			mci->mci_errno = errno;
3300c2aa98e2SPeter Wemm 			oldholderrs = HoldErrs;
330140266059SGregory Neil Shapiro 			HoldErrs = true;
3302*d39bd2c1SGregory Neil Shapiro 			usrerr("451 4.4.2 reply: read error from %s",
330340266059SGregory Neil Shapiro 			       CURHOSTNAME);
3304c2aa98e2SPeter Wemm 			mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
3305c2aa98e2SPeter Wemm 
3306c2aa98e2SPeter Wemm 			/* if debugging, pause so we can see state */
3307c2aa98e2SPeter Wemm 			if (tTd(18, 100))
330806f25ae9SGregory Neil Shapiro 				(void) pause();
3309c2aa98e2SPeter Wemm 			mci->mci_state = MCIS_ERROR;
3310c2aa98e2SPeter Wemm 			smtpquit(m, mci, e);
3311c2aa98e2SPeter Wemm #if XDEBUG
3312c2aa98e2SPeter Wemm 			{
3313c2aa98e2SPeter Wemm 				char wbuf[MAXLINE];
3314c2aa98e2SPeter Wemm 
331506f25ae9SGregory Neil Shapiro 				p = wbuf;
3316c2aa98e2SPeter Wemm 				if (e->e_to != NULL)
3317c2aa98e2SPeter Wemm 				{
331840266059SGregory Neil Shapiro 					(void) sm_snprintf(p,
331940266059SGregory Neil Shapiro 							   SPACELEFT(wbuf, p),
332040266059SGregory Neil Shapiro 							   "%s... ",
3321c2aa98e2SPeter Wemm 							   shortenstring(e->e_to, MAXSHORTSTR));
332240266059SGregory Neil Shapiro 					p += strlen(p);
3323c2aa98e2SPeter Wemm 				}
332440266059SGregory Neil Shapiro 				(void) sm_snprintf(p, SPACELEFT(wbuf, p),
332540266059SGregory Neil Shapiro 						   "reply(%.100s) during %s",
332640266059SGregory Neil Shapiro 						   CURHOSTNAME, SmtpPhase);
3327c2aa98e2SPeter Wemm 				checkfd012(wbuf);
3328c2aa98e2SPeter Wemm 			}
332906f25ae9SGregory Neil Shapiro #endif /* XDEBUG */
3330c2aa98e2SPeter Wemm 			HoldErrs = oldholderrs;
333140266059SGregory Neil Shapiro 			errno = save_errno;
333206f25ae9SGregory Neil Shapiro 			return -1;
3333c2aa98e2SPeter Wemm 		}
333440266059SGregory Neil Shapiro 		fixcrlf(bufp, true);
3335*d39bd2c1SGregory Neil Shapiro 		if (tTd(18, 4))
3336*d39bd2c1SGregory Neil Shapiro 			sm_dprintf("received=%s\n", bufp);
3337c2aa98e2SPeter Wemm 
3338c2aa98e2SPeter Wemm 		/* EHLO failure is not a real error */
3339c2aa98e2SPeter Wemm 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
3340c2aa98e2SPeter Wemm 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
3341c2aa98e2SPeter Wemm 		{
3342c2aa98e2SPeter Wemm 			/* serious error -- log the previous command */
3343c2aa98e2SPeter Wemm 			if (SmtpNeedIntro)
3344c2aa98e2SPeter Wemm 			{
3345c2aa98e2SPeter Wemm 				/* inform user who we are chatting with */
334640266059SGregory Neil Shapiro 				(void) sm_io_fprintf(CurEnv->e_xfp,
334740266059SGregory Neil Shapiro 						     SM_TIME_DEFAULT,
3348c2aa98e2SPeter Wemm 						     "... while talking to %s:\n",
334940266059SGregory Neil Shapiro 						     CURHOSTNAME);
335040266059SGregory Neil Shapiro 				SmtpNeedIntro = false;
3351c2aa98e2SPeter Wemm 			}
3352c2aa98e2SPeter Wemm 			if (SmtpMsgBuffer[0] != '\0')
3353e92d3f3fSGregory Neil Shapiro 			{
3354e92d3f3fSGregory Neil Shapiro 				(void) sm_io_fprintf(e->e_xfp,
3355e92d3f3fSGregory Neil Shapiro 					SM_TIME_DEFAULT,
3356e92d3f3fSGregory Neil Shapiro 					">>> %s\n",
3357e92d3f3fSGregory Neil Shapiro 					(rtype == XS_STARTTLS)
3358e92d3f3fSGregory Neil Shapiro 					? "STARTTLS dialogue"
3359e92d3f3fSGregory Neil Shapiro 					: ((rtype == XS_AUTH)
3360e92d3f3fSGregory Neil Shapiro 					   ? "AUTH dialogue"
3361e92d3f3fSGregory Neil Shapiro 					   : SmtpMsgBuffer));
3362c2aa98e2SPeter Wemm 				SmtpMsgBuffer[0] = '\0';
3363e92d3f3fSGregory Neil Shapiro 			}
3364c2aa98e2SPeter Wemm 
3365c2aa98e2SPeter Wemm 			/* now log the message as from the other side */
336640266059SGregory Neil Shapiro 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
336740266059SGregory Neil Shapiro 					     "<<< %s\n", bufp);
3368c2aa98e2SPeter Wemm 		}
3369c2aa98e2SPeter Wemm 
3370c2aa98e2SPeter Wemm 		/* display the input for verbose mode */
3371c2aa98e2SPeter Wemm 		if (Verbose)
3372c2aa98e2SPeter Wemm 			nmessage("050 %s", bufp);
3373c2aa98e2SPeter Wemm 
337406f25ae9SGregory Neil Shapiro 		/* ignore improperly formatted input */
337506f25ae9SGregory Neil Shapiro 		if (!ISSMTPREPLY(bufp))
3376c2aa98e2SPeter Wemm 			continue;
3377c2aa98e2SPeter Wemm 
3378*d39bd2c1SGregory Neil Shapiro 		if (NULL != rtext && firstline)
3379*d39bd2c1SGregory Neil Shapiro 			*rtext = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer);
3380*d39bd2c1SGregory Neil Shapiro 
338106f25ae9SGregory Neil Shapiro 		if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
338206f25ae9SGregory Neil Shapiro 		    enhstat != NULL &&
338306f25ae9SGregory Neil Shapiro 		    extenhsc(bufp + 4, ' ', enhstatcode) > 0)
338406f25ae9SGregory Neil Shapiro 			*enhstat = enhstatcode;
338506f25ae9SGregory Neil Shapiro 
3386c2aa98e2SPeter Wemm 		/* process the line */
3387c2aa98e2SPeter Wemm 		if (pfunc != NULL)
3388c2aa98e2SPeter Wemm 			(*pfunc)(bufp, firstline, m, mci, e);
3389c2aa98e2SPeter Wemm 
3390c2aa98e2SPeter Wemm 		/* decode the reply code */
3391c2aa98e2SPeter Wemm 		r = atoi(bufp);
3392c2aa98e2SPeter Wemm 
3393c2aa98e2SPeter Wemm 		/* extra semantics: 0xx codes are "informational" */
3394c2aa98e2SPeter Wemm 		if (r < 100)
33956f9c8e5bSGregory Neil Shapiro 		{
33966f9c8e5bSGregory Neil Shapiro 			firstline = false;
3397c2aa98e2SPeter Wemm 			continue;
33986f9c8e5bSGregory Neil Shapiro 		}
33995b0945b5SGregory Neil Shapiro 		if (REPLYTYPE(r) > 3 && firstline
3400da7d7b9cSGregory Neil Shapiro #if _FFR_PROXY
34015b0945b5SGregory Neil Shapiro 		    &&
34025b0945b5SGregory Neil Shapiro 		    (e->e_sendmode != SM_PROXY
34035b0945b5SGregory Neil Shapiro 		     || (e->e_sendmode == SM_PROXY
34045b0945b5SGregory Neil Shapiro 			 && (e->e_rcode == 0 || REPLYTYPE(e->e_rcode) < 5))
34055b0945b5SGregory Neil Shapiro 		    )
3406da7d7b9cSGregory Neil Shapiro #endif
34075b0945b5SGregory Neil Shapiro 		   )
3408da7d7b9cSGregory Neil Shapiro 		{
3409da7d7b9cSGregory Neil Shapiro 			int o = -1;
3410da7d7b9cSGregory Neil Shapiro 			/*
3411da7d7b9cSGregory Neil Shapiro 			**  ignore error iff: DATA, 5xy error, but we had
3412da7d7b9cSGregory Neil Shapiro 			**  "retryable" recipients. XREF: smtpdata()
3413da7d7b9cSGregory Neil Shapiro 			*/
3414da7d7b9cSGregory Neil Shapiro 
3415da7d7b9cSGregory Neil Shapiro 			if (!(rtype == XS_DATA && REPLYTYPE(r) == 5 &&
3416da7d7b9cSGregory Neil Shapiro 			      mci->mci_okrcpts <= 0 && mci->mci_retryrcpt))
3417da7d7b9cSGregory Neil Shapiro 			{
3418da7d7b9cSGregory Neil Shapiro 				o = extenhsc(bufp + 4, ' ', enhstatcode);
3419da7d7b9cSGregory Neil Shapiro 				if (o > 0)
3420da7d7b9cSGregory Neil Shapiro 				{
3421da7d7b9cSGregory Neil Shapiro 					sm_strlcpy(e->e_renhsc, enhstatcode,
3422da7d7b9cSGregory Neil Shapiro 						sizeof(e->e_renhsc));
3423da7d7b9cSGregory Neil Shapiro 
3424da7d7b9cSGregory Neil Shapiro 					/* skip SMTP reply code, delimiters */
3425da7d7b9cSGregory Neil Shapiro 					o += 5;
3426da7d7b9cSGregory Neil Shapiro 				}
3427da7d7b9cSGregory Neil Shapiro 				else
3428da7d7b9cSGregory Neil Shapiro 					o = 4;
34295b0945b5SGregory Neil Shapiro 
34305b0945b5SGregory Neil Shapiro 				/*
34315b0945b5SGregory Neil Shapiro 				**  Don't use this for reply= logging
34325b0945b5SGregory Neil Shapiro 				**  if it was for QUIT.
34335b0945b5SGregory Neil Shapiro 				**  (Note: use the debug option to
34345b0945b5SGregory Neil Shapiro 				**  reproduce the original error.)
34355b0945b5SGregory Neil Shapiro 				*/
34365b0945b5SGregory Neil Shapiro 
34375b0945b5SGregory Neil Shapiro 				if (rtype != XS_QUIT || tTd(87, 101))
34385b0945b5SGregory Neil Shapiro 				{
3439da7d7b9cSGregory Neil Shapiro 					e->e_rcode = r;
34405b0945b5SGregory Neil Shapiro 					e->e_text = sm_rpool_strdup_x(
34415b0945b5SGregory Neil Shapiro 							e->e_rpool, bufp + o);
3442*d39bd2c1SGregory Neil Shapiro #if _FFR_LOG_STAGE
3443*d39bd2c1SGregory Neil Shapiro 					e->e_estate = rtype;
3444*d39bd2c1SGregory Neil Shapiro #endif
34455b0945b5SGregory Neil Shapiro 				}
3446da7d7b9cSGregory Neil Shapiro 			}
3447da7d7b9cSGregory Neil Shapiro 			if (tTd(87, 2))
3448da7d7b9cSGregory Neil Shapiro 			{
34495b0945b5SGregory Neil Shapiro 				sm_dprintf("user: e=%p, offset=%d, bufp=%s, rcode=%d, enhstat=%s, rtype=%d, text=%s\n"
34505b0945b5SGregory Neil Shapiro 					, (void *)e, o, bufp, r, e->e_renhsc
34515b0945b5SGregory Neil Shapiro 					, rtype, e->e_text);
3452da7d7b9cSGregory Neil Shapiro 			}
3453da7d7b9cSGregory Neil Shapiro 		}
34542fb4f839SGregory Neil Shapiro #if _FFR_REPLY_MULTILINE
34552fb4f839SGregory Neil Shapiro # if _FFR_REPLY_MULTILINE > 200
34562fb4f839SGregory Neil Shapiro #  define MLLIMIT _FFR_REPLY_MULTILINE
34572fb4f839SGregory Neil Shapiro # else
34582fb4f839SGregory Neil Shapiro #  define MLLIMIT 1024
34592fb4f839SGregory Neil Shapiro # endif
34602fb4f839SGregory Neil Shapiro 		if ((REPLYTYPE(r) > 3 && !firstline && e->e_text != NULL &&
34612fb4f839SGregory Neil Shapiro 		    rtype != XS_QUIT) || tTd(87, 101))
34622fb4f839SGregory Neil Shapiro 		{
34632fb4f839SGregory Neil Shapiro 			int len;
34642fb4f839SGregory Neil Shapiro 			char *new;
34652fb4f839SGregory Neil Shapiro 
34662fb4f839SGregory Neil Shapiro 			/* skip the same stuff or use o? */
34672fb4f839SGregory Neil Shapiro 			/* but o is a local variable in the block above */
34682fb4f839SGregory Neil Shapiro 			len = strlen(e->e_text) + strlen(bufp) + 3;
34692fb4f839SGregory Neil Shapiro 			if (len < MLLIMIT &&
34702fb4f839SGregory Neil Shapiro 			    (new = (char *) sm_rpool_malloc(e->e_rpool, len))
34712fb4f839SGregory Neil Shapiro 				!= NULL)
34722fb4f839SGregory Neil Shapiro 			{
34732fb4f839SGregory Neil Shapiro 				sm_strlcpyn(new, len, 3, e->e_text, "; ",
34742fb4f839SGregory Neil Shapiro 					bufp /* + o */);
34752fb4f839SGregory Neil Shapiro 				e->e_text = new;
34762fb4f839SGregory Neil Shapiro 			}
34772fb4f839SGregory Neil Shapiro 		}
34782fb4f839SGregory Neil Shapiro #endif /* _FFR_REPLY_MULTILINE */
34796f9c8e5bSGregory Neil Shapiro 
34806f9c8e5bSGregory Neil Shapiro 		firstline = false;
3481c2aa98e2SPeter Wemm 
3482c2aa98e2SPeter Wemm 		/* if no continuation lines, return this line */
3483c2aa98e2SPeter Wemm 		if (bufp[3] != '-')
3484c2aa98e2SPeter Wemm 			break;
3485c2aa98e2SPeter Wemm 
3486c2aa98e2SPeter Wemm 		/* first line of real reply -- ignore rest */
3487c2aa98e2SPeter Wemm 		bufp = junkbuf;
3488c2aa98e2SPeter Wemm 	}
3489c2aa98e2SPeter Wemm 
3490c2aa98e2SPeter Wemm 	/*
3491c2aa98e2SPeter Wemm 	**  Now look at SmtpReplyBuffer -- only care about the first
3492c2aa98e2SPeter Wemm 	**  line of the response from here on out.
3493c2aa98e2SPeter Wemm 	*/
3494c2aa98e2SPeter Wemm 
3495c2aa98e2SPeter Wemm 	/* save temporary failure messages for posterity */
349640266059SGregory Neil Shapiro 	if (SmtpReplyBuffer[0] == '4')
3497d0cef73dSGregory Neil Shapiro 		(void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof(SmtpError));
3498c2aa98e2SPeter Wemm 
3499c2aa98e2SPeter Wemm 	/* reply code 421 is "Service Shutting Down" */
3500605302a5SGregory Neil Shapiro 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD &&
3501605302a5SGregory Neil Shapiro 	    mci->mci_state != MCIS_QUITING)
3502c2aa98e2SPeter Wemm 	{
3503c2aa98e2SPeter Wemm 		/* send the quit protocol */
3504c2aa98e2SPeter Wemm 		mci->mci_state = MCIS_SSD;
3505c2aa98e2SPeter Wemm 		smtpquit(m, mci, e);
3506c2aa98e2SPeter Wemm 	}
3507c2aa98e2SPeter Wemm 
350806f25ae9SGregory Neil Shapiro 	return r;
3509c2aa98e2SPeter Wemm }
351040266059SGregory Neil Shapiro /*
3511c2aa98e2SPeter Wemm **  SMTPMESSAGE -- send message to server
3512c2aa98e2SPeter Wemm **
3513c2aa98e2SPeter Wemm **	Parameters:
3514c2aa98e2SPeter Wemm **		f -- format
3515c2aa98e2SPeter Wemm **		m -- the mailer to control formatting.
3516c2aa98e2SPeter Wemm **		a, b, c -- parameters
3517c2aa98e2SPeter Wemm **
3518c2aa98e2SPeter Wemm **	Returns:
3519c2aa98e2SPeter Wemm **		none.
3520c2aa98e2SPeter Wemm **
3521c2aa98e2SPeter Wemm **	Side Effects:
3522c2aa98e2SPeter Wemm **		writes message to mci->mci_out.
3523c2aa98e2SPeter Wemm */
3524c2aa98e2SPeter Wemm 
3525c2aa98e2SPeter Wemm /*VARARGS1*/
3526c2aa98e2SPeter Wemm void
3527c2aa98e2SPeter Wemm #ifdef __STDC__
smtpmessage(char * f,MAILER * m,MCI * mci,...)3528c2aa98e2SPeter Wemm smtpmessage(char *f, MAILER *m, MCI *mci, ...)
352906f25ae9SGregory Neil Shapiro #else /* __STDC__ */
3530c2aa98e2SPeter Wemm smtpmessage(f, m, mci, va_alist)
3531c2aa98e2SPeter Wemm 	char *f;
3532c2aa98e2SPeter Wemm 	MAILER *m;
3533c2aa98e2SPeter Wemm 	MCI *mci;
3534c2aa98e2SPeter Wemm 	va_dcl
353506f25ae9SGregory Neil Shapiro #endif /* __STDC__ */
3536c2aa98e2SPeter Wemm {
353740266059SGregory Neil Shapiro 	SM_VA_LOCAL_DECL
3538c2aa98e2SPeter Wemm 
353940266059SGregory Neil Shapiro 	SM_VA_START(ap, mci);
3540d0cef73dSGregory Neil Shapiro 	(void) sm_vsnprintf(SmtpMsgBuffer, sizeof(SmtpMsgBuffer), f, ap);
354140266059SGregory Neil Shapiro 	SM_VA_END(ap);
3542c2aa98e2SPeter Wemm 
3543c2aa98e2SPeter Wemm 	if (tTd(18, 1) || Verbose)
3544c2aa98e2SPeter Wemm 		nmessage(">>> %s", SmtpMsgBuffer);
3545c2aa98e2SPeter Wemm 	if (TrafficLogFile != NULL)
354640266059SGregory Neil Shapiro 		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
354740266059SGregory Neil Shapiro 				     "%05d >>> %s\n", (int) CurrentPid,
354840266059SGregory Neil Shapiro 				     SmtpMsgBuffer);
3549c2aa98e2SPeter Wemm 	if (mci->mci_out != NULL)
3550c2aa98e2SPeter Wemm 	{
355140266059SGregory Neil Shapiro 		(void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s",
355240266059SGregory Neil Shapiro 				     SmtpMsgBuffer, m == NULL ? "\r\n"
355340266059SGregory Neil Shapiro 							      : m->m_eol);
3554c2aa98e2SPeter Wemm 	}
3555c2aa98e2SPeter Wemm 	else if (tTd(18, 1))
355640266059SGregory Neil Shapiro 		sm_dprintf("smtpmessage: NULL mci_out\n");
3557c2aa98e2SPeter Wemm }
3558