xref: /illumos-gate/usr/src/cmd/sendmail/src/usersmtp.c (revision e9af4bc0)
17c478bd9Sstevel@tonic-gate /*
2*e9af4bc0SJohn Beck  * Copyright (c) 1998-2006, 2008, 2009 Sendmail, Inc. and its suppliers.
37c478bd9Sstevel@tonic-gate  *	All rights reserved.
47c478bd9Sstevel@tonic-gate  * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
57c478bd9Sstevel@tonic-gate  * Copyright (c) 1988, 1993
67c478bd9Sstevel@tonic-gate  *	The Regents of the University of California.  All rights reserved.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * By using this file, you agree to the terms and conditions set
97c478bd9Sstevel@tonic-gate  * forth in the LICENSE file which can be found at the top level of
107c478bd9Sstevel@tonic-gate  * the sendmail distribution.
117c478bd9Sstevel@tonic-gate  *
127c478bd9Sstevel@tonic-gate  */
137c478bd9Sstevel@tonic-gate 
147c478bd9Sstevel@tonic-gate #include <sendmail.h>
157c478bd9Sstevel@tonic-gate 
16*e9af4bc0SJohn Beck SM_RCSID("@(#)$Id: usersmtp.c,v 8.473 2009/06/17 17:26:51 ca Exp $")
177c478bd9Sstevel@tonic-gate 
187c478bd9Sstevel@tonic-gate #include <sysexits.h>
197c478bd9Sstevel@tonic-gate 
207c478bd9Sstevel@tonic-gate 
217c478bd9Sstevel@tonic-gate static void	esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
227c478bd9Sstevel@tonic-gate static void	helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
237c478bd9Sstevel@tonic-gate static int	smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate #if SASL
267c478bd9Sstevel@tonic-gate extern void	*sm_sasl_malloc __P((unsigned long));
277c478bd9Sstevel@tonic-gate extern void	sm_sasl_free __P((void *));
287c478bd9Sstevel@tonic-gate #endif /* SASL */
297c478bd9Sstevel@tonic-gate 
307c478bd9Sstevel@tonic-gate /*
317c478bd9Sstevel@tonic-gate **  USERSMTP -- run SMTP protocol from the user end.
327c478bd9Sstevel@tonic-gate **
337c478bd9Sstevel@tonic-gate **	This protocol is described in RFC821.
347c478bd9Sstevel@tonic-gate */
357c478bd9Sstevel@tonic-gate 
367c478bd9Sstevel@tonic-gate #define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
377c478bd9Sstevel@tonic-gate #define SMTPCLOSING	421			/* "Service Shutting Down" */
387c478bd9Sstevel@tonic-gate 
397c478bd9Sstevel@tonic-gate #define ENHSCN(e, d)	((e) == NULL ? (d) : (e))
407c478bd9Sstevel@tonic-gate 
417c478bd9Sstevel@tonic-gate #define ENHSCN_RPOOL(e, d, rpool) \
427c478bd9Sstevel@tonic-gate 	((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
437c478bd9Sstevel@tonic-gate 
447c478bd9Sstevel@tonic-gate static char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
457c478bd9Sstevel@tonic-gate static char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
467c478bd9Sstevel@tonic-gate static bool	SmtpNeedIntro;		/* need "while talking" in transcript */
477c478bd9Sstevel@tonic-gate /*
487c478bd9Sstevel@tonic-gate **  SMTPINIT -- initialize SMTP.
497c478bd9Sstevel@tonic-gate **
507c478bd9Sstevel@tonic-gate **	Opens the connection and sends the initial protocol.
517c478bd9Sstevel@tonic-gate **
527c478bd9Sstevel@tonic-gate **	Parameters:
537c478bd9Sstevel@tonic-gate **		m -- mailer to create connection to.
547c478bd9Sstevel@tonic-gate **		mci -- the mailer connection info.
557c478bd9Sstevel@tonic-gate **		e -- the envelope.
567c478bd9Sstevel@tonic-gate **		onlyhelo -- send only helo command?
577c478bd9Sstevel@tonic-gate **
587c478bd9Sstevel@tonic-gate **	Returns:
597c478bd9Sstevel@tonic-gate **		none.
607c478bd9Sstevel@tonic-gate **
617c478bd9Sstevel@tonic-gate **	Side Effects:
627c478bd9Sstevel@tonic-gate **		creates connection and sends initial protocol.
637c478bd9Sstevel@tonic-gate */
647c478bd9Sstevel@tonic-gate 
657c478bd9Sstevel@tonic-gate void
smtpinit(m,mci,e,onlyhelo)667c478bd9Sstevel@tonic-gate smtpinit(m, mci, e, onlyhelo)
677c478bd9Sstevel@tonic-gate 	MAILER *m;
687c478bd9Sstevel@tonic-gate 	register MCI *mci;
697c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
707c478bd9Sstevel@tonic-gate 	bool onlyhelo;
717c478bd9Sstevel@tonic-gate {
727c478bd9Sstevel@tonic-gate 	register int r;
737c478bd9Sstevel@tonic-gate 	int state;
747c478bd9Sstevel@tonic-gate 	register char *p;
757c478bd9Sstevel@tonic-gate 	register char *hn;
767c478bd9Sstevel@tonic-gate 	char *enhsc;
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate 	enhsc = NULL;
797c478bd9Sstevel@tonic-gate 	if (tTd(18, 1))
807c478bd9Sstevel@tonic-gate 	{
817c478bd9Sstevel@tonic-gate 		sm_dprintf("smtpinit ");
827c478bd9Sstevel@tonic-gate 		mci_dump(sm_debug_file(), mci, false);
837c478bd9Sstevel@tonic-gate 	}
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 	/*
867c478bd9Sstevel@tonic-gate 	**  Open the connection to the mailer.
877c478bd9Sstevel@tonic-gate 	*/
887c478bd9Sstevel@tonic-gate 
897c478bd9Sstevel@tonic-gate 	SmtpError[0] = '\0';
907c478bd9Sstevel@tonic-gate 	SmtpMsgBuffer[0] = '\0';
917c478bd9Sstevel@tonic-gate 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
927c478bd9Sstevel@tonic-gate 	if (CurHostName == NULL)
937c478bd9Sstevel@tonic-gate 		CurHostName = MyHostName;
947c478bd9Sstevel@tonic-gate 	SmtpNeedIntro = true;
957c478bd9Sstevel@tonic-gate 	state = mci->mci_state;
967c478bd9Sstevel@tonic-gate 	switch (state)
977c478bd9Sstevel@tonic-gate 	{
987c478bd9Sstevel@tonic-gate 	  case MCIS_MAIL:
997c478bd9Sstevel@tonic-gate 	  case MCIS_RCPT:
1007c478bd9Sstevel@tonic-gate 	  case MCIS_DATA:
1017c478bd9Sstevel@tonic-gate 		/* need to clear old information */
1027c478bd9Sstevel@tonic-gate 		smtprset(m, mci, e);
1037c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate 	  case MCIS_OPEN:
1067c478bd9Sstevel@tonic-gate 		if (!onlyhelo)
1077c478bd9Sstevel@tonic-gate 			return;
1087c478bd9Sstevel@tonic-gate 		break;
1097c478bd9Sstevel@tonic-gate 
1107c478bd9Sstevel@tonic-gate 	  case MCIS_ERROR:
1117c478bd9Sstevel@tonic-gate 	  case MCIS_QUITING:
1127c478bd9Sstevel@tonic-gate 	  case MCIS_SSD:
1137c478bd9Sstevel@tonic-gate 		/* shouldn't happen */
1147c478bd9Sstevel@tonic-gate 		smtpquit(m, mci, e);
1157c478bd9Sstevel@tonic-gate 		/* FALLTHROUGH */
1167c478bd9Sstevel@tonic-gate 
1177c478bd9Sstevel@tonic-gate 	  case MCIS_CLOSED:
1187c478bd9Sstevel@tonic-gate 		syserr("451 4.4.0 smtpinit: state CLOSED (was %d)", state);
1197c478bd9Sstevel@tonic-gate 		return;
1207c478bd9Sstevel@tonic-gate 
1217c478bd9Sstevel@tonic-gate 	  case MCIS_OPENING:
1227c478bd9Sstevel@tonic-gate 		break;
1237c478bd9Sstevel@tonic-gate 	}
1247c478bd9Sstevel@tonic-gate 	if (onlyhelo)
1257c478bd9Sstevel@tonic-gate 		goto helo;
1267c478bd9Sstevel@tonic-gate 
1277c478bd9Sstevel@tonic-gate 	mci->mci_state = MCIS_OPENING;
1287c478bd9Sstevel@tonic-gate 	clrsessenvelope(e);
1297c478bd9Sstevel@tonic-gate 
1307c478bd9Sstevel@tonic-gate 	/*
1317c478bd9Sstevel@tonic-gate 	**  Get the greeting message.
1327c478bd9Sstevel@tonic-gate 	**	This should appear spontaneously.  Give it five minutes to
1337c478bd9Sstevel@tonic-gate 	**	happen.
1347c478bd9Sstevel@tonic-gate 	*/
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate 	SmtpPhase = mci->mci_phase = "client greeting";
1377c478bd9Sstevel@tonic-gate 	sm_setproctitle(true, e, "%s %s: %s",
1387c478bd9Sstevel@tonic-gate 			qid_printname(e), CurHostName, mci->mci_phase);
1397c478bd9Sstevel@tonic-gate 	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL,
1407c478bd9Sstevel@tonic-gate 		XS_DEFAULT);
1417c478bd9Sstevel@tonic-gate 	if (r < 0)
1427c478bd9Sstevel@tonic-gate 		goto tempfail1;
1437c478bd9Sstevel@tonic-gate 	if (REPLYTYPE(r) == 4)
1447c478bd9Sstevel@tonic-gate 		goto tempfail2;
1457c478bd9Sstevel@tonic-gate 	if (REPLYTYPE(r) != 2)
1467c478bd9Sstevel@tonic-gate 		goto unavailable;
1477c478bd9Sstevel@tonic-gate 
1487c478bd9Sstevel@tonic-gate 	/*
1497c478bd9Sstevel@tonic-gate 	**  Send the HELO command.
1507c478bd9Sstevel@tonic-gate 	**	My mother taught me to always introduce myself.
1517c478bd9Sstevel@tonic-gate 	*/
1527c478bd9Sstevel@tonic-gate 
1537c478bd9Sstevel@tonic-gate helo:
1547c478bd9Sstevel@tonic-gate 	if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
1557c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_ESMTP;
1567c478bd9Sstevel@tonic-gate 	hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
1577c478bd9Sstevel@tonic-gate 
1587c478bd9Sstevel@tonic-gate tryhelo:
1597c478bd9Sstevel@tonic-gate #if _FFR_IGNORE_EXT_ON_HELO
1607c478bd9Sstevel@tonic-gate 	mci->mci_flags &= ~MCIF_HELO;
1617c478bd9Sstevel@tonic-gate #endif /* _FFR_IGNORE_EXT_ON_HELO */
1627c478bd9Sstevel@tonic-gate 	if (bitnset(M_LMTP, m->m_flags))
1637c478bd9Sstevel@tonic-gate 	{
1647c478bd9Sstevel@tonic-gate 		smtpmessage("LHLO %s", m, mci, hn);
1657c478bd9Sstevel@tonic-gate 		SmtpPhase = mci->mci_phase = "client LHLO";
1667c478bd9Sstevel@tonic-gate 	}
1677c478bd9Sstevel@tonic-gate 	else if (bitset(MCIF_ESMTP, mci->mci_flags) &&
1687c478bd9Sstevel@tonic-gate 		 !bitnset(M_FSMTP, m->m_flags))
1697c478bd9Sstevel@tonic-gate 	{
1707c478bd9Sstevel@tonic-gate 		smtpmessage("EHLO %s", m, mci, hn);
1717c478bd9Sstevel@tonic-gate 		SmtpPhase = mci->mci_phase = "client EHLO";
1727c478bd9Sstevel@tonic-gate 	}
1737c478bd9Sstevel@tonic-gate 	else
1747c478bd9Sstevel@tonic-gate 	{
1757c478bd9Sstevel@tonic-gate 		smtpmessage("HELO %s", m, mci, hn);
1767c478bd9Sstevel@tonic-gate 		SmtpPhase = mci->mci_phase = "client HELO";
1777c478bd9Sstevel@tonic-gate #if _FFR_IGNORE_EXT_ON_HELO
1787c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_HELO;
1797c478bd9Sstevel@tonic-gate #endif /* _FFR_IGNORE_EXT_ON_HELO */
1807c478bd9Sstevel@tonic-gate 	}
1817c478bd9Sstevel@tonic-gate 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
1827c478bd9Sstevel@tonic-gate 			CurHostName, mci->mci_phase);
1837c478bd9Sstevel@tonic-gate 	r = reply(m, mci, e,
1847c478bd9Sstevel@tonic-gate 		  bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo
1857c478bd9Sstevel@tonic-gate 					      : TimeOuts.to_helo,
1867c478bd9Sstevel@tonic-gate 		  helo_options, NULL, XS_DEFAULT);
1877c478bd9Sstevel@tonic-gate 	if (r < 0)
1887c478bd9Sstevel@tonic-gate 		goto tempfail1;
1897c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 5)
1907c478bd9Sstevel@tonic-gate 	{
1917c478bd9Sstevel@tonic-gate 		if (bitset(MCIF_ESMTP, mci->mci_flags) &&
1927c478bd9Sstevel@tonic-gate 		    !bitnset(M_LMTP, m->m_flags))
1937c478bd9Sstevel@tonic-gate 		{
1947c478bd9Sstevel@tonic-gate 			/* try old SMTP instead */
1957c478bd9Sstevel@tonic-gate 			mci->mci_flags &= ~MCIF_ESMTP;
1967c478bd9Sstevel@tonic-gate 			goto tryhelo;
1977c478bd9Sstevel@tonic-gate 		}
1987c478bd9Sstevel@tonic-gate 		goto unavailable;
1997c478bd9Sstevel@tonic-gate 	}
2007c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) != 2)
2017c478bd9Sstevel@tonic-gate 		goto tempfail2;
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 	/*
2047c478bd9Sstevel@tonic-gate 	**  Check to see if we actually ended up talking to ourself.
2057c478bd9Sstevel@tonic-gate 	**  This means we didn't know about an alias or MX, or we managed
2067c478bd9Sstevel@tonic-gate 	**  to connect to an echo server.
2077c478bd9Sstevel@tonic-gate 	*/
2087c478bd9Sstevel@tonic-gate 
2097c478bd9Sstevel@tonic-gate 	p = strchr(&SmtpReplyBuffer[4], ' ');
2107c478bd9Sstevel@tonic-gate 	if (p != NULL)
2117c478bd9Sstevel@tonic-gate 		*p = '\0';
2127c478bd9Sstevel@tonic-gate 	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
2137c478bd9Sstevel@tonic-gate 	    !bitnset(M_LMTP, m->m_flags) &&
2147c478bd9Sstevel@tonic-gate 	    sm_strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
2157c478bd9Sstevel@tonic-gate 	{
2167c478bd9Sstevel@tonic-gate 		syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
2177c478bd9Sstevel@tonic-gate 			CurHostName);
2187c478bd9Sstevel@tonic-gate 		mci_setstat(mci, EX_CONFIG, "5.3.5",
2197c478bd9Sstevel@tonic-gate 			    "553 5.3.5 system config error");
2207c478bd9Sstevel@tonic-gate 		mci->mci_errno = 0;
2217c478bd9Sstevel@tonic-gate 		smtpquit(m, mci, e);
2227c478bd9Sstevel@tonic-gate 		return;
2237c478bd9Sstevel@tonic-gate 	}
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate 	/*
2267c478bd9Sstevel@tonic-gate 	**  If this is expected to be another sendmail, send some internal
2277c478bd9Sstevel@tonic-gate 	**  commands.
2287c478bd9Sstevel@tonic-gate 	**  If we're running as MSP, "propagate" -v flag if possible.
2297c478bd9Sstevel@tonic-gate 	*/
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 	if ((UseMSP && Verbose && bitset(MCIF_VERB, mci->mci_flags))
2327c478bd9Sstevel@tonic-gate # if !_FFR_DEPRECATE_MAILER_FLAG_I
2337c478bd9Sstevel@tonic-gate 	    || bitnset(M_INTERNAL, m->m_flags)
2347c478bd9Sstevel@tonic-gate # endif /* !_FFR_DEPRECATE_MAILER_FLAG_I */
2357c478bd9Sstevel@tonic-gate 	   )
2367c478bd9Sstevel@tonic-gate 	{
2377c478bd9Sstevel@tonic-gate 		/* tell it to be verbose */
2387c478bd9Sstevel@tonic-gate 		smtpmessage("VERB", m, mci);
2397c478bd9Sstevel@tonic-gate 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc,
2407c478bd9Sstevel@tonic-gate 			XS_DEFAULT);
2417c478bd9Sstevel@tonic-gate 		if (r < 0)
2427c478bd9Sstevel@tonic-gate 			goto tempfail1;
2437c478bd9Sstevel@tonic-gate 	}
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	if (mci->mci_state != MCIS_CLOSED)
2467c478bd9Sstevel@tonic-gate 	{
2477c478bd9Sstevel@tonic-gate 		mci->mci_state = MCIS_OPEN;
2487c478bd9Sstevel@tonic-gate 		return;
2497c478bd9Sstevel@tonic-gate 	}
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate 	/* got a 421 error code during startup */
2527c478bd9Sstevel@tonic-gate 
2537c478bd9Sstevel@tonic-gate   tempfail1:
2547c478bd9Sstevel@tonic-gate 	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
2557c478bd9Sstevel@tonic-gate 	if (mci->mci_state != MCIS_CLOSED)
2567c478bd9Sstevel@tonic-gate 		smtpquit(m, mci, e);
2577c478bd9Sstevel@tonic-gate 	return;
2587c478bd9Sstevel@tonic-gate 
2597c478bd9Sstevel@tonic-gate   tempfail2:
2607c478bd9Sstevel@tonic-gate 	/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
2617c478bd9Sstevel@tonic-gate 	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
2627c478bd9Sstevel@tonic-gate 		    SmtpReplyBuffer);
2637c478bd9Sstevel@tonic-gate 	if (mci->mci_state != MCIS_CLOSED)
2647c478bd9Sstevel@tonic-gate 		smtpquit(m, mci, e);
2657c478bd9Sstevel@tonic-gate 	return;
2667c478bd9Sstevel@tonic-gate 
2677c478bd9Sstevel@tonic-gate   unavailable:
2687c478bd9Sstevel@tonic-gate 	mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
2697c478bd9Sstevel@tonic-gate 	smtpquit(m, mci, e);
2707c478bd9Sstevel@tonic-gate 	return;
2717c478bd9Sstevel@tonic-gate }
2727c478bd9Sstevel@tonic-gate /*
2737c478bd9Sstevel@tonic-gate **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
2747c478bd9Sstevel@tonic-gate **
2757c478bd9Sstevel@tonic-gate **	Parameters:
2767c478bd9Sstevel@tonic-gate **		line -- the response line.
2777c478bd9Sstevel@tonic-gate **		firstline -- set if this is the first line of the reply.
2787c478bd9Sstevel@tonic-gate **		m -- the mailer.
2797c478bd9Sstevel@tonic-gate **		mci -- the mailer connection info.
2807c478bd9Sstevel@tonic-gate **		e -- the envelope.
2817c478bd9Sstevel@tonic-gate **
2827c478bd9Sstevel@tonic-gate **	Returns:
2837c478bd9Sstevel@tonic-gate **		none.
2847c478bd9Sstevel@tonic-gate */
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate static void
esmtp_check(line,firstline,m,mci,e)2877c478bd9Sstevel@tonic-gate esmtp_check(line, firstline, m, mci, e)
2887c478bd9Sstevel@tonic-gate 	char *line;
2897c478bd9Sstevel@tonic-gate 	bool firstline;
2907c478bd9Sstevel@tonic-gate 	MAILER *m;
2917c478bd9Sstevel@tonic-gate 	register MCI *mci;
2927c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
2937c478bd9Sstevel@tonic-gate {
2947c478bd9Sstevel@tonic-gate 	if (strstr(line, "ESMTP") != NULL)
2957c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_ESMTP;
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	/*
2987c478bd9Sstevel@tonic-gate 	**  Dirty hack below. Quoting the author:
2997c478bd9Sstevel@tonic-gate 	**  This was a response to people who wanted SMTP transmission to be
3007c478bd9Sstevel@tonic-gate 	**  just-send-8 by default.  Essentially, you could put this tag into
3017c478bd9Sstevel@tonic-gate 	**  your greeting message to behave as though the F=8 flag was set on
3027c478bd9Sstevel@tonic-gate 	**  the mailer.
3037c478bd9Sstevel@tonic-gate 	*/
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	if (strstr(line, "8BIT-OK") != NULL)
3067c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_8BITOK;
3077c478bd9Sstevel@tonic-gate }
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate #if SASL
3107c478bd9Sstevel@tonic-gate /* specify prototype so compiler can check calls */
3117c478bd9Sstevel@tonic-gate static char *str_union __P((char *, char *, SM_RPOOL_T *));
3127c478bd9Sstevel@tonic-gate 
3137c478bd9Sstevel@tonic-gate /*
3147c478bd9Sstevel@tonic-gate **  STR_UNION -- create the union of two lists
3157c478bd9Sstevel@tonic-gate **
3167c478bd9Sstevel@tonic-gate **	Parameters:
3177c478bd9Sstevel@tonic-gate **		s1, s2 -- lists of items (separated by single blanks).
3187c478bd9Sstevel@tonic-gate **		rpool -- resource pool from which result is allocated.
3197c478bd9Sstevel@tonic-gate **
3207c478bd9Sstevel@tonic-gate **	Returns:
3217c478bd9Sstevel@tonic-gate **		the union of both lists.
3227c478bd9Sstevel@tonic-gate */
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate static char *
str_union(s1,s2,rpool)3257c478bd9Sstevel@tonic-gate str_union(s1, s2, rpool)
3267c478bd9Sstevel@tonic-gate 	char *s1, *s2;
3277c478bd9Sstevel@tonic-gate 	SM_RPOOL_T *rpool;
3287c478bd9Sstevel@tonic-gate {
3297c478bd9Sstevel@tonic-gate 	char *hr, *h1, *h, *res;
3307c478bd9Sstevel@tonic-gate 	int l1, l2, rl;
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	if (s1 == NULL || *s1 == '\0')
3337c478bd9Sstevel@tonic-gate 		return s2;
3347c478bd9Sstevel@tonic-gate 	if (s2 == NULL || *s2 == '\0')
3357c478bd9Sstevel@tonic-gate 		return s1;
3367c478bd9Sstevel@tonic-gate 	l1 = strlen(s1);
3377c478bd9Sstevel@tonic-gate 	l2 = strlen(s2);
3387c478bd9Sstevel@tonic-gate 	rl = l1 + l2;
3397c478bd9Sstevel@tonic-gate 	res = (char *) sm_rpool_malloc(rpool, rl + 2);
3407c478bd9Sstevel@tonic-gate 	if (res == NULL)
3417c478bd9Sstevel@tonic-gate 	{
3427c478bd9Sstevel@tonic-gate 		if (l1 > l2)
3437c478bd9Sstevel@tonic-gate 			return s1;
3447c478bd9Sstevel@tonic-gate 		return s2;
3457c478bd9Sstevel@tonic-gate 	}
3467c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy(res, s1, rl);
3477c478bd9Sstevel@tonic-gate 	hr = res + l1;
3487c478bd9Sstevel@tonic-gate 	h1 = s2;
3497c478bd9Sstevel@tonic-gate 	h = s2;
3507c478bd9Sstevel@tonic-gate 
3517c478bd9Sstevel@tonic-gate 	/* walk through s2 */
3527c478bd9Sstevel@tonic-gate 	while (h != NULL && *h1 != '\0')
3537c478bd9Sstevel@tonic-gate 	{
3547c478bd9Sstevel@tonic-gate 		/* is there something after the current word? */
3557c478bd9Sstevel@tonic-gate 		if ((h = strchr(h1, ' ')) != NULL)
3567c478bd9Sstevel@tonic-gate 			*h = '\0';
3577c478bd9Sstevel@tonic-gate 		l1 = strlen(h1);
3587c478bd9Sstevel@tonic-gate 
3597c478bd9Sstevel@tonic-gate 		/* does the current word appear in s1 ? */
3607c478bd9Sstevel@tonic-gate 		if (iteminlist(h1, s1, " ") == NULL)
3617c478bd9Sstevel@tonic-gate 		{
3627c478bd9Sstevel@tonic-gate 			/* add space as delimiter */
3637c478bd9Sstevel@tonic-gate 			*hr++ = ' ';
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 			/* copy the item */
3667c478bd9Sstevel@tonic-gate 			memcpy(hr, h1, l1);
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate 			/* advance pointer in result list */
3697c478bd9Sstevel@tonic-gate 			hr += l1;
3707c478bd9Sstevel@tonic-gate 			*hr = '\0';
3717c478bd9Sstevel@tonic-gate 		}
3727c478bd9Sstevel@tonic-gate 		if (h != NULL)
3737c478bd9Sstevel@tonic-gate 		{
3747c478bd9Sstevel@tonic-gate 			/* there are more items */
3757c478bd9Sstevel@tonic-gate 			*h = ' ';
3767c478bd9Sstevel@tonic-gate 			h1 = h + 1;
3777c478bd9Sstevel@tonic-gate 		}
3787c478bd9Sstevel@tonic-gate 	}
3797c478bd9Sstevel@tonic-gate 	return res;
3807c478bd9Sstevel@tonic-gate }
3817c478bd9Sstevel@tonic-gate #endif /* SASL */
3827c478bd9Sstevel@tonic-gate 
3837c478bd9Sstevel@tonic-gate /*
3847c478bd9Sstevel@tonic-gate **  HELO_OPTIONS -- process the options on a HELO line.
3857c478bd9Sstevel@tonic-gate **
3867c478bd9Sstevel@tonic-gate **	Parameters:
3877c478bd9Sstevel@tonic-gate **		line -- the response line.
3887c478bd9Sstevel@tonic-gate **		firstline -- set if this is the first line of the reply.
3897c478bd9Sstevel@tonic-gate **		m -- the mailer.
3907c478bd9Sstevel@tonic-gate **		mci -- the mailer connection info.
3917c478bd9Sstevel@tonic-gate **		e -- the envelope (unused).
3927c478bd9Sstevel@tonic-gate **
3937c478bd9Sstevel@tonic-gate **	Returns:
3947c478bd9Sstevel@tonic-gate **		none.
3957c478bd9Sstevel@tonic-gate */
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate static void
helo_options(line,firstline,m,mci,e)3987c478bd9Sstevel@tonic-gate helo_options(line, firstline, m, mci, e)
3997c478bd9Sstevel@tonic-gate 	char *line;
4007c478bd9Sstevel@tonic-gate 	bool firstline;
4017c478bd9Sstevel@tonic-gate 	MAILER *m;
4027c478bd9Sstevel@tonic-gate 	register MCI *mci;
4037c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
4047c478bd9Sstevel@tonic-gate {
4057c478bd9Sstevel@tonic-gate 	register char *p;
4067c478bd9Sstevel@tonic-gate #if _FFR_IGNORE_EXT_ON_HELO
4077c478bd9Sstevel@tonic-gate 	static bool logged = false;
4087c478bd9Sstevel@tonic-gate #endif /* _FFR_IGNORE_EXT_ON_HELO */
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 	if (firstline)
4117c478bd9Sstevel@tonic-gate 	{
4127c478bd9Sstevel@tonic-gate #if SASL
4137c478bd9Sstevel@tonic-gate 		mci->mci_saslcap = NULL;
4147c478bd9Sstevel@tonic-gate #endif /* SASL */
4157c478bd9Sstevel@tonic-gate #if _FFR_IGNORE_EXT_ON_HELO
4167c478bd9Sstevel@tonic-gate 		logged = false;
4177c478bd9Sstevel@tonic-gate #endif /* _FFR_IGNORE_EXT_ON_HELO */
4187c478bd9Sstevel@tonic-gate 		return;
4197c478bd9Sstevel@tonic-gate 	}
4207c478bd9Sstevel@tonic-gate #if _FFR_IGNORE_EXT_ON_HELO
4217c478bd9Sstevel@tonic-gate 	else if (bitset(MCIF_HELO, mci->mci_flags))
4227c478bd9Sstevel@tonic-gate 	{
4237c478bd9Sstevel@tonic-gate 		if (LogLevel > 8 && !logged)
4247c478bd9Sstevel@tonic-gate 		{
4257c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_WARNING, NOQID,
4267c478bd9Sstevel@tonic-gate 				  "server=%s [%s] returned extensions despite HELO command",
4277c478bd9Sstevel@tonic-gate 				  macvalue(macid("{server_name}"), e),
4287c478bd9Sstevel@tonic-gate 				  macvalue(macid("{server_addr}"), e));
4297c478bd9Sstevel@tonic-gate 			logged = true;
4307c478bd9Sstevel@tonic-gate 		}
4317c478bd9Sstevel@tonic-gate 		return;
4327c478bd9Sstevel@tonic-gate 	}
4337c478bd9Sstevel@tonic-gate #endif /* _FFR_IGNORE_EXT_ON_HELO */
4347c478bd9Sstevel@tonic-gate 
4357c478bd9Sstevel@tonic-gate 	if (strlen(line) < 5)
4367c478bd9Sstevel@tonic-gate 		return;
4377c478bd9Sstevel@tonic-gate 	line += 4;
4387c478bd9Sstevel@tonic-gate 	p = strpbrk(line, " =");
4397c478bd9Sstevel@tonic-gate 	if (p != NULL)
4407c478bd9Sstevel@tonic-gate 		*p++ = '\0';
4417c478bd9Sstevel@tonic-gate 	if (sm_strcasecmp(line, "size") == 0)
4427c478bd9Sstevel@tonic-gate 	{
4437c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_SIZE;
4447c478bd9Sstevel@tonic-gate 		if (p != NULL)
4457c478bd9Sstevel@tonic-gate 			mci->mci_maxsize = atol(p);
4467c478bd9Sstevel@tonic-gate 	}
4477c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(line, "8bitmime") == 0)
4487c478bd9Sstevel@tonic-gate 	{
4497c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_8BITMIME;
4507c478bd9Sstevel@tonic-gate 		mci->mci_flags &= ~MCIF_7BIT;
4517c478bd9Sstevel@tonic-gate 	}
4527c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(line, "expn") == 0)
4537c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_EXPN;
4547c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(line, "dsn") == 0)
4557c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_DSN;
4567c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(line, "enhancedstatuscodes") == 0)
4577c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_ENHSTAT;
4587c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(line, "pipelining") == 0)
4597c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_PIPELINED;
4607c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(line, "verb") == 0)
4617c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_VERB;
4627c478bd9Sstevel@tonic-gate #if STARTTLS
4637c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(line, "starttls") == 0)
4647c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_TLS;
4657c478bd9Sstevel@tonic-gate #endif /* STARTTLS */
4667c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(line, "deliverby") == 0)
4677c478bd9Sstevel@tonic-gate 	{
4687c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_DLVR_BY;
4697c478bd9Sstevel@tonic-gate 		if (p != NULL)
4707c478bd9Sstevel@tonic-gate 			mci->mci_min_by = atol(p);
4717c478bd9Sstevel@tonic-gate 	}
4727c478bd9Sstevel@tonic-gate #if SASL
4737c478bd9Sstevel@tonic-gate 	else if (sm_strcasecmp(line, "auth") == 0)
4747c478bd9Sstevel@tonic-gate 	{
4757c478bd9Sstevel@tonic-gate 		if (p != NULL && *p != '\0')
4767c478bd9Sstevel@tonic-gate 		{
4777c478bd9Sstevel@tonic-gate 			if (mci->mci_saslcap != NULL)
4787c478bd9Sstevel@tonic-gate 			{
4797c478bd9Sstevel@tonic-gate 				/*
4807c478bd9Sstevel@tonic-gate 				**  Create the union with previous auth
4817c478bd9Sstevel@tonic-gate 				**  offerings because we recognize "auth "
4827c478bd9Sstevel@tonic-gate 				**  and "auth=" (old format).
4837c478bd9Sstevel@tonic-gate 				*/
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate 				mci->mci_saslcap = str_union(mci->mci_saslcap,
4867c478bd9Sstevel@tonic-gate 							     p, mci->mci_rpool);
4877c478bd9Sstevel@tonic-gate 				mci->mci_flags |= MCIF_AUTH;
4887c478bd9Sstevel@tonic-gate 			}
4897c478bd9Sstevel@tonic-gate 			else
4907c478bd9Sstevel@tonic-gate 			{
4917c478bd9Sstevel@tonic-gate 				int l;
4927c478bd9Sstevel@tonic-gate 
4937c478bd9Sstevel@tonic-gate 				l = strlen(p) + 1;
4947c478bd9Sstevel@tonic-gate 				mci->mci_saslcap = (char *)
4957c478bd9Sstevel@tonic-gate 					sm_rpool_malloc(mci->mci_rpool, l);
4967c478bd9Sstevel@tonic-gate 				if (mci->mci_saslcap != NULL)
4977c478bd9Sstevel@tonic-gate 				{
4987c478bd9Sstevel@tonic-gate 					(void) sm_strlcpy(mci->mci_saslcap, p,
4997c478bd9Sstevel@tonic-gate 							  l);
5007c478bd9Sstevel@tonic-gate 					mci->mci_flags |= MCIF_AUTH;
5017c478bd9Sstevel@tonic-gate 				}
5027c478bd9Sstevel@tonic-gate 			}
5037c478bd9Sstevel@tonic-gate 		}
5047c478bd9Sstevel@tonic-gate 	}
5057c478bd9Sstevel@tonic-gate #endif /* SASL */
5067c478bd9Sstevel@tonic-gate }
5077c478bd9Sstevel@tonic-gate #if SASL
5087c478bd9Sstevel@tonic-gate 
5097c478bd9Sstevel@tonic-gate static int getsimple	__P((void *, int, const char **, unsigned *));
5107c478bd9Sstevel@tonic-gate static int getsecret	__P((sasl_conn_t *, void *, int, sasl_secret_t **));
5117c478bd9Sstevel@tonic-gate static int saslgetrealm	__P((void *, int, const char **, const char **));
5127c478bd9Sstevel@tonic-gate static int readauth	__P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *));
5137c478bd9Sstevel@tonic-gate static int getauth	__P((MCI *, ENVELOPE *, SASL_AI_T *));
5147c478bd9Sstevel@tonic-gate static char *removemech	__P((char *, char *, SM_RPOOL_T *));
5157c478bd9Sstevel@tonic-gate static int attemptauth	__P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *));
5167c478bd9Sstevel@tonic-gate 
5177c478bd9Sstevel@tonic-gate static sasl_callback_t callbacks[] =
5187c478bd9Sstevel@tonic-gate {
5197c478bd9Sstevel@tonic-gate 	{	SASL_CB_GETREALM,	&saslgetrealm,	NULL	},
5207c478bd9Sstevel@tonic-gate #define CB_GETREALM_IDX	0
5217c478bd9Sstevel@tonic-gate 	{	SASL_CB_PASS,		&getsecret,	NULL	},
5227c478bd9Sstevel@tonic-gate #define CB_PASS_IDX	1
5237c478bd9Sstevel@tonic-gate 	{	SASL_CB_USER,		&getsimple,	NULL	},
5247c478bd9Sstevel@tonic-gate #define CB_USER_IDX	2
5257c478bd9Sstevel@tonic-gate 	{	SASL_CB_AUTHNAME,	&getsimple,	NULL	},
5267c478bd9Sstevel@tonic-gate #define CB_AUTHNAME_IDX	3
5277c478bd9Sstevel@tonic-gate 	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
5287c478bd9Sstevel@tonic-gate #define CB_SAFESASL_IDX	4
5297c478bd9Sstevel@tonic-gate 	{	SASL_CB_LIST_END,	NULL,		NULL	}
5307c478bd9Sstevel@tonic-gate };
5317c478bd9Sstevel@tonic-gate 
5327c478bd9Sstevel@tonic-gate /*
5337c478bd9Sstevel@tonic-gate **  INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
5347c478bd9Sstevel@tonic-gate **
5357c478bd9Sstevel@tonic-gate **	Parameters:
5367c478bd9Sstevel@tonic-gate **		none.
5377c478bd9Sstevel@tonic-gate **
5387c478bd9Sstevel@tonic-gate **	Returns:
5397c478bd9Sstevel@tonic-gate **		SASL_OK -- if successful.
5407c478bd9Sstevel@tonic-gate **		SASL error code -- otherwise.
5417c478bd9Sstevel@tonic-gate **
5427c478bd9Sstevel@tonic-gate **	Side Effects:
5437c478bd9Sstevel@tonic-gate **		checks/sets sasl_clt_init.
544058561cbSjbeck **
545058561cbSjbeck **	Note:
546058561cbSjbeck **	Callbacks are ignored if sasl_client_init() has
547058561cbSjbeck **	been called before (by a library such as libnss_ldap)
5487c478bd9Sstevel@tonic-gate */
5497c478bd9Sstevel@tonic-gate 
5507c478bd9Sstevel@tonic-gate static bool sasl_clt_init = false;
5517c478bd9Sstevel@tonic-gate 
5527c478bd9Sstevel@tonic-gate static int
init_sasl_client()5537c478bd9Sstevel@tonic-gate init_sasl_client()
5547c478bd9Sstevel@tonic-gate {
5557c478bd9Sstevel@tonic-gate 	int result;
5567c478bd9Sstevel@tonic-gate 
5577c478bd9Sstevel@tonic-gate 	if (sasl_clt_init)
5587c478bd9Sstevel@tonic-gate 		return SASL_OK;
5597c478bd9Sstevel@tonic-gate 	result = sasl_client_init(callbacks);
5607c478bd9Sstevel@tonic-gate 
5617c478bd9Sstevel@tonic-gate 	/* should we retry later again or just remember that it failed? */
5627c478bd9Sstevel@tonic-gate 	if (result == SASL_OK)
5637c478bd9Sstevel@tonic-gate 		sasl_clt_init = true;
5647c478bd9Sstevel@tonic-gate 	return result;
5657c478bd9Sstevel@tonic-gate }
5667c478bd9Sstevel@tonic-gate /*
5677c478bd9Sstevel@tonic-gate **  STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
5687c478bd9Sstevel@tonic-gate **
5697c478bd9Sstevel@tonic-gate **	Parameters:
5707c478bd9Sstevel@tonic-gate **		none.
5717c478bd9Sstevel@tonic-gate **
5727c478bd9Sstevel@tonic-gate **	Returns:
5737c478bd9Sstevel@tonic-gate **		none.
5747c478bd9Sstevel@tonic-gate **
5757c478bd9Sstevel@tonic-gate **	Side Effects:
5767c478bd9Sstevel@tonic-gate **		checks/sets sasl_clt_init.
5777c478bd9Sstevel@tonic-gate */
5787c478bd9Sstevel@tonic-gate 
5797c478bd9Sstevel@tonic-gate void
stop_sasl_client()5807c478bd9Sstevel@tonic-gate stop_sasl_client()
5817c478bd9Sstevel@tonic-gate {
5827c478bd9Sstevel@tonic-gate 	if (!sasl_clt_init)
5837c478bd9Sstevel@tonic-gate 		return;
5847c478bd9Sstevel@tonic-gate 	sasl_clt_init = false;
5857c478bd9Sstevel@tonic-gate 	sasl_done();
5867c478bd9Sstevel@tonic-gate }
5877c478bd9Sstevel@tonic-gate /*
5887c478bd9Sstevel@tonic-gate **  GETSASLDATA -- process the challenges from the SASL protocol
5897c478bd9Sstevel@tonic-gate **
5907c478bd9Sstevel@tonic-gate **	This gets the relevant sasl response data out of the reply
5917c478bd9Sstevel@tonic-gate **	from the server.
5927c478bd9Sstevel@tonic-gate **
5937c478bd9Sstevel@tonic-gate **	Parameters:
5947c478bd9Sstevel@tonic-gate **		line -- the response line.
5957c478bd9Sstevel@tonic-gate **		firstline -- set if this is the first line of the reply.
5967c478bd9Sstevel@tonic-gate **		m -- the mailer.
5977c478bd9Sstevel@tonic-gate **		mci -- the mailer connection info.
5987c478bd9Sstevel@tonic-gate **		e -- the envelope (unused).
5997c478bd9Sstevel@tonic-gate **
6007c478bd9Sstevel@tonic-gate **	Returns:
6017c478bd9Sstevel@tonic-gate **		none.
6027c478bd9Sstevel@tonic-gate */
6037c478bd9Sstevel@tonic-gate 
6047c478bd9Sstevel@tonic-gate static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
6057c478bd9Sstevel@tonic-gate 
6067c478bd9Sstevel@tonic-gate static void
getsasldata(line,firstline,m,mci,e)6077c478bd9Sstevel@tonic-gate getsasldata(line, firstline, m, mci, e)
6087c478bd9Sstevel@tonic-gate 	char *line;
6097c478bd9Sstevel@tonic-gate 	bool firstline;
6107c478bd9Sstevel@tonic-gate 	MAILER *m;
6117c478bd9Sstevel@tonic-gate 	register MCI *mci;
6127c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
6137c478bd9Sstevel@tonic-gate {
6147c478bd9Sstevel@tonic-gate 	int len;
6157c478bd9Sstevel@tonic-gate 	int result;
6167c478bd9Sstevel@tonic-gate # if SASL < 20000
6177c478bd9Sstevel@tonic-gate 	char *out;
6187c478bd9Sstevel@tonic-gate # endif /* SASL < 20000 */
6197c478bd9Sstevel@tonic-gate 
6207c478bd9Sstevel@tonic-gate 	/* if not a continue we don't care about it */
6217c478bd9Sstevel@tonic-gate 	len = strlen(line);
6227c478bd9Sstevel@tonic-gate 	if ((len <= 4) ||
6237c478bd9Sstevel@tonic-gate 	    (line[0] != '3') ||
6247c478bd9Sstevel@tonic-gate 	     !isascii(line[1]) || !isdigit(line[1]) ||
6257c478bd9Sstevel@tonic-gate 	     !isascii(line[2]) || !isdigit(line[2]))
6267c478bd9Sstevel@tonic-gate 	{
6277c478bd9Sstevel@tonic-gate 		SM_FREE_CLR(mci->mci_sasl_string);
6287c478bd9Sstevel@tonic-gate 		return;
6297c478bd9Sstevel@tonic-gate 	}
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	/* forget about "334 " */
6327c478bd9Sstevel@tonic-gate 	line += 4;
6337c478bd9Sstevel@tonic-gate 	len -= 4;
6347c478bd9Sstevel@tonic-gate # if SASL >= 20000
6357c478bd9Sstevel@tonic-gate 	/* XXX put this into a macro/function? It's duplicated below */
6367c478bd9Sstevel@tonic-gate 	if (mci->mci_sasl_string != NULL)
6377c478bd9Sstevel@tonic-gate 	{
6387c478bd9Sstevel@tonic-gate 		if (mci->mci_sasl_string_len <= len)
6397c478bd9Sstevel@tonic-gate 		{
6407c478bd9Sstevel@tonic-gate 			sm_free(mci->mci_sasl_string); /* XXX */
6417c478bd9Sstevel@tonic-gate 			mci->mci_sasl_string = xalloc(len + 1);
6427c478bd9Sstevel@tonic-gate 		}
6437c478bd9Sstevel@tonic-gate 	}
6447c478bd9Sstevel@tonic-gate 	else
6457c478bd9Sstevel@tonic-gate 		mci->mci_sasl_string = xalloc(len + 1);
6467c478bd9Sstevel@tonic-gate 
6477c478bd9Sstevel@tonic-gate 	result = sasl_decode64(line, len, mci->mci_sasl_string, len + 1,
6487c478bd9Sstevel@tonic-gate 			       (unsigned int *) &mci->mci_sasl_string_len);
6497c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
6507c478bd9Sstevel@tonic-gate 	{
6517c478bd9Sstevel@tonic-gate 		mci->mci_sasl_string_len = 0;
6527c478bd9Sstevel@tonic-gate 		*mci->mci_sasl_string = '\0';
6537c478bd9Sstevel@tonic-gate 	}
6547c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
6557c478bd9Sstevel@tonic-gate 	out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1);
6567c478bd9Sstevel@tonic-gate 	result = sasl_decode64(line, len, out, (unsigned int *) &len);
6577c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
6587c478bd9Sstevel@tonic-gate 	{
6597c478bd9Sstevel@tonic-gate 		len = 0;
6607c478bd9Sstevel@tonic-gate 		*out = '\0';
6617c478bd9Sstevel@tonic-gate 	}
6627c478bd9Sstevel@tonic-gate 
6637c478bd9Sstevel@tonic-gate 	/*
6647c478bd9Sstevel@tonic-gate 	**  mci_sasl_string is "shared" with Cyrus-SASL library; hence
6657c478bd9Sstevel@tonic-gate 	**	it can't be in an rpool unless we use the same memory
6667c478bd9Sstevel@tonic-gate 	**	management mechanism (with same rpool!) for Cyrus SASL.
6677c478bd9Sstevel@tonic-gate 	*/
6687c478bd9Sstevel@tonic-gate 
6697c478bd9Sstevel@tonic-gate 	if (mci->mci_sasl_string != NULL)
6707c478bd9Sstevel@tonic-gate 	{
6717c478bd9Sstevel@tonic-gate 		if (mci->mci_sasl_string_len <= len)
6727c478bd9Sstevel@tonic-gate 		{
6737c478bd9Sstevel@tonic-gate 			sm_free(mci->mci_sasl_string); /* XXX */
6747c478bd9Sstevel@tonic-gate 			mci->mci_sasl_string = xalloc(len + 1);
6757c478bd9Sstevel@tonic-gate 		}
6767c478bd9Sstevel@tonic-gate 	}
6777c478bd9Sstevel@tonic-gate 	else
6787c478bd9Sstevel@tonic-gate 		mci->mci_sasl_string = xalloc(len + 1);
6797c478bd9Sstevel@tonic-gate 
6807c478bd9Sstevel@tonic-gate 	memcpy(mci->mci_sasl_string, out, len);
6817c478bd9Sstevel@tonic-gate 	mci->mci_sasl_string[len] = '\0';
6827c478bd9Sstevel@tonic-gate 	mci->mci_sasl_string_len = len;
6837c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
6847c478bd9Sstevel@tonic-gate 	return;
6857c478bd9Sstevel@tonic-gate }
6867c478bd9Sstevel@tonic-gate /*
6877c478bd9Sstevel@tonic-gate **  READAUTH -- read auth values from a file
6887c478bd9Sstevel@tonic-gate **
6897c478bd9Sstevel@tonic-gate **	Parameters:
6907c478bd9Sstevel@tonic-gate **		filename -- name of file to read.
6917c478bd9Sstevel@tonic-gate **		safe -- if set, this is a safe read.
6927c478bd9Sstevel@tonic-gate **		sai -- where to store auth_info.
6937c478bd9Sstevel@tonic-gate **		rpool -- resource pool for sai.
6947c478bd9Sstevel@tonic-gate **
6957c478bd9Sstevel@tonic-gate **	Returns:
6967c478bd9Sstevel@tonic-gate **		EX_OK -- data succesfully read.
6977c478bd9Sstevel@tonic-gate **		EX_UNAVAILABLE -- no valid filename.
6987c478bd9Sstevel@tonic-gate **		EX_TEMPFAIL -- temporary failure.
6997c478bd9Sstevel@tonic-gate */
7007c478bd9Sstevel@tonic-gate 
7017c478bd9Sstevel@tonic-gate static char *sasl_info_name[] =
7027c478bd9Sstevel@tonic-gate {
7037c478bd9Sstevel@tonic-gate 	"user id",
7047c478bd9Sstevel@tonic-gate 	"authentication id",
7057c478bd9Sstevel@tonic-gate 	"password",
7067c478bd9Sstevel@tonic-gate 	"realm",
7077c478bd9Sstevel@tonic-gate 	"mechlist"
7087c478bd9Sstevel@tonic-gate };
7097c478bd9Sstevel@tonic-gate static int
readauth(filename,safe,sai,rpool)7107c478bd9Sstevel@tonic-gate readauth(filename, safe, sai, rpool)
7117c478bd9Sstevel@tonic-gate 	char *filename;
7127c478bd9Sstevel@tonic-gate 	bool safe;
7137c478bd9Sstevel@tonic-gate 	SASL_AI_T *sai;
7147c478bd9Sstevel@tonic-gate 	SM_RPOOL_T *rpool;
7157c478bd9Sstevel@tonic-gate {
7167c478bd9Sstevel@tonic-gate 	SM_FILE_T *f;
7177c478bd9Sstevel@tonic-gate 	long sff;
7187c478bd9Sstevel@tonic-gate 	pid_t pid;
7197c478bd9Sstevel@tonic-gate 	int lc;
7207c478bd9Sstevel@tonic-gate 	char *s;
7217c478bd9Sstevel@tonic-gate 	char buf[MAXLINE];
7227c478bd9Sstevel@tonic-gate 
7237c478bd9Sstevel@tonic-gate 	if (filename == NULL || filename[0] == '\0')
7247c478bd9Sstevel@tonic-gate 		return EX_UNAVAILABLE;
7257c478bd9Sstevel@tonic-gate 
7267c478bd9Sstevel@tonic-gate #if !_FFR_ALLOW_SASLINFO
7277c478bd9Sstevel@tonic-gate 	/*
7287c478bd9Sstevel@tonic-gate 	**  make sure we don't use a program that is not
7297c478bd9Sstevel@tonic-gate 	**  accesible to the user who specified a different authinfo file.
7307c478bd9Sstevel@tonic-gate 	**  However, currently we don't pass this info (authinfo file
7317c478bd9Sstevel@tonic-gate 	**  specified by user) around, so we just turn off program access.
7327c478bd9Sstevel@tonic-gate 	*/
7337c478bd9Sstevel@tonic-gate 
7347c478bd9Sstevel@tonic-gate 	if (filename[0] == '|')
7357c478bd9Sstevel@tonic-gate 	{
7367c478bd9Sstevel@tonic-gate 		auto int fd;
7377c478bd9Sstevel@tonic-gate 		int i;
7387c478bd9Sstevel@tonic-gate 		char *p;
7397c478bd9Sstevel@tonic-gate 		char *argv[MAXPV + 1];
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate 		i = 0;
7427c478bd9Sstevel@tonic-gate 		for (p = strtok(&filename[1], " \t"); p != NULL;
7437c478bd9Sstevel@tonic-gate 		     p = strtok(NULL, " \t"))
7447c478bd9Sstevel@tonic-gate 		{
7457c478bd9Sstevel@tonic-gate 			if (i >= MAXPV)
7467c478bd9Sstevel@tonic-gate 				break;
7477c478bd9Sstevel@tonic-gate 			argv[i++] = p;
7487c478bd9Sstevel@tonic-gate 		}
7497c478bd9Sstevel@tonic-gate 		argv[i] = NULL;
7507c478bd9Sstevel@tonic-gate 		pid = prog_open(argv, &fd, CurEnv);
7517c478bd9Sstevel@tonic-gate 		if (pid < 0)
7527c478bd9Sstevel@tonic-gate 			f = NULL;
7537c478bd9Sstevel@tonic-gate 		else
7547c478bd9Sstevel@tonic-gate 			f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
7557c478bd9Sstevel@tonic-gate 				       (void *) &fd, SM_IO_RDONLY, NULL);
7567c478bd9Sstevel@tonic-gate 	}
7577c478bd9Sstevel@tonic-gate 	else
7587c478bd9Sstevel@tonic-gate #endif /* !_FFR_ALLOW_SASLINFO */
7597c478bd9Sstevel@tonic-gate 	{
7607c478bd9Sstevel@tonic-gate 		pid = -1;
7617c478bd9Sstevel@tonic-gate 		sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
7627c478bd9Sstevel@tonic-gate 		      |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES;
7637c478bd9Sstevel@tonic-gate # if _FFR_GROUPREADABLEAUTHINFOFILE
7647c478bd9Sstevel@tonic-gate 		if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail))
7657c478bd9Sstevel@tonic-gate # endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
7667c478bd9Sstevel@tonic-gate 			sff |= SFF_NOGRFILES;
7677c478bd9Sstevel@tonic-gate 		if (DontLockReadFiles)
7687c478bd9Sstevel@tonic-gate 			sff |= SFF_NOLOCK;
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate #if _FFR_ALLOW_SASLINFO
7717c478bd9Sstevel@tonic-gate 		/*
7727c478bd9Sstevel@tonic-gate 		**  XXX: make sure we don't read or open files that are not
7737c478bd9Sstevel@tonic-gate 		**  accesible to the user who specified a different authinfo
7747c478bd9Sstevel@tonic-gate 		**  file.
7757c478bd9Sstevel@tonic-gate 		*/
7767c478bd9Sstevel@tonic-gate 
7777c478bd9Sstevel@tonic-gate 		sff |= SFF_MUSTOWN;
7787c478bd9Sstevel@tonic-gate #else /* _FFR_ALLOW_SASLINFO */
7797c478bd9Sstevel@tonic-gate 		if (safe)
7807c478bd9Sstevel@tonic-gate 			sff |= SFF_OPENASROOT;
7817c478bd9Sstevel@tonic-gate #endif /* _FFR_ALLOW_SASLINFO */
7827c478bd9Sstevel@tonic-gate 
7837c478bd9Sstevel@tonic-gate 		f = safefopen(filename, O_RDONLY, 0, sff);
7847c478bd9Sstevel@tonic-gate 	}
7857c478bd9Sstevel@tonic-gate 	if (f == NULL)
7867c478bd9Sstevel@tonic-gate 	{
7877c478bd9Sstevel@tonic-gate 		if (LogLevel > 5)
7887c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, NOQID,
7897c478bd9Sstevel@tonic-gate 				  "AUTH=client, error: can't open %s: %s",
7907c478bd9Sstevel@tonic-gate 				  filename, sm_errstring(errno));
7917c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
7927c478bd9Sstevel@tonic-gate 	}
7937c478bd9Sstevel@tonic-gate 
7947c478bd9Sstevel@tonic-gate 	lc = 0;
7957c478bd9Sstevel@tonic-gate 	while (lc <= SASL_MECHLIST &&
796058561cbSjbeck 		sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
7977c478bd9Sstevel@tonic-gate 	{
7987c478bd9Sstevel@tonic-gate 		if (buf[0] != '#')
7997c478bd9Sstevel@tonic-gate 		{
8007c478bd9Sstevel@tonic-gate 			(*sai)[lc] = sm_rpool_strdup_x(rpool, buf);
8017c478bd9Sstevel@tonic-gate 			if ((s = strchr((*sai)[lc], '\n')) != NULL)
8027c478bd9Sstevel@tonic-gate 				*s = '\0';
8037c478bd9Sstevel@tonic-gate 			lc++;
8047c478bd9Sstevel@tonic-gate 		}
8057c478bd9Sstevel@tonic-gate 	}
8067c478bd9Sstevel@tonic-gate 
8077c478bd9Sstevel@tonic-gate 	(void) sm_io_close(f, SM_TIME_DEFAULT);
8087c478bd9Sstevel@tonic-gate 	if (pid > 0)
8097c478bd9Sstevel@tonic-gate 		(void) waitfor(pid);
8107c478bd9Sstevel@tonic-gate 	if (lc < SASL_PASSWORD)
8117c478bd9Sstevel@tonic-gate 	{
8127c478bd9Sstevel@tonic-gate 		if (LogLevel > 8)
8137c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_ERR, NOQID,
8147c478bd9Sstevel@tonic-gate 				  "AUTH=client, error: can't read %s from %s",
8157c478bd9Sstevel@tonic-gate 				  sasl_info_name[lc + 1], filename);
8167c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
8177c478bd9Sstevel@tonic-gate 	}
8187c478bd9Sstevel@tonic-gate 	return EX_OK;
8197c478bd9Sstevel@tonic-gate }
8207c478bd9Sstevel@tonic-gate 
8217c478bd9Sstevel@tonic-gate /*
8227c478bd9Sstevel@tonic-gate **  GETAUTH -- get authinfo from ruleset call
8237c478bd9Sstevel@tonic-gate **
8247c478bd9Sstevel@tonic-gate **	{server_name}, {server_addr} must be set
8257c478bd9Sstevel@tonic-gate **
8267c478bd9Sstevel@tonic-gate **	Parameters:
8277c478bd9Sstevel@tonic-gate **		mci -- the mailer connection structure.
8287c478bd9Sstevel@tonic-gate **		e -- the envelope (including the sender to specify).
8297c478bd9Sstevel@tonic-gate **		sai -- pointer to authinfo (result).
8307c478bd9Sstevel@tonic-gate **
8317c478bd9Sstevel@tonic-gate **	Returns:
8327c478bd9Sstevel@tonic-gate **		EX_OK -- ruleset was succesfully called, data may not
8337c478bd9Sstevel@tonic-gate **			be available, sai must be checked.
8347c478bd9Sstevel@tonic-gate **		EX_UNAVAILABLE -- ruleset unavailable (or failed).
8357c478bd9Sstevel@tonic-gate **		EX_TEMPFAIL -- temporary failure (from ruleset).
8367c478bd9Sstevel@tonic-gate **
8377c478bd9Sstevel@tonic-gate **	Side Effects:
8387c478bd9Sstevel@tonic-gate **		Fills in sai if successful.
8397c478bd9Sstevel@tonic-gate */
8407c478bd9Sstevel@tonic-gate 
8417c478bd9Sstevel@tonic-gate static int
getauth(mci,e,sai)8427c478bd9Sstevel@tonic-gate getauth(mci, e, sai)
8437c478bd9Sstevel@tonic-gate 	MCI *mci;
8447c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
8457c478bd9Sstevel@tonic-gate 	SASL_AI_T *sai;
8467c478bd9Sstevel@tonic-gate {
8477c478bd9Sstevel@tonic-gate 	int i, r, l, got, ret;
8487c478bd9Sstevel@tonic-gate 	char **pvp;
8497c478bd9Sstevel@tonic-gate 	char pvpbuf[PSBUFSIZE];
8507c478bd9Sstevel@tonic-gate 
8517c478bd9Sstevel@tonic-gate 	r = rscap("authinfo", macvalue(macid("{server_name}"), e),
8527c478bd9Sstevel@tonic-gate 		   macvalue(macid("{server_addr}"), e), e,
8537c478bd9Sstevel@tonic-gate 		   &pvp, pvpbuf, sizeof(pvpbuf));
8547c478bd9Sstevel@tonic-gate 
8557c478bd9Sstevel@tonic-gate 	if (r != EX_OK)
8567c478bd9Sstevel@tonic-gate 		return EX_UNAVAILABLE;
8577c478bd9Sstevel@tonic-gate 
8587c478bd9Sstevel@tonic-gate 	/* other than expected return value: ok (i.e., no auth) */
8597c478bd9Sstevel@tonic-gate 	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
8607c478bd9Sstevel@tonic-gate 		return EX_OK;
8617c478bd9Sstevel@tonic-gate 	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
8627c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	/*
8657c478bd9Sstevel@tonic-gate 	**  parse the data, put it into sai
8667c478bd9Sstevel@tonic-gate 	**  format: "TDstring" (including the '"' !)
8677c478bd9Sstevel@tonic-gate 	**  where T is a tag: 'U', ...
8687c478bd9Sstevel@tonic-gate 	**  D is a delimiter: ':' or '='
8697c478bd9Sstevel@tonic-gate 	*/
8707c478bd9Sstevel@tonic-gate 
8717c478bd9Sstevel@tonic-gate 	ret = EX_OK;	/* default return value */
8727c478bd9Sstevel@tonic-gate 	i = 0;
8737c478bd9Sstevel@tonic-gate 	got = 0;
8747c478bd9Sstevel@tonic-gate 	while (i < SASL_ENTRIES)
8757c478bd9Sstevel@tonic-gate 	{
8767c478bd9Sstevel@tonic-gate 		if (pvp[i + 1] == NULL)
8777c478bd9Sstevel@tonic-gate 			break;
8787c478bd9Sstevel@tonic-gate 		if (pvp[i + 1][0] != '"')
8797c478bd9Sstevel@tonic-gate 			break;
8807c478bd9Sstevel@tonic-gate 		switch (pvp[i + 1][1])
8817c478bd9Sstevel@tonic-gate 		{
8827c478bd9Sstevel@tonic-gate 		  case 'U':
8837c478bd9Sstevel@tonic-gate 		  case 'u':
8847c478bd9Sstevel@tonic-gate 			r = SASL_USER;
8857c478bd9Sstevel@tonic-gate 			break;
8867c478bd9Sstevel@tonic-gate 		  case 'I':
8877c478bd9Sstevel@tonic-gate 		  case 'i':
8887c478bd9Sstevel@tonic-gate 			r = SASL_AUTHID;
8897c478bd9Sstevel@tonic-gate 			break;
8907c478bd9Sstevel@tonic-gate 		  case 'P':
8917c478bd9Sstevel@tonic-gate 		  case 'p':
8927c478bd9Sstevel@tonic-gate 			r = SASL_PASSWORD;
8937c478bd9Sstevel@tonic-gate 			break;
8947c478bd9Sstevel@tonic-gate 		  case 'R':
8957c478bd9Sstevel@tonic-gate 		  case 'r':
8967c478bd9Sstevel@tonic-gate 			r = SASL_DEFREALM;
8977c478bd9Sstevel@tonic-gate 			break;
8987c478bd9Sstevel@tonic-gate 		  case 'M':
8997c478bd9Sstevel@tonic-gate 		  case 'm':
9007c478bd9Sstevel@tonic-gate 			r = SASL_MECHLIST;
9017c478bd9Sstevel@tonic-gate 			break;
9027c478bd9Sstevel@tonic-gate 		  default:
9037c478bd9Sstevel@tonic-gate 			goto fail;
9047c478bd9Sstevel@tonic-gate 		}
9057c478bd9Sstevel@tonic-gate 		l = strlen(pvp[i + 1]);
9067c478bd9Sstevel@tonic-gate 
9077c478bd9Sstevel@tonic-gate 		/* check syntax */
9087c478bd9Sstevel@tonic-gate 		if (l <= 3 || pvp[i + 1][l - 1] != '"')
9097c478bd9Sstevel@tonic-gate 			goto fail;
9107c478bd9Sstevel@tonic-gate 
9117c478bd9Sstevel@tonic-gate 		/* remove closing quote */
9127c478bd9Sstevel@tonic-gate 		pvp[i + 1][l - 1] = '\0';
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 		/* remove "TD and " */
9157c478bd9Sstevel@tonic-gate 		l -= 4;
9167c478bd9Sstevel@tonic-gate 		(*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1);
9177c478bd9Sstevel@tonic-gate 		if ((*sai)[r] == NULL)
9187c478bd9Sstevel@tonic-gate 			goto tempfail;
9197c478bd9Sstevel@tonic-gate 		if (pvp[i + 1][2] == ':')
9207c478bd9Sstevel@tonic-gate 		{
9217c478bd9Sstevel@tonic-gate 			/* ':text' (just copy) */
9227c478bd9Sstevel@tonic-gate 			(void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1);
9237c478bd9Sstevel@tonic-gate 			got |= 1 << r;
9247c478bd9Sstevel@tonic-gate 		}
9257c478bd9Sstevel@tonic-gate 		else if (pvp[i + 1][2] == '=')
9267c478bd9Sstevel@tonic-gate 		{
9277c478bd9Sstevel@tonic-gate 			unsigned int len;
9287c478bd9Sstevel@tonic-gate 
9297c478bd9Sstevel@tonic-gate 			/* '=base64' (decode) */
9307c478bd9Sstevel@tonic-gate # if SASL >= 20000
9317c478bd9Sstevel@tonic-gate 			ret = sasl_decode64(pvp[i + 1] + 3,
9327c478bd9Sstevel@tonic-gate 					  (unsigned int) l, (*sai)[r],
9337c478bd9Sstevel@tonic-gate 					  (unsigned int) l + 1, &len);
9347c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
9357c478bd9Sstevel@tonic-gate 			ret = sasl_decode64(pvp[i + 1] + 3,
9367c478bd9Sstevel@tonic-gate 					  (unsigned int) l, (*sai)[r], &len);
9377c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
9387c478bd9Sstevel@tonic-gate 			if (ret != SASL_OK)
9397c478bd9Sstevel@tonic-gate 				goto fail;
9407c478bd9Sstevel@tonic-gate 			got |= 1 << r;
9417c478bd9Sstevel@tonic-gate 		}
9427c478bd9Sstevel@tonic-gate 		else
9437c478bd9Sstevel@tonic-gate 			goto fail;
9447c478bd9Sstevel@tonic-gate 		if (tTd(95, 5))
9457c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, NOQID, "getauth %s=%s",
9467c478bd9Sstevel@tonic-gate 				  sasl_info_name[r], (*sai)[r]);
9477c478bd9Sstevel@tonic-gate 		++i;
9487c478bd9Sstevel@tonic-gate 	}
9497c478bd9Sstevel@tonic-gate 
9507c478bd9Sstevel@tonic-gate 	/* did we get the expected data? */
9517c478bd9Sstevel@tonic-gate 	/* XXX: EXTERNAL mechanism only requires (and only uses) SASL_USER */
9527c478bd9Sstevel@tonic-gate 	if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) &&
9537c478bd9Sstevel@tonic-gate 	      bitset(SASL_PASSWORD_BIT, got)))
9547c478bd9Sstevel@tonic-gate 		goto fail;
9557c478bd9Sstevel@tonic-gate 
9567c478bd9Sstevel@tonic-gate 	/* no authid? copy uid */
9577c478bd9Sstevel@tonic-gate 	if (!bitset(SASL_AUTHID_BIT, got))
9587c478bd9Sstevel@tonic-gate 	{
9597c478bd9Sstevel@tonic-gate 		l = strlen((*sai)[SASL_USER]) + 1;
9607c478bd9Sstevel@tonic-gate 		(*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool,
9617c478bd9Sstevel@tonic-gate 							       l + 1);
9627c478bd9Sstevel@tonic-gate 		if ((*sai)[SASL_AUTHID] == NULL)
9637c478bd9Sstevel@tonic-gate 			goto tempfail;
9647c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l);
9657c478bd9Sstevel@tonic-gate 	}
9667c478bd9Sstevel@tonic-gate 
9677c478bd9Sstevel@tonic-gate 	/* no uid? copy authid */
9687c478bd9Sstevel@tonic-gate 	if (!bitset(SASL_USER_BIT, got))
9697c478bd9Sstevel@tonic-gate 	{
9707c478bd9Sstevel@tonic-gate 		l = strlen((*sai)[SASL_AUTHID]) + 1;
9717c478bd9Sstevel@tonic-gate 		(*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool,
9727c478bd9Sstevel@tonic-gate 							     l + 1);
9737c478bd9Sstevel@tonic-gate 		if ((*sai)[SASL_USER] == NULL)
9747c478bd9Sstevel@tonic-gate 			goto tempfail;
9757c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l);
9767c478bd9Sstevel@tonic-gate 	}
9777c478bd9Sstevel@tonic-gate 	return EX_OK;
9787c478bd9Sstevel@tonic-gate 
9797c478bd9Sstevel@tonic-gate   tempfail:
9807c478bd9Sstevel@tonic-gate 	ret = EX_TEMPFAIL;
9817c478bd9Sstevel@tonic-gate   fail:
9827c478bd9Sstevel@tonic-gate 	if (LogLevel > 8)
9837c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_WARNING, NOQID,
9847c478bd9Sstevel@tonic-gate 			  "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
9857c478bd9Sstevel@tonic-gate 			  macvalue(macid("{server_name}"), e),
9867c478bd9Sstevel@tonic-gate 			  macvalue(macid("{server_addr}"), e),
9877c478bd9Sstevel@tonic-gate 			  ret == EX_TEMPFAIL ? "temp" : "");
9887c478bd9Sstevel@tonic-gate 	for (i = 0; i <= SASL_MECHLIST; i++)
9897c478bd9Sstevel@tonic-gate 		(*sai)[i] = NULL;	/* just clear; rpool */
9907c478bd9Sstevel@tonic-gate 	return ret;
9917c478bd9Sstevel@tonic-gate }
9927c478bd9Sstevel@tonic-gate 
9937c478bd9Sstevel@tonic-gate # if SASL >= 20000
9947c478bd9Sstevel@tonic-gate /*
9957c478bd9Sstevel@tonic-gate **  GETSIMPLE -- callback to get userid or authid
9967c478bd9Sstevel@tonic-gate **
9977c478bd9Sstevel@tonic-gate **	Parameters:
9987c478bd9Sstevel@tonic-gate **		context -- sai
9997c478bd9Sstevel@tonic-gate **		id -- what to do
10007c478bd9Sstevel@tonic-gate **		result -- (pointer to) result
10017c478bd9Sstevel@tonic-gate **		len -- (pointer to) length of result
10027c478bd9Sstevel@tonic-gate **
10037c478bd9Sstevel@tonic-gate **	Returns:
10047c478bd9Sstevel@tonic-gate **		OK/failure values
10057c478bd9Sstevel@tonic-gate */
10067c478bd9Sstevel@tonic-gate 
10077c478bd9Sstevel@tonic-gate static int
getsimple(context,id,result,len)10087c478bd9Sstevel@tonic-gate getsimple(context, id, result, len)
10097c478bd9Sstevel@tonic-gate 	void *context;
10107c478bd9Sstevel@tonic-gate 	int id;
10117c478bd9Sstevel@tonic-gate 	const char **result;
10127c478bd9Sstevel@tonic-gate 	unsigned *len;
10137c478bd9Sstevel@tonic-gate {
10147c478bd9Sstevel@tonic-gate 	SASL_AI_T *sai;
10157c478bd9Sstevel@tonic-gate 
10167c478bd9Sstevel@tonic-gate 	if (result == NULL || context == NULL)
10177c478bd9Sstevel@tonic-gate 		return SASL_BADPARAM;
10187c478bd9Sstevel@tonic-gate 	sai = (SASL_AI_T *) context;
10197c478bd9Sstevel@tonic-gate 
10207c478bd9Sstevel@tonic-gate 	switch (id)
10217c478bd9Sstevel@tonic-gate 	{
10227c478bd9Sstevel@tonic-gate 	  case SASL_CB_USER:
10237c478bd9Sstevel@tonic-gate 		*result = (*sai)[SASL_USER];
10247c478bd9Sstevel@tonic-gate 		if (tTd(95, 5))
10257c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
10267c478bd9Sstevel@tonic-gate 				  *result);
10277c478bd9Sstevel@tonic-gate 		if (len != NULL)
10287c478bd9Sstevel@tonic-gate 			*len = *result != NULL ? strlen(*result) : 0;
10297c478bd9Sstevel@tonic-gate 		break;
10307c478bd9Sstevel@tonic-gate 
10317c478bd9Sstevel@tonic-gate 	  case SASL_CB_AUTHNAME:
10327c478bd9Sstevel@tonic-gate 		*result = (*sai)[SASL_AUTHID];
10337c478bd9Sstevel@tonic-gate 		if (tTd(95, 5))
10347c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
10357c478bd9Sstevel@tonic-gate 				  *result);
10367c478bd9Sstevel@tonic-gate 		if (len != NULL)
10377c478bd9Sstevel@tonic-gate 			*len = *result != NULL ? strlen(*result) : 0;
10387c478bd9Sstevel@tonic-gate 		break;
10397c478bd9Sstevel@tonic-gate 
10407c478bd9Sstevel@tonic-gate 	  case SASL_CB_LANGUAGE:
10417c478bd9Sstevel@tonic-gate 		*result = NULL;
10427c478bd9Sstevel@tonic-gate 		if (len != NULL)
10437c478bd9Sstevel@tonic-gate 			*len = 0;
10447c478bd9Sstevel@tonic-gate 		break;
10457c478bd9Sstevel@tonic-gate 
10467c478bd9Sstevel@tonic-gate 	  default:
10477c478bd9Sstevel@tonic-gate 		return SASL_BADPARAM;
10487c478bd9Sstevel@tonic-gate 	}
10497c478bd9Sstevel@tonic-gate 	return SASL_OK;
10507c478bd9Sstevel@tonic-gate }
10517c478bd9Sstevel@tonic-gate /*
10527c478bd9Sstevel@tonic-gate **  GETSECRET -- callback to get password
10537c478bd9Sstevel@tonic-gate **
10547c478bd9Sstevel@tonic-gate **	Parameters:
10557c478bd9Sstevel@tonic-gate **		conn -- connection information
10567c478bd9Sstevel@tonic-gate **		context -- sai
10577c478bd9Sstevel@tonic-gate **		id -- what to do
10587c478bd9Sstevel@tonic-gate **		psecret -- (pointer to) result
10597c478bd9Sstevel@tonic-gate **
10607c478bd9Sstevel@tonic-gate **	Returns:
10617c478bd9Sstevel@tonic-gate **		OK/failure values
10627c478bd9Sstevel@tonic-gate */
10637c478bd9Sstevel@tonic-gate 
10647c478bd9Sstevel@tonic-gate static int
getsecret(conn,context,id,psecret)10657c478bd9Sstevel@tonic-gate getsecret(conn, context, id, psecret)
10667c478bd9Sstevel@tonic-gate 	sasl_conn_t *conn;
10677c478bd9Sstevel@tonic-gate 	SM_UNUSED(void *context);
10687c478bd9Sstevel@tonic-gate 	int id;
10697c478bd9Sstevel@tonic-gate 	sasl_secret_t **psecret;
10707c478bd9Sstevel@tonic-gate {
10717c478bd9Sstevel@tonic-gate 	int len;
10727c478bd9Sstevel@tonic-gate 	char *authpass;
10737c478bd9Sstevel@tonic-gate 	MCI *mci;
10747c478bd9Sstevel@tonic-gate 
10757c478bd9Sstevel@tonic-gate 	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
10767c478bd9Sstevel@tonic-gate 		return SASL_BADPARAM;
10777c478bd9Sstevel@tonic-gate 
10787c478bd9Sstevel@tonic-gate 	mci = (MCI *) context;
10797c478bd9Sstevel@tonic-gate 	authpass = mci->mci_sai[SASL_PASSWORD];
10807c478bd9Sstevel@tonic-gate 	len = strlen(authpass);
10817c478bd9Sstevel@tonic-gate 
10827c478bd9Sstevel@tonic-gate 	/*
10837c478bd9Sstevel@tonic-gate 	**  use an rpool because we are responsible for free()ing the secret,
10847c478bd9Sstevel@tonic-gate 	**  but we can't free() it until after the auth completes
10857c478bd9Sstevel@tonic-gate 	*/
10867c478bd9Sstevel@tonic-gate 
10877c478bd9Sstevel@tonic-gate 	*psecret = (sasl_secret_t *) sm_rpool_malloc(mci->mci_rpool,
10887c478bd9Sstevel@tonic-gate 						     sizeof(sasl_secret_t) +
10897c478bd9Sstevel@tonic-gate 						     len + 1);
10907c478bd9Sstevel@tonic-gate 	if (*psecret == NULL)
10917c478bd9Sstevel@tonic-gate 		return SASL_FAIL;
10927c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy((char *) (*psecret)->data, authpass, len + 1);
10937c478bd9Sstevel@tonic-gate 	(*psecret)->len = (unsigned long) len;
10947c478bd9Sstevel@tonic-gate 	return SASL_OK;
10957c478bd9Sstevel@tonic-gate }
10967c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
10977c478bd9Sstevel@tonic-gate /*
10987c478bd9Sstevel@tonic-gate **  GETSIMPLE -- callback to get userid or authid
10997c478bd9Sstevel@tonic-gate **
11007c478bd9Sstevel@tonic-gate **	Parameters:
11017c478bd9Sstevel@tonic-gate **		context -- sai
11027c478bd9Sstevel@tonic-gate **		id -- what to do
11037c478bd9Sstevel@tonic-gate **		result -- (pointer to) result
11047c478bd9Sstevel@tonic-gate **		len -- (pointer to) length of result
11057c478bd9Sstevel@tonic-gate **
11067c478bd9Sstevel@tonic-gate **	Returns:
11077c478bd9Sstevel@tonic-gate **		OK/failure values
11087c478bd9Sstevel@tonic-gate */
11097c478bd9Sstevel@tonic-gate 
11107c478bd9Sstevel@tonic-gate static int
getsimple(context,id,result,len)11117c478bd9Sstevel@tonic-gate getsimple(context, id, result, len)
11127c478bd9Sstevel@tonic-gate 	void *context;
11137c478bd9Sstevel@tonic-gate 	int id;
11147c478bd9Sstevel@tonic-gate 	const char **result;
11157c478bd9Sstevel@tonic-gate 	unsigned *len;
11167c478bd9Sstevel@tonic-gate {
11177c478bd9Sstevel@tonic-gate 	char *h, *s;
11187c478bd9Sstevel@tonic-gate # if SASL > 10509
11197c478bd9Sstevel@tonic-gate 	bool addrealm;
11207c478bd9Sstevel@tonic-gate # endif /* SASL > 10509 */
11217c478bd9Sstevel@tonic-gate 	size_t l;
11227c478bd9Sstevel@tonic-gate 	SASL_AI_T *sai;
11237c478bd9Sstevel@tonic-gate 	char *authid = NULL;
11247c478bd9Sstevel@tonic-gate 
11257c478bd9Sstevel@tonic-gate 	if (result == NULL || context == NULL)
11267c478bd9Sstevel@tonic-gate 		return SASL_BADPARAM;
11277c478bd9Sstevel@tonic-gate 	sai = (SASL_AI_T *) context;
11287c478bd9Sstevel@tonic-gate 
11297c478bd9Sstevel@tonic-gate 	/*
11307c478bd9Sstevel@tonic-gate 	**  Unfortunately it is not clear whether this routine should
11317c478bd9Sstevel@tonic-gate 	**  return a copy of a string or just a pointer to a string.
11327c478bd9Sstevel@tonic-gate 	**  The Cyrus-SASL plugins treat these return values differently, e.g.,
11337c478bd9Sstevel@tonic-gate 	**  plugins/cram.c free()s authid, plugings/digestmd5.c does not.
11347c478bd9Sstevel@tonic-gate 	**  The best solution to this problem is to fix Cyrus-SASL, but it
11357c478bd9Sstevel@tonic-gate 	**  seems there is nobody who creates patches... Hello CMU!?
11367c478bd9Sstevel@tonic-gate 	**  The second best solution is to have flags that tell this routine
11377c478bd9Sstevel@tonic-gate 	**  whether to return an malloc()ed copy.
11387c478bd9Sstevel@tonic-gate 	**  The next best solution is to always return an malloc()ed copy,
11397c478bd9Sstevel@tonic-gate 	**  and suffer from some memory leak, which is ugly for persistent
11407c478bd9Sstevel@tonic-gate 	**  queue runners.
11417c478bd9Sstevel@tonic-gate 	**  For now we go with the last solution...
11427c478bd9Sstevel@tonic-gate 	**  We can't use rpools (which would avoid this particular problem)
11437c478bd9Sstevel@tonic-gate 	**  as explained in sasl.c.
11447c478bd9Sstevel@tonic-gate 	*/
11457c478bd9Sstevel@tonic-gate 
11467c478bd9Sstevel@tonic-gate 	switch (id)
11477c478bd9Sstevel@tonic-gate 	{
11487c478bd9Sstevel@tonic-gate 	  case SASL_CB_USER:
11497c478bd9Sstevel@tonic-gate 		l = strlen((*sai)[SASL_USER]) + 1;
11507c478bd9Sstevel@tonic-gate 		s = sm_sasl_malloc(l);
11517c478bd9Sstevel@tonic-gate 		if (s == NULL)
11527c478bd9Sstevel@tonic-gate 		{
11537c478bd9Sstevel@tonic-gate 			if (len != NULL)
11547c478bd9Sstevel@tonic-gate 				*len = 0;
11557c478bd9Sstevel@tonic-gate 			*result = NULL;
11567c478bd9Sstevel@tonic-gate 			return SASL_NOMEM;
11577c478bd9Sstevel@tonic-gate 		}
11587c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(s, (*sai)[SASL_USER], l);
11597c478bd9Sstevel@tonic-gate 		*result = s;
11607c478bd9Sstevel@tonic-gate 		if (tTd(95, 5))
11617c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
11627c478bd9Sstevel@tonic-gate 				  *result);
11637c478bd9Sstevel@tonic-gate 		if (len != NULL)
11647c478bd9Sstevel@tonic-gate 			*len = *result != NULL ? strlen(*result) : 0;
11657c478bd9Sstevel@tonic-gate 		break;
11667c478bd9Sstevel@tonic-gate 
11677c478bd9Sstevel@tonic-gate 	  case SASL_CB_AUTHNAME:
11687c478bd9Sstevel@tonic-gate 		h = (*sai)[SASL_AUTHID];
11697c478bd9Sstevel@tonic-gate # if SASL > 10509
11707c478bd9Sstevel@tonic-gate 		/* XXX maybe other mechanisms too?! */
11717c478bd9Sstevel@tonic-gate 		addrealm = (*sai)[SASL_MECH] != NULL &&
11727c478bd9Sstevel@tonic-gate 			   sm_strcasecmp((*sai)[SASL_MECH], "CRAM-MD5") == 0;
11737c478bd9Sstevel@tonic-gate 
11747c478bd9Sstevel@tonic-gate 		/*
11757c478bd9Sstevel@tonic-gate 		**  Add realm to authentication id unless authid contains
11767c478bd9Sstevel@tonic-gate 		**  '@' (i.e., a realm) or the default realm is empty.
11777c478bd9Sstevel@tonic-gate 		*/
11787c478bd9Sstevel@tonic-gate 
11797c478bd9Sstevel@tonic-gate 		if (addrealm && h != NULL && strchr(h, '@') == NULL)
11807c478bd9Sstevel@tonic-gate 		{
11817c478bd9Sstevel@tonic-gate 			/* has this been done before? */
11827c478bd9Sstevel@tonic-gate 			if ((*sai)[SASL_ID_REALM] == NULL)
11837c478bd9Sstevel@tonic-gate 			{
11847c478bd9Sstevel@tonic-gate 				char *realm;
11857c478bd9Sstevel@tonic-gate 
11867c478bd9Sstevel@tonic-gate 				realm = (*sai)[SASL_DEFREALM];
11877c478bd9Sstevel@tonic-gate 
11887c478bd9Sstevel@tonic-gate 				/* do not add an empty realm */
11897c478bd9Sstevel@tonic-gate 				if (*realm == '\0')
11907c478bd9Sstevel@tonic-gate 				{
11917c478bd9Sstevel@tonic-gate 					authid = h;
11927c478bd9Sstevel@tonic-gate 					(*sai)[SASL_ID_REALM] = NULL;
11937c478bd9Sstevel@tonic-gate 				}
11947c478bd9Sstevel@tonic-gate 				else
11957c478bd9Sstevel@tonic-gate 				{
11967c478bd9Sstevel@tonic-gate 					l = strlen(h) + strlen(realm) + 2;
11977c478bd9Sstevel@tonic-gate 
11987c478bd9Sstevel@tonic-gate 					/* should use rpool, but from where? */
11997c478bd9Sstevel@tonic-gate 					authid = sm_sasl_malloc(l);
12007c478bd9Sstevel@tonic-gate 					if (authid != NULL)
12017c478bd9Sstevel@tonic-gate 					{
12027c478bd9Sstevel@tonic-gate 						(void) sm_snprintf(authid, l,
12037c478bd9Sstevel@tonic-gate 								  "%s@%s",
12047c478bd9Sstevel@tonic-gate 								   h, realm);
12057c478bd9Sstevel@tonic-gate 						(*sai)[SASL_ID_REALM] = authid;
12067c478bd9Sstevel@tonic-gate 					}
12077c478bd9Sstevel@tonic-gate 					else
12087c478bd9Sstevel@tonic-gate 					{
12097c478bd9Sstevel@tonic-gate 						authid = h;
12107c478bd9Sstevel@tonic-gate 						(*sai)[SASL_ID_REALM] = NULL;
12117c478bd9Sstevel@tonic-gate 					}
12127c478bd9Sstevel@tonic-gate 				}
12137c478bd9Sstevel@tonic-gate 			}
12147c478bd9Sstevel@tonic-gate 			else
12157c478bd9Sstevel@tonic-gate 				authid = (*sai)[SASL_ID_REALM];
12167c478bd9Sstevel@tonic-gate 		}
12177c478bd9Sstevel@tonic-gate 		else
12187c478bd9Sstevel@tonic-gate # endif /* SASL > 10509 */
12197c478bd9Sstevel@tonic-gate 			authid = h;
12207c478bd9Sstevel@tonic-gate 		l = strlen(authid) + 1;
12217c478bd9Sstevel@tonic-gate 		s = sm_sasl_malloc(l);
12227c478bd9Sstevel@tonic-gate 		if (s == NULL)
12237c478bd9Sstevel@tonic-gate 		{
12247c478bd9Sstevel@tonic-gate 			if (len != NULL)
12257c478bd9Sstevel@tonic-gate 				*len = 0;
12267c478bd9Sstevel@tonic-gate 			*result = NULL;
12277c478bd9Sstevel@tonic-gate 			return SASL_NOMEM;
12287c478bd9Sstevel@tonic-gate 		}
12297c478bd9Sstevel@tonic-gate 		(void) sm_strlcpy(s, authid, l);
12307c478bd9Sstevel@tonic-gate 		*result = s;
12317c478bd9Sstevel@tonic-gate 		if (tTd(95, 5))
12327c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
12337c478bd9Sstevel@tonic-gate 				  *result);
12347c478bd9Sstevel@tonic-gate 		if (len != NULL)
12357c478bd9Sstevel@tonic-gate 			*len = authid ? strlen(authid) : 0;
12367c478bd9Sstevel@tonic-gate 		break;
12377c478bd9Sstevel@tonic-gate 
12387c478bd9Sstevel@tonic-gate 	  case SASL_CB_LANGUAGE:
12397c478bd9Sstevel@tonic-gate 		*result = NULL;
12407c478bd9Sstevel@tonic-gate 		if (len != NULL)
12417c478bd9Sstevel@tonic-gate 			*len = 0;
12427c478bd9Sstevel@tonic-gate 		break;
12437c478bd9Sstevel@tonic-gate 
12447c478bd9Sstevel@tonic-gate 	  default:
12457c478bd9Sstevel@tonic-gate 		return SASL_BADPARAM;
12467c478bd9Sstevel@tonic-gate 	}
12477c478bd9Sstevel@tonic-gate 	return SASL_OK;
12487c478bd9Sstevel@tonic-gate }
12497c478bd9Sstevel@tonic-gate /*
12507c478bd9Sstevel@tonic-gate **  GETSECRET -- callback to get password
12517c478bd9Sstevel@tonic-gate **
12527c478bd9Sstevel@tonic-gate **	Parameters:
12537c478bd9Sstevel@tonic-gate **		conn -- connection information
12547c478bd9Sstevel@tonic-gate **		context -- sai
12557c478bd9Sstevel@tonic-gate **		id -- what to do
12567c478bd9Sstevel@tonic-gate **		psecret -- (pointer to) result
12577c478bd9Sstevel@tonic-gate **
12587c478bd9Sstevel@tonic-gate **	Returns:
12597c478bd9Sstevel@tonic-gate **		OK/failure values
12607c478bd9Sstevel@tonic-gate */
12617c478bd9Sstevel@tonic-gate 
12627c478bd9Sstevel@tonic-gate static int
getsecret(conn,context,id,psecret)12637c478bd9Sstevel@tonic-gate getsecret(conn, context, id, psecret)
12647c478bd9Sstevel@tonic-gate 	sasl_conn_t *conn;
12657c478bd9Sstevel@tonic-gate 	SM_UNUSED(void *context);
12667c478bd9Sstevel@tonic-gate 	int id;
12677c478bd9Sstevel@tonic-gate 	sasl_secret_t **psecret;
12687c478bd9Sstevel@tonic-gate {
12697c478bd9Sstevel@tonic-gate 	int len;
12707c478bd9Sstevel@tonic-gate 	char *authpass;
12717c478bd9Sstevel@tonic-gate 	SASL_AI_T *sai;
12727c478bd9Sstevel@tonic-gate 
12737c478bd9Sstevel@tonic-gate 	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
12747c478bd9Sstevel@tonic-gate 		return SASL_BADPARAM;
12757c478bd9Sstevel@tonic-gate 
12767c478bd9Sstevel@tonic-gate 	sai = (SASL_AI_T *) context;
12777c478bd9Sstevel@tonic-gate 	authpass = (*sai)[SASL_PASSWORD];
12787c478bd9Sstevel@tonic-gate 	len = strlen(authpass);
12797c478bd9Sstevel@tonic-gate 	*psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) +
12807c478bd9Sstevel@tonic-gate 						    len + 1);
12817c478bd9Sstevel@tonic-gate 	if (*psecret == NULL)
12827c478bd9Sstevel@tonic-gate 		return SASL_FAIL;
12837c478bd9Sstevel@tonic-gate 	(void) sm_strlcpy((*psecret)->data, authpass, len + 1);
12847c478bd9Sstevel@tonic-gate 	(*psecret)->len = (unsigned long) len;
12857c478bd9Sstevel@tonic-gate 	return SASL_OK;
12867c478bd9Sstevel@tonic-gate }
12877c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
12887c478bd9Sstevel@tonic-gate 
12897c478bd9Sstevel@tonic-gate /*
12907c478bd9Sstevel@tonic-gate **  SAFESASLFILE -- callback for sasl: is file safe?
12917c478bd9Sstevel@tonic-gate **
12927c478bd9Sstevel@tonic-gate **	Parameters:
12937c478bd9Sstevel@tonic-gate **		context -- pointer to context between invocations (unused)
12947c478bd9Sstevel@tonic-gate **		file -- name of file to check
12957c478bd9Sstevel@tonic-gate **		type -- type of file to check
12967c478bd9Sstevel@tonic-gate **
12977c478bd9Sstevel@tonic-gate **	Returns:
12987c478bd9Sstevel@tonic-gate **		SASL_OK -- file can be used
12997c478bd9Sstevel@tonic-gate **		SASL_CONTINUE -- don't use file
13007c478bd9Sstevel@tonic-gate **		SASL_FAIL -- failure (not used here)
13017c478bd9Sstevel@tonic-gate **
13027c478bd9Sstevel@tonic-gate */
13037c478bd9Sstevel@tonic-gate 
13047c478bd9Sstevel@tonic-gate int
13057c478bd9Sstevel@tonic-gate #if SASL > 10515
safesaslfile(context,file,type)13067c478bd9Sstevel@tonic-gate safesaslfile(context, file, type)
13077c478bd9Sstevel@tonic-gate #else /* SASL > 10515 */
13087c478bd9Sstevel@tonic-gate safesaslfile(context, file)
13097c478bd9Sstevel@tonic-gate #endif /* SASL > 10515 */
13107c478bd9Sstevel@tonic-gate 	void *context;
13117c478bd9Sstevel@tonic-gate # if SASL >= 20000
13127c478bd9Sstevel@tonic-gate 	const char *file;
13137c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
13147c478bd9Sstevel@tonic-gate 	char *file;
13157c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
13167c478bd9Sstevel@tonic-gate #if SASL > 10515
13177c478bd9Sstevel@tonic-gate # if SASL >= 20000
13187c478bd9Sstevel@tonic-gate 	sasl_verify_type_t type;
13197c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
13207c478bd9Sstevel@tonic-gate 	int type;
13217c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
13227c478bd9Sstevel@tonic-gate #endif /* SASL > 10515 */
13237c478bd9Sstevel@tonic-gate {
13247c478bd9Sstevel@tonic-gate 	long sff;
13257c478bd9Sstevel@tonic-gate 	int r;
13267c478bd9Sstevel@tonic-gate #if SASL <= 10515
13277c478bd9Sstevel@tonic-gate 	size_t len;
13287c478bd9Sstevel@tonic-gate #endif /* SASL <= 10515 */
13297c478bd9Sstevel@tonic-gate 	char *p;
13307c478bd9Sstevel@tonic-gate 
13317c478bd9Sstevel@tonic-gate 	if (file == NULL || *file == '\0')
13327c478bd9Sstevel@tonic-gate 		return SASL_OK;
13337c478bd9Sstevel@tonic-gate 	sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
13347c478bd9Sstevel@tonic-gate #if SASL <= 10515
13357c478bd9Sstevel@tonic-gate 	if ((p = strrchr(file, '/')) == NULL)
13367c478bd9Sstevel@tonic-gate 		p = file;
13377c478bd9Sstevel@tonic-gate 	else
13387c478bd9Sstevel@tonic-gate 		++p;
13397c478bd9Sstevel@tonic-gate 
13407c478bd9Sstevel@tonic-gate 	/* everything beside libs and .conf files must not be readable */
13417c478bd9Sstevel@tonic-gate 	len = strlen(p);
13427c478bd9Sstevel@tonic-gate 	if ((len <= 3 || strncmp(p, "lib", 3) != 0) &&
13437c478bd9Sstevel@tonic-gate 	    (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0))
13447c478bd9Sstevel@tonic-gate 	{
13457c478bd9Sstevel@tonic-gate 		if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
13467c478bd9Sstevel@tonic-gate 			sff |= SFF_NORFILES;
13477c478bd9Sstevel@tonic-gate 		if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
13487c478bd9Sstevel@tonic-gate 			sff |= SFF_NOGWFILES;
13497c478bd9Sstevel@tonic-gate 	}
13507c478bd9Sstevel@tonic-gate #else /* SASL <= 10515 */
13517c478bd9Sstevel@tonic-gate 	/* files containing passwords should be not readable */
13527c478bd9Sstevel@tonic-gate 	if (type == SASL_VRFY_PASSWD)
13537c478bd9Sstevel@tonic-gate 	{
13547c478bd9Sstevel@tonic-gate 		if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
13557c478bd9Sstevel@tonic-gate 			sff |= SFF_NOWRFILES;
13567c478bd9Sstevel@tonic-gate 		else
13577c478bd9Sstevel@tonic-gate 			sff |= SFF_NORFILES;
13587c478bd9Sstevel@tonic-gate 		if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
13597c478bd9Sstevel@tonic-gate 			sff |= SFF_NOGWFILES;
13607c478bd9Sstevel@tonic-gate 	}
13617c478bd9Sstevel@tonic-gate #endif /* SASL <= 10515 */
13627c478bd9Sstevel@tonic-gate 
13637c478bd9Sstevel@tonic-gate 	p = (char *) file;
13647c478bd9Sstevel@tonic-gate 	if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
13657c478bd9Sstevel@tonic-gate 			  S_IRUSR, NULL)) == 0)
13667c478bd9Sstevel@tonic-gate 		return SASL_OK;
13677c478bd9Sstevel@tonic-gate 	if (LogLevel > (r != ENOENT ? 8 : 10))
13687c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
13697c478bd9Sstevel@tonic-gate 			  p, sm_errstring(r));
13707c478bd9Sstevel@tonic-gate 	return SASL_CONTINUE;
13717c478bd9Sstevel@tonic-gate }
13727c478bd9Sstevel@tonic-gate 
13737c478bd9Sstevel@tonic-gate /*
13747c478bd9Sstevel@tonic-gate **  SASLGETREALM -- return the realm for SASL
13757c478bd9Sstevel@tonic-gate **
13767c478bd9Sstevel@tonic-gate **	return the realm for the client
13777c478bd9Sstevel@tonic-gate **
13787c478bd9Sstevel@tonic-gate **	Parameters:
13797c478bd9Sstevel@tonic-gate **		context -- context shared between invocations
13807c478bd9Sstevel@tonic-gate **		availrealms -- list of available realms
13817c478bd9Sstevel@tonic-gate **			{realm, realm, ...}
13827c478bd9Sstevel@tonic-gate **		result -- pointer to result
13837c478bd9Sstevel@tonic-gate **
13847c478bd9Sstevel@tonic-gate **	Returns:
13857c478bd9Sstevel@tonic-gate **		failure/success
13867c478bd9Sstevel@tonic-gate */
13877c478bd9Sstevel@tonic-gate 
13887c478bd9Sstevel@tonic-gate static int
saslgetrealm(context,id,availrealms,result)13897c478bd9Sstevel@tonic-gate saslgetrealm(context, id, availrealms, result)
13907c478bd9Sstevel@tonic-gate 	void *context;
13917c478bd9Sstevel@tonic-gate 	int id;
13927c478bd9Sstevel@tonic-gate 	const char **availrealms;
13937c478bd9Sstevel@tonic-gate 	const char **result;
13947c478bd9Sstevel@tonic-gate {
13957c478bd9Sstevel@tonic-gate 	char *r;
13967c478bd9Sstevel@tonic-gate 	SASL_AI_T *sai;
13977c478bd9Sstevel@tonic-gate 
13987c478bd9Sstevel@tonic-gate 	sai = (SASL_AI_T *) context;
13997c478bd9Sstevel@tonic-gate 	if (sai == NULL)
14007c478bd9Sstevel@tonic-gate 		return SASL_FAIL;
14017c478bd9Sstevel@tonic-gate 	r = (*sai)[SASL_DEFREALM];
14027c478bd9Sstevel@tonic-gate 
14037c478bd9Sstevel@tonic-gate 	if (LogLevel > 12)
14047c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_INFO, NOQID,
14057c478bd9Sstevel@tonic-gate 			  "AUTH=client, realm=%s, available realms=%s",
14067c478bd9Sstevel@tonic-gate 			  r == NULL ? "<No Realm>" : r,
14077c478bd9Sstevel@tonic-gate 			  (availrealms == NULL || *availrealms == NULL)
14087c478bd9Sstevel@tonic-gate 				? "<No Realms>" : *availrealms);
14097c478bd9Sstevel@tonic-gate 
14107c478bd9Sstevel@tonic-gate 	/* check whether context is in list */
14117c478bd9Sstevel@tonic-gate 	if (availrealms != NULL && *availrealms != NULL)
14127c478bd9Sstevel@tonic-gate 	{
14137c478bd9Sstevel@tonic-gate 		if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
14147c478bd9Sstevel@tonic-gate 		    NULL)
14157c478bd9Sstevel@tonic-gate 		{
14167c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
14177c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, NOQID,
14187c478bd9Sstevel@tonic-gate 					  "AUTH=client, realm=%s not in list=%s",
14197c478bd9Sstevel@tonic-gate 					  r, *availrealms);
14207c478bd9Sstevel@tonic-gate 			return SASL_FAIL;
14217c478bd9Sstevel@tonic-gate 		}
14227c478bd9Sstevel@tonic-gate 	}
14237c478bd9Sstevel@tonic-gate 	*result = r;
14247c478bd9Sstevel@tonic-gate 	return SASL_OK;
14257c478bd9Sstevel@tonic-gate }
14267c478bd9Sstevel@tonic-gate /*
14277c478bd9Sstevel@tonic-gate **  ITEMINLIST -- does item appear in list?
14287c478bd9Sstevel@tonic-gate **
14297c478bd9Sstevel@tonic-gate **	Check whether item appears in list (which must be separated by a
14307c478bd9Sstevel@tonic-gate **	character in delim) as a "word", i.e. it must appear at the begin
14317c478bd9Sstevel@tonic-gate **	of the list or after a space, and it must end with a space or the
14327c478bd9Sstevel@tonic-gate **	end of the list.
14337c478bd9Sstevel@tonic-gate **
14347c478bd9Sstevel@tonic-gate **	Parameters:
14357c478bd9Sstevel@tonic-gate **		item -- item to search.
14367c478bd9Sstevel@tonic-gate **		list -- list of items.
14377c478bd9Sstevel@tonic-gate **		delim -- list of delimiters.
14387c478bd9Sstevel@tonic-gate **
14397c478bd9Sstevel@tonic-gate **	Returns:
14407c478bd9Sstevel@tonic-gate **		pointer to occurrence (NULL if not found).
14417c478bd9Sstevel@tonic-gate */
14427c478bd9Sstevel@tonic-gate 
14437c478bd9Sstevel@tonic-gate char *
iteminlist(item,list,delim)14447c478bd9Sstevel@tonic-gate iteminlist(item, list, delim)
14457c478bd9Sstevel@tonic-gate 	char *item;
14467c478bd9Sstevel@tonic-gate 	char *list;
14477c478bd9Sstevel@tonic-gate 	char *delim;
14487c478bd9Sstevel@tonic-gate {
14497c478bd9Sstevel@tonic-gate 	char *s;
14507c478bd9Sstevel@tonic-gate 	int len;
14517c478bd9Sstevel@tonic-gate 
14527c478bd9Sstevel@tonic-gate 	if (list == NULL || *list == '\0')
14537c478bd9Sstevel@tonic-gate 		return NULL;
14547c478bd9Sstevel@tonic-gate 	if (item == NULL || *item == '\0')
14557c478bd9Sstevel@tonic-gate 		return NULL;
14567c478bd9Sstevel@tonic-gate 	s = list;
14577c478bd9Sstevel@tonic-gate 	len = strlen(item);
14587c478bd9Sstevel@tonic-gate 	while (s != NULL && *s != '\0')
14597c478bd9Sstevel@tonic-gate 	{
14607c478bd9Sstevel@tonic-gate 		if (sm_strncasecmp(s, item, len) == 0 &&
14617c478bd9Sstevel@tonic-gate 		    (s[len] == '\0' || strchr(delim, s[len]) != NULL))
14627c478bd9Sstevel@tonic-gate 			return s;
14637c478bd9Sstevel@tonic-gate 		s = strpbrk(s, delim);
14647c478bd9Sstevel@tonic-gate 		if (s != NULL)
14657c478bd9Sstevel@tonic-gate 			while (*++s == ' ')
14667c478bd9Sstevel@tonic-gate 				continue;
14677c478bd9Sstevel@tonic-gate 	}
14687c478bd9Sstevel@tonic-gate 	return NULL;
14697c478bd9Sstevel@tonic-gate }
14707c478bd9Sstevel@tonic-gate /*
14717c478bd9Sstevel@tonic-gate **  REMOVEMECH -- remove item [rem] from list [list]
14727c478bd9Sstevel@tonic-gate **
14737c478bd9Sstevel@tonic-gate **	Parameters:
14747c478bd9Sstevel@tonic-gate **		rem -- item to remove
14757c478bd9Sstevel@tonic-gate **		list -- list of items
14767c478bd9Sstevel@tonic-gate **		rpool -- resource pool from which result is allocated.
14777c478bd9Sstevel@tonic-gate **
14787c478bd9Sstevel@tonic-gate **	Returns:
14797c478bd9Sstevel@tonic-gate **		pointer to new list (NULL in case of error).
14807c478bd9Sstevel@tonic-gate */
14817c478bd9Sstevel@tonic-gate 
14827c478bd9Sstevel@tonic-gate static char *
removemech(rem,list,rpool)14837c478bd9Sstevel@tonic-gate removemech(rem, list, rpool)
14847c478bd9Sstevel@tonic-gate 	char *rem;
14857c478bd9Sstevel@tonic-gate 	char *list;
14867c478bd9Sstevel@tonic-gate 	SM_RPOOL_T *rpool;
14877c478bd9Sstevel@tonic-gate {
14887c478bd9Sstevel@tonic-gate 	char *ret;
14897c478bd9Sstevel@tonic-gate 	char *needle;
14907c478bd9Sstevel@tonic-gate 	int len;
14917c478bd9Sstevel@tonic-gate 
14927c478bd9Sstevel@tonic-gate 	if (list == NULL)
14937c478bd9Sstevel@tonic-gate 		return NULL;
14947c478bd9Sstevel@tonic-gate 	if (rem == NULL || *rem == '\0')
14957c478bd9Sstevel@tonic-gate 	{
14967c478bd9Sstevel@tonic-gate 		/* take out what? */
14977c478bd9Sstevel@tonic-gate 		return NULL;
14987c478bd9Sstevel@tonic-gate 	}
14997c478bd9Sstevel@tonic-gate 
15007c478bd9Sstevel@tonic-gate 	/* find the item in the list */
15017c478bd9Sstevel@tonic-gate 	if ((needle = iteminlist(rem, list, " ")) == NULL)
15027c478bd9Sstevel@tonic-gate 	{
15037c478bd9Sstevel@tonic-gate 		/* not in there: return original */
15047c478bd9Sstevel@tonic-gate 		return list;
15057c478bd9Sstevel@tonic-gate 	}
15067c478bd9Sstevel@tonic-gate 
15077c478bd9Sstevel@tonic-gate 	/* length of string without rem */
15087c478bd9Sstevel@tonic-gate 	len = strlen(list) - strlen(rem);
15097c478bd9Sstevel@tonic-gate 	if (len <= 0)
15107c478bd9Sstevel@tonic-gate 	{
15117c478bd9Sstevel@tonic-gate 		ret = (char *) sm_rpool_malloc_x(rpool, 1);
15127c478bd9Sstevel@tonic-gate 		*ret = '\0';
15137c478bd9Sstevel@tonic-gate 		return ret;
15147c478bd9Sstevel@tonic-gate 	}
15157c478bd9Sstevel@tonic-gate 	ret = (char *) sm_rpool_malloc_x(rpool, len);
15167c478bd9Sstevel@tonic-gate 	memset(ret, '\0', len);
15177c478bd9Sstevel@tonic-gate 
15187c478bd9Sstevel@tonic-gate 	/* copy from start to removed item */
15197c478bd9Sstevel@tonic-gate 	memcpy(ret, list, needle - list);
15207c478bd9Sstevel@tonic-gate 
15217c478bd9Sstevel@tonic-gate 	/* length of rest of string past removed item */
15227c478bd9Sstevel@tonic-gate 	len = strlen(needle) - strlen(rem) - 1;
15237c478bd9Sstevel@tonic-gate 	if (len > 0)
15247c478bd9Sstevel@tonic-gate 	{
15257c478bd9Sstevel@tonic-gate 		/* not last item -- copy into string */
15267c478bd9Sstevel@tonic-gate 		memcpy(ret + (needle - list),
15277c478bd9Sstevel@tonic-gate 		       list + (needle - list) + strlen(rem) + 1,
15287c478bd9Sstevel@tonic-gate 		       len);
15297c478bd9Sstevel@tonic-gate 	}
15307c478bd9Sstevel@tonic-gate 	else
15317c478bd9Sstevel@tonic-gate 		ret[(needle - list) - 1] = '\0';
15327c478bd9Sstevel@tonic-gate 	return ret;
15337c478bd9Sstevel@tonic-gate }
15347c478bd9Sstevel@tonic-gate /*
15357c478bd9Sstevel@tonic-gate **  ATTEMPTAUTH -- try to AUTHenticate using one mechanism
15367c478bd9Sstevel@tonic-gate **
15377c478bd9Sstevel@tonic-gate **	Parameters:
15387c478bd9Sstevel@tonic-gate **		m -- the mailer.
15397c478bd9Sstevel@tonic-gate **		mci -- the mailer connection structure.
15407c478bd9Sstevel@tonic-gate **		e -- the envelope (including the sender to specify).
15417c478bd9Sstevel@tonic-gate **		sai - sasl authinfo
15427c478bd9Sstevel@tonic-gate **
15437c478bd9Sstevel@tonic-gate **	Returns:
15447c478bd9Sstevel@tonic-gate **		EX_OK -- authentication was successful.
15457c478bd9Sstevel@tonic-gate **		EX_NOPERM -- authentication failed.
15467c478bd9Sstevel@tonic-gate **		EX_IOERR -- authentication dialogue failed (I/O problem?).
15477c478bd9Sstevel@tonic-gate **		EX_TEMPFAIL -- temporary failure.
15487c478bd9Sstevel@tonic-gate **
15497c478bd9Sstevel@tonic-gate */
15507c478bd9Sstevel@tonic-gate 
15517c478bd9Sstevel@tonic-gate static int
attemptauth(m,mci,e,sai)15527c478bd9Sstevel@tonic-gate attemptauth(m, mci, e, sai)
15537c478bd9Sstevel@tonic-gate 	MAILER *m;
15547c478bd9Sstevel@tonic-gate 	MCI *mci;
15557c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
15567c478bd9Sstevel@tonic-gate 	SASL_AI_T *sai;
15577c478bd9Sstevel@tonic-gate {
15587c478bd9Sstevel@tonic-gate 	int saslresult, smtpresult;
15597c478bd9Sstevel@tonic-gate # if SASL >= 20000
15607c478bd9Sstevel@tonic-gate 	sasl_ssf_t ssf;
15617c478bd9Sstevel@tonic-gate 	const char *auth_id;
15627c478bd9Sstevel@tonic-gate 	const char *out;
15637c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
15647c478bd9Sstevel@tonic-gate 	sasl_external_properties_t ssf;
15657c478bd9Sstevel@tonic-gate 	char *out;
15667c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
15677c478bd9Sstevel@tonic-gate 	unsigned int outlen;
15687c478bd9Sstevel@tonic-gate 	sasl_interact_t *client_interact = NULL;
15697c478bd9Sstevel@tonic-gate 	char *mechusing;
15707c478bd9Sstevel@tonic-gate 	sasl_security_properties_t ssp;
1571*e9af4bc0SJohn Beck 
1572*e9af4bc0SJohn Beck 	/* MUST NOT be a multiple of 4: bug in some sasl_encode64() versions */
1573*e9af4bc0SJohn Beck 	char in64[MAXOUTLEN + 1];
15747c478bd9Sstevel@tonic-gate #if NETINET || (NETINET6 && SASL >= 20000)
15757c478bd9Sstevel@tonic-gate 	extern SOCKADDR CurHostAddr;
15767c478bd9Sstevel@tonic-gate #endif /* NETINET || (NETINET6 && SASL >= 20000) */
15777c478bd9Sstevel@tonic-gate 
15787c478bd9Sstevel@tonic-gate 	/* no mechanism selected (yet) */
15797c478bd9Sstevel@tonic-gate 	(*sai)[SASL_MECH] = NULL;
15807c478bd9Sstevel@tonic-gate 
15817c478bd9Sstevel@tonic-gate 	/* dispose old connection */
15827c478bd9Sstevel@tonic-gate 	if (mci->mci_conn != NULL)
15837c478bd9Sstevel@tonic-gate 		sasl_dispose(&(mci->mci_conn));
15847c478bd9Sstevel@tonic-gate 
15857c478bd9Sstevel@tonic-gate 	/* make a new client sasl connection */
15867c478bd9Sstevel@tonic-gate # if SASL >= 20000
1587058561cbSjbeck 	/*
1588058561cbSjbeck 	**  We provide the callbacks again because global callbacks in
1589058561cbSjbeck 	**  sasl_client_init() are ignored if SASL has been initialized
1590058561cbSjbeck 	**  before, for example, by a library such as libnss-ldap.
1591058561cbSjbeck 	*/
1592058561cbSjbeck 
15937c478bd9Sstevel@tonic-gate 	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
15947c478bd9Sstevel@tonic-gate 								 : "smtp",
1595058561cbSjbeck 				     CurHostName, NULL, NULL, callbacks, 0,
15967c478bd9Sstevel@tonic-gate 				     &mci->mci_conn);
15977c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
15987c478bd9Sstevel@tonic-gate 	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
15997c478bd9Sstevel@tonic-gate 								 : "smtp",
16007c478bd9Sstevel@tonic-gate 				     CurHostName, NULL, 0, &mci->mci_conn);
16017c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
16027c478bd9Sstevel@tonic-gate 	if (saslresult != SASL_OK)
16037c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
16047c478bd9Sstevel@tonic-gate 
16057c478bd9Sstevel@tonic-gate 	/* set properties */
1606058561cbSjbeck 	(void) memset(&ssp, '\0', sizeof(ssp));
16077c478bd9Sstevel@tonic-gate 
16087c478bd9Sstevel@tonic-gate 	/* XXX should these be options settable via .cf ? */
16097c478bd9Sstevel@tonic-gate 	{
16107c478bd9Sstevel@tonic-gate 		ssp.max_ssf = MaxSLBits;
16117c478bd9Sstevel@tonic-gate 		ssp.maxbufsize = MAXOUTLEN;
16127c478bd9Sstevel@tonic-gate #  if 0
16137c478bd9Sstevel@tonic-gate 		ssp.security_flags = SASL_SEC_NOPLAINTEXT;
16147c478bd9Sstevel@tonic-gate #  endif /* 0 */
16157c478bd9Sstevel@tonic-gate 	}
16167c478bd9Sstevel@tonic-gate 	saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
16177c478bd9Sstevel@tonic-gate 	if (saslresult != SASL_OK)
16187c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
16197c478bd9Sstevel@tonic-gate 
16207c478bd9Sstevel@tonic-gate # if SASL >= 20000
16217c478bd9Sstevel@tonic-gate 	/* external security strength factor, authentication id */
16227c478bd9Sstevel@tonic-gate 	ssf = 0;
16237c478bd9Sstevel@tonic-gate 	auth_id = NULL;
16247c478bd9Sstevel@tonic-gate #  if STARTTLS
16257c478bd9Sstevel@tonic-gate 	out = macvalue(macid("{cert_subject}"), e);
16267c478bd9Sstevel@tonic-gate 	if (out != NULL && *out != '\0')
16277c478bd9Sstevel@tonic-gate 		auth_id = out;
16287c478bd9Sstevel@tonic-gate 	out = macvalue(macid("{cipher_bits}"), e);
16297c478bd9Sstevel@tonic-gate 	if (out != NULL && *out != '\0')
16307c478bd9Sstevel@tonic-gate 		ssf = atoi(out);
16317c478bd9Sstevel@tonic-gate #  endif /* STARTTLS */
16327c478bd9Sstevel@tonic-gate 	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
16337c478bd9Sstevel@tonic-gate 	if (saslresult != SASL_OK)
16347c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
16357c478bd9Sstevel@tonic-gate 	saslresult = sasl_setprop(mci->mci_conn, SASL_AUTH_EXTERNAL, auth_id);
16367c478bd9Sstevel@tonic-gate 	if (saslresult != SASL_OK)
16377c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
16387c478bd9Sstevel@tonic-gate 
16397c478bd9Sstevel@tonic-gate #  if NETINET || NETINET6
16407c478bd9Sstevel@tonic-gate 	/* set local/remote ipv4 addresses */
16417c478bd9Sstevel@tonic-gate 	if (mci->mci_out != NULL && (
16427c478bd9Sstevel@tonic-gate #   if NETINET6
16437c478bd9Sstevel@tonic-gate 		CurHostAddr.sa.sa_family == AF_INET6 ||
16447c478bd9Sstevel@tonic-gate #   endif /* NETINET6 */
16457c478bd9Sstevel@tonic-gate 		CurHostAddr.sa.sa_family == AF_INET))
16467c478bd9Sstevel@tonic-gate 	{
16477c478bd9Sstevel@tonic-gate 		SOCKADDR_LEN_T addrsize;
16487c478bd9Sstevel@tonic-gate 		SOCKADDR saddr_l;
16497c478bd9Sstevel@tonic-gate 		char localip[60], remoteip[60];
16507c478bd9Sstevel@tonic-gate 
16517c478bd9Sstevel@tonic-gate 		switch (CurHostAddr.sa.sa_family)
16527c478bd9Sstevel@tonic-gate 		{
16537c478bd9Sstevel@tonic-gate 		  case AF_INET:
16547c478bd9Sstevel@tonic-gate 			addrsize = sizeof(struct sockaddr_in);
16557c478bd9Sstevel@tonic-gate 			break;
16567c478bd9Sstevel@tonic-gate #   if NETINET6
16577c478bd9Sstevel@tonic-gate 		  case AF_INET6:
16587c478bd9Sstevel@tonic-gate 			addrsize = sizeof(struct sockaddr_in6);
16597c478bd9Sstevel@tonic-gate 			break;
16607c478bd9Sstevel@tonic-gate #   endif /* NETINET6 */
16617c478bd9Sstevel@tonic-gate 		  default:
16627c478bd9Sstevel@tonic-gate 			break;
16637c478bd9Sstevel@tonic-gate 		}
16647c478bd9Sstevel@tonic-gate 		if (iptostring(&CurHostAddr, addrsize,
1665058561cbSjbeck 			       remoteip, sizeof(remoteip)))
16667c478bd9Sstevel@tonic-gate 		{
16677c478bd9Sstevel@tonic-gate 			if (sasl_setprop(mci->mci_conn, SASL_IPREMOTEPORT,
16687c478bd9Sstevel@tonic-gate 					 remoteip) != SASL_OK)
16697c478bd9Sstevel@tonic-gate 				return EX_TEMPFAIL;
16707c478bd9Sstevel@tonic-gate 		}
16717c478bd9Sstevel@tonic-gate 		addrsize = sizeof(saddr_l);
16727c478bd9Sstevel@tonic-gate 		if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
16737c478bd9Sstevel@tonic-gate 					      NULL),
16747c478bd9Sstevel@tonic-gate 				(struct sockaddr *) &saddr_l, &addrsize) == 0)
16757c478bd9Sstevel@tonic-gate 		{
16767c478bd9Sstevel@tonic-gate 			if (iptostring(&saddr_l, addrsize,
1677058561cbSjbeck 				       localip, sizeof(localip)))
16787c478bd9Sstevel@tonic-gate 			{
16797c478bd9Sstevel@tonic-gate 				if (sasl_setprop(mci->mci_conn,
16807c478bd9Sstevel@tonic-gate 						 SASL_IPLOCALPORT,
16817c478bd9Sstevel@tonic-gate 						 localip) != SASL_OK)
16827c478bd9Sstevel@tonic-gate 					return EX_TEMPFAIL;
16837c478bd9Sstevel@tonic-gate 			}
16847c478bd9Sstevel@tonic-gate 		}
16857c478bd9Sstevel@tonic-gate 	}
16867c478bd9Sstevel@tonic-gate #  endif /* NETINET || NETINET6 */
16877c478bd9Sstevel@tonic-gate 
16887c478bd9Sstevel@tonic-gate 	/* start client side of sasl */
16897c478bd9Sstevel@tonic-gate 	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
16907c478bd9Sstevel@tonic-gate 				       &client_interact,
16917c478bd9Sstevel@tonic-gate 				       &out, &outlen,
16927c478bd9Sstevel@tonic-gate 				       (const char **) &mechusing);
16937c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
16947c478bd9Sstevel@tonic-gate 	/* external security strength factor, authentication id */
16957c478bd9Sstevel@tonic-gate 	ssf.ssf = 0;
16967c478bd9Sstevel@tonic-gate 	ssf.auth_id = NULL;
16977c478bd9Sstevel@tonic-gate #  if STARTTLS
16987c478bd9Sstevel@tonic-gate 	out = macvalue(macid("{cert_subject}"), e);
16997c478bd9Sstevel@tonic-gate 	if (out != NULL && *out != '\0')
17007c478bd9Sstevel@tonic-gate 		ssf.auth_id = out;
17017c478bd9Sstevel@tonic-gate 	out = macvalue(macid("{cipher_bits}"), e);
17027c478bd9Sstevel@tonic-gate 	if (out != NULL && *out != '\0')
17037c478bd9Sstevel@tonic-gate 		ssf.ssf = atoi(out);
17047c478bd9Sstevel@tonic-gate #  endif /* STARTTLS */
17057c478bd9Sstevel@tonic-gate 	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
17067c478bd9Sstevel@tonic-gate 	if (saslresult != SASL_OK)
17077c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
17087c478bd9Sstevel@tonic-gate 
17097c478bd9Sstevel@tonic-gate #  if NETINET
17107c478bd9Sstevel@tonic-gate 	/* set local/remote ipv4 addresses */
17117c478bd9Sstevel@tonic-gate 	if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
17127c478bd9Sstevel@tonic-gate 	{
17137c478bd9Sstevel@tonic-gate 		SOCKADDR_LEN_T addrsize;
17147c478bd9Sstevel@tonic-gate 		struct sockaddr_in saddr_l;
17157c478bd9Sstevel@tonic-gate 
17167c478bd9Sstevel@tonic-gate 		if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
17177c478bd9Sstevel@tonic-gate 				 (struct sockaddr_in *) &CurHostAddr)
17187c478bd9Sstevel@tonic-gate 		    != SASL_OK)
17197c478bd9Sstevel@tonic-gate 			return EX_TEMPFAIL;
17207c478bd9Sstevel@tonic-gate 		addrsize = sizeof(struct sockaddr_in);
17217c478bd9Sstevel@tonic-gate 		if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
17227c478bd9Sstevel@tonic-gate 					      NULL),
17237c478bd9Sstevel@tonic-gate 				(struct sockaddr *) &saddr_l, &addrsize) == 0)
17247c478bd9Sstevel@tonic-gate 		{
17257c478bd9Sstevel@tonic-gate 			if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
17267c478bd9Sstevel@tonic-gate 					 &saddr_l) != SASL_OK)
17277c478bd9Sstevel@tonic-gate 				return EX_TEMPFAIL;
17287c478bd9Sstevel@tonic-gate 		}
17297c478bd9Sstevel@tonic-gate 	}
17307c478bd9Sstevel@tonic-gate #  endif /* NETINET */
17317c478bd9Sstevel@tonic-gate 
17327c478bd9Sstevel@tonic-gate 	/* start client side of sasl */
17337c478bd9Sstevel@tonic-gate 	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
17347c478bd9Sstevel@tonic-gate 				       NULL, &client_interact,
17357c478bd9Sstevel@tonic-gate 				       &out, &outlen,
17367c478bd9Sstevel@tonic-gate 				       (const char **) &mechusing);
17377c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
17387c478bd9Sstevel@tonic-gate 
17397c478bd9Sstevel@tonic-gate 	if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
17407c478bd9Sstevel@tonic-gate 	{
17417c478bd9Sstevel@tonic-gate 		if (saslresult == SASL_NOMECH && LogLevel > 8)
17427c478bd9Sstevel@tonic-gate 		{
17437c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_NOTICE, e->e_id,
17447c478bd9Sstevel@tonic-gate 				  "AUTH=client, available mechanisms do not fulfill requirements");
17457c478bd9Sstevel@tonic-gate 		}
17467c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
17477c478bd9Sstevel@tonic-gate 	}
17487c478bd9Sstevel@tonic-gate 
17497c478bd9Sstevel@tonic-gate 	/* just point current mechanism to the data in the sasl library */
17507c478bd9Sstevel@tonic-gate 	(*sai)[SASL_MECH] = mechusing;
17517c478bd9Sstevel@tonic-gate 
17527c478bd9Sstevel@tonic-gate 	/* send the info across the wire */
17537c478bd9Sstevel@tonic-gate 	if (out == NULL
17547c478bd9Sstevel@tonic-gate 		/* login and digest-md5 up to 1.5.28 set out="" */
17557c478bd9Sstevel@tonic-gate 	    || (outlen == 0 &&
17567c478bd9Sstevel@tonic-gate 		(sm_strcasecmp(mechusing, "LOGIN") == 0 ||
17577c478bd9Sstevel@tonic-gate 		 sm_strcasecmp(mechusing, "DIGEST-MD5") == 0))
17587c478bd9Sstevel@tonic-gate 	   )
17597c478bd9Sstevel@tonic-gate 	{
17607c478bd9Sstevel@tonic-gate 		/* no initial response */
17617c478bd9Sstevel@tonic-gate 		smtpmessage("AUTH %s", m, mci, mechusing);
17627c478bd9Sstevel@tonic-gate 	}
17637c478bd9Sstevel@tonic-gate 	else if (outlen == 0)
17647c478bd9Sstevel@tonic-gate 	{
17657c478bd9Sstevel@tonic-gate 		/*
17667c478bd9Sstevel@tonic-gate 		**  zero-length initial response, per RFC 2554 4.:
17677c478bd9Sstevel@tonic-gate 		**  "Unlike a zero-length client answer to a 334 reply, a zero-
17687c478bd9Sstevel@tonic-gate 		**  length initial response is sent as a single equals sign"
17697c478bd9Sstevel@tonic-gate 		*/
17707c478bd9Sstevel@tonic-gate 
17717c478bd9Sstevel@tonic-gate 		smtpmessage("AUTH %s =", m, mci, mechusing);
17727c478bd9Sstevel@tonic-gate 	}
17737c478bd9Sstevel@tonic-gate 	else
17747c478bd9Sstevel@tonic-gate 	{
1775*e9af4bc0SJohn Beck 		saslresult = sasl_encode64(out, outlen, in64, sizeof(in64),
1776*e9af4bc0SJohn Beck 					   NULL);
17777c478bd9Sstevel@tonic-gate 		if (saslresult != SASL_OK) /* internal error */
17787c478bd9Sstevel@tonic-gate 		{
17797c478bd9Sstevel@tonic-gate 			if (LogLevel > 8)
17807c478bd9Sstevel@tonic-gate 				sm_syslog(LOG_ERR, e->e_id,
17817c478bd9Sstevel@tonic-gate 					"encode64 for AUTH failed");
17827c478bd9Sstevel@tonic-gate 			return EX_TEMPFAIL;
17837c478bd9Sstevel@tonic-gate 		}
17847c478bd9Sstevel@tonic-gate 		smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
17857c478bd9Sstevel@tonic-gate 	}
17867c478bd9Sstevel@tonic-gate # if SASL < 20000
17877c478bd9Sstevel@tonic-gate 	sm_sasl_free(out); /* XXX only if no rpool is used */
17887c478bd9Sstevel@tonic-gate # endif /* SASL < 20000 */
17897c478bd9Sstevel@tonic-gate 
17907c478bd9Sstevel@tonic-gate 	/* get the reply */
17917c478bd9Sstevel@tonic-gate 	smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL,
17927c478bd9Sstevel@tonic-gate 			XS_AUTH);
17937c478bd9Sstevel@tonic-gate 
17947c478bd9Sstevel@tonic-gate 	for (;;)
17957c478bd9Sstevel@tonic-gate 	{
17967c478bd9Sstevel@tonic-gate 		/* check return code from server */
17977c478bd9Sstevel@tonic-gate 		if (smtpresult == 235)
17987c478bd9Sstevel@tonic-gate 		{
17997c478bd9Sstevel@tonic-gate 			macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"),
18007c478bd9Sstevel@tonic-gate 				  mechusing);
18017c478bd9Sstevel@tonic-gate 			return EX_OK;
18027c478bd9Sstevel@tonic-gate 		}
18037c478bd9Sstevel@tonic-gate 		if (smtpresult == -1)
18047c478bd9Sstevel@tonic-gate 			return EX_IOERR;
18057c478bd9Sstevel@tonic-gate 		if (REPLYTYPE(smtpresult) == 5)
18067c478bd9Sstevel@tonic-gate 			return EX_NOPERM;	/* ugly, but ... */
18077c478bd9Sstevel@tonic-gate 		if (REPLYTYPE(smtpresult) != 3)
18087c478bd9Sstevel@tonic-gate 		{
18097c478bd9Sstevel@tonic-gate 			/* should we fail deliberately, see RFC 2554 4. ? */
18107c478bd9Sstevel@tonic-gate 			/* smtpmessage("*", m, mci); */
18117c478bd9Sstevel@tonic-gate 			return EX_TEMPFAIL;
18127c478bd9Sstevel@tonic-gate 		}
18137c478bd9Sstevel@tonic-gate 
18147c478bd9Sstevel@tonic-gate 		saslresult = sasl_client_step(mci->mci_conn,
18157c478bd9Sstevel@tonic-gate 					      mci->mci_sasl_string,
18167c478bd9Sstevel@tonic-gate 					      mci->mci_sasl_string_len,
18177c478bd9Sstevel@tonic-gate 					      &client_interact,
18187c478bd9Sstevel@tonic-gate 					      &out, &outlen);
18197c478bd9Sstevel@tonic-gate 
18207c478bd9Sstevel@tonic-gate 		if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
18217c478bd9Sstevel@tonic-gate 		{
18227c478bd9Sstevel@tonic-gate 			if (tTd(95, 5))
18237c478bd9Sstevel@tonic-gate 				sm_dprintf("AUTH FAIL=%s (%d)\n",
18247c478bd9Sstevel@tonic-gate 					sasl_errstring(saslresult, NULL, NULL),
18257c478bd9Sstevel@tonic-gate 					saslresult);
18267c478bd9Sstevel@tonic-gate 
18277c478bd9Sstevel@tonic-gate 			/* fail deliberately, see RFC 2554 4. */
18287c478bd9Sstevel@tonic-gate 			smtpmessage("*", m, mci);
18297c478bd9Sstevel@tonic-gate 
18307c478bd9Sstevel@tonic-gate 			/*
18317c478bd9Sstevel@tonic-gate 			**  but we should only fail for this authentication
18327c478bd9Sstevel@tonic-gate 			**  mechanism; how to do that?
18337c478bd9Sstevel@tonic-gate 			*/
18347c478bd9Sstevel@tonic-gate 
18357c478bd9Sstevel@tonic-gate 			smtpresult = reply(m, mci, e, TimeOuts.to_auth,
18367c478bd9Sstevel@tonic-gate 					   getsasldata, NULL, XS_AUTH);
18377c478bd9Sstevel@tonic-gate 			return EX_NOPERM;
18387c478bd9Sstevel@tonic-gate 		}
18397c478bd9Sstevel@tonic-gate 
18407c478bd9Sstevel@tonic-gate 		if (outlen > 0)
18417c478bd9Sstevel@tonic-gate 		{
18427c478bd9Sstevel@tonic-gate 			saslresult = sasl_encode64(out, outlen, in64,
1843*e9af4bc0SJohn Beck 						   sizeof(in64), NULL);
18447c478bd9Sstevel@tonic-gate 			if (saslresult != SASL_OK)
18457c478bd9Sstevel@tonic-gate 			{
18467c478bd9Sstevel@tonic-gate 				/* give an error reply to the other side! */
18477c478bd9Sstevel@tonic-gate 				smtpmessage("*", m, mci);
18487c478bd9Sstevel@tonic-gate 				return EX_TEMPFAIL;
18497c478bd9Sstevel@tonic-gate 			}
18507c478bd9Sstevel@tonic-gate 		}
18517c478bd9Sstevel@tonic-gate 		else
18527c478bd9Sstevel@tonic-gate 			in64[0] = '\0';
18537c478bd9Sstevel@tonic-gate # if SASL < 20000
18547c478bd9Sstevel@tonic-gate 		sm_sasl_free(out); /* XXX only if no rpool is used */
18557c478bd9Sstevel@tonic-gate # endif /* SASL < 20000 */
18567c478bd9Sstevel@tonic-gate 		smtpmessage("%s", m, mci, in64);
18577c478bd9Sstevel@tonic-gate 		smtpresult = reply(m, mci, e, TimeOuts.to_auth,
18587c478bd9Sstevel@tonic-gate 				   getsasldata, NULL, XS_AUTH);
18597c478bd9Sstevel@tonic-gate 	}
18607c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
18617c478bd9Sstevel@tonic-gate }
18627c478bd9Sstevel@tonic-gate /*
18637c478bd9Sstevel@tonic-gate **  SMTPAUTH -- try to AUTHenticate
18647c478bd9Sstevel@tonic-gate **
18657c478bd9Sstevel@tonic-gate **	This will try mechanisms in the order the sasl library decided until:
18667c478bd9Sstevel@tonic-gate **	- there are no more mechanisms
18677c478bd9Sstevel@tonic-gate **	- a mechanism succeeds
18687c478bd9Sstevel@tonic-gate **	- the sasl library fails initializing
18697c478bd9Sstevel@tonic-gate **
18707c478bd9Sstevel@tonic-gate **	Parameters:
18717c478bd9Sstevel@tonic-gate **		m -- the mailer.
18727c478bd9Sstevel@tonic-gate **		mci -- the mailer connection info.
18737c478bd9Sstevel@tonic-gate **		e -- the envelope.
18747c478bd9Sstevel@tonic-gate **
18757c478bd9Sstevel@tonic-gate **	Returns:
18767c478bd9Sstevel@tonic-gate **		EX_OK -- authentication was successful
18777c478bd9Sstevel@tonic-gate **		EX_UNAVAILABLE -- authentication not possible, e.g.,
18787c478bd9Sstevel@tonic-gate **			no data available.
18797c478bd9Sstevel@tonic-gate **		EX_NOPERM -- authentication failed.
18807c478bd9Sstevel@tonic-gate **		EX_TEMPFAIL -- temporary failure.
18817c478bd9Sstevel@tonic-gate **
18827c478bd9Sstevel@tonic-gate **	Notice: AuthInfo is used for all connections, hence we must
18837c478bd9Sstevel@tonic-gate **		return EX_TEMPFAIL only if we really want to retry, i.e.,
18847c478bd9Sstevel@tonic-gate **		iff getauth() tempfailed or getauth() was used and
18857c478bd9Sstevel@tonic-gate **		authentication tempfailed.
18867c478bd9Sstevel@tonic-gate */
18877c478bd9Sstevel@tonic-gate 
18887c478bd9Sstevel@tonic-gate int
smtpauth(m,mci,e)18897c478bd9Sstevel@tonic-gate smtpauth(m, mci, e)
18907c478bd9Sstevel@tonic-gate 	MAILER *m;
18917c478bd9Sstevel@tonic-gate 	MCI *mci;
18927c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
18937c478bd9Sstevel@tonic-gate {
18947c478bd9Sstevel@tonic-gate 	int result;
18957c478bd9Sstevel@tonic-gate 	int i;
18967c478bd9Sstevel@tonic-gate 	bool usedgetauth;
18977c478bd9Sstevel@tonic-gate 
18987c478bd9Sstevel@tonic-gate 	mci->mci_sasl_auth = false;
18997c478bd9Sstevel@tonic-gate 	for (i = 0; i < SASL_MECH ; i++)
19007c478bd9Sstevel@tonic-gate 		mci->mci_sai[i] = NULL;
19017c478bd9Sstevel@tonic-gate 
19027c478bd9Sstevel@tonic-gate 	result = getauth(mci, e, &(mci->mci_sai));
19037c478bd9Sstevel@tonic-gate 	if (result == EX_TEMPFAIL)
19047c478bd9Sstevel@tonic-gate 		return result;
19057c478bd9Sstevel@tonic-gate 	usedgetauth = true;
19067c478bd9Sstevel@tonic-gate 
19077c478bd9Sstevel@tonic-gate 	/* no data available: don't try to authenticate */
19087c478bd9Sstevel@tonic-gate 	if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL)
19097c478bd9Sstevel@tonic-gate 		return result;
19107c478bd9Sstevel@tonic-gate 	if (result != EX_OK)
19117c478bd9Sstevel@tonic-gate 	{
19127c478bd9Sstevel@tonic-gate 		if (SASLInfo == NULL)
19137c478bd9Sstevel@tonic-gate 			return EX_UNAVAILABLE;
19147c478bd9Sstevel@tonic-gate 
19157c478bd9Sstevel@tonic-gate 		/* read authinfo from file */
19167c478bd9Sstevel@tonic-gate 		result = readauth(SASLInfo, true, &(mci->mci_sai),
19177c478bd9Sstevel@tonic-gate 				  mci->mci_rpool);
19187c478bd9Sstevel@tonic-gate 		if (result != EX_OK)
19197c478bd9Sstevel@tonic-gate 			return result;
19207c478bd9Sstevel@tonic-gate 		usedgetauth = false;
19217c478bd9Sstevel@tonic-gate 	}
19227c478bd9Sstevel@tonic-gate 
19237c478bd9Sstevel@tonic-gate 	/* check whether sufficient data is available */
19247c478bd9Sstevel@tonic-gate 	if (mci->mci_sai[SASL_PASSWORD] == NULL ||
19257c478bd9Sstevel@tonic-gate 	    *(mci->mci_sai)[SASL_PASSWORD] == '\0')
19267c478bd9Sstevel@tonic-gate 		return EX_UNAVAILABLE;
19277c478bd9Sstevel@tonic-gate 	if ((mci->mci_sai[SASL_AUTHID] == NULL ||
19287c478bd9Sstevel@tonic-gate 	     *(mci->mci_sai)[SASL_AUTHID] == '\0') &&
19297c478bd9Sstevel@tonic-gate 	    (mci->mci_sai[SASL_USER] == NULL ||
19307c478bd9Sstevel@tonic-gate 	     *(mci->mci_sai)[SASL_USER] == '\0'))
19317c478bd9Sstevel@tonic-gate 		return EX_UNAVAILABLE;
19327c478bd9Sstevel@tonic-gate 
19337c478bd9Sstevel@tonic-gate 	/* set the context for the callback function to sai */
19347c478bd9Sstevel@tonic-gate # if SASL >= 20000
19357c478bd9Sstevel@tonic-gate 	callbacks[CB_PASS_IDX].context = (void *) mci;
19367c478bd9Sstevel@tonic-gate # else /* SASL >= 20000 */
19377c478bd9Sstevel@tonic-gate 	callbacks[CB_PASS_IDX].context = (void *) &mci->mci_sai;
19387c478bd9Sstevel@tonic-gate # endif /* SASL >= 20000 */
19397c478bd9Sstevel@tonic-gate 	callbacks[CB_USER_IDX].context = (void *) &mci->mci_sai;
19407c478bd9Sstevel@tonic-gate 	callbacks[CB_AUTHNAME_IDX].context = (void *) &mci->mci_sai;
19417c478bd9Sstevel@tonic-gate 	callbacks[CB_GETREALM_IDX].context = (void *) &mci->mci_sai;
19427c478bd9Sstevel@tonic-gate #if 0
19437c478bd9Sstevel@tonic-gate 	callbacks[CB_SAFESASL_IDX].context = (void *) &mci->mci_sai;
19447c478bd9Sstevel@tonic-gate #endif /* 0 */
19457c478bd9Sstevel@tonic-gate 
19467c478bd9Sstevel@tonic-gate 	/* set default value for realm */
19477c478bd9Sstevel@tonic-gate 	if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
19487c478bd9Sstevel@tonic-gate 		(mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool,
19497c478bd9Sstevel@tonic-gate 							macvalue('j', CurEnv));
19507c478bd9Sstevel@tonic-gate 
19517c478bd9Sstevel@tonic-gate 	/* set default value for list of mechanism to use */
19527c478bd9Sstevel@tonic-gate 	if ((mci->mci_sai)[SASL_MECHLIST] == NULL ||
19537c478bd9Sstevel@tonic-gate 	    *(mci->mci_sai)[SASL_MECHLIST] == '\0')
19547c478bd9Sstevel@tonic-gate 		(mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms;
19557c478bd9Sstevel@tonic-gate 
19567c478bd9Sstevel@tonic-gate 	/* create list of mechanisms to try */
19577c478bd9Sstevel@tonic-gate 	mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST],
19587c478bd9Sstevel@tonic-gate 				     mci->mci_saslcap, mci->mci_rpool);
19597c478bd9Sstevel@tonic-gate 
19607c478bd9Sstevel@tonic-gate 	/* initialize sasl client library */
19617c478bd9Sstevel@tonic-gate 	result = init_sasl_client();
19627c478bd9Sstevel@tonic-gate 	if (result != SASL_OK)
19637c478bd9Sstevel@tonic-gate 		return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE;
19647c478bd9Sstevel@tonic-gate 	do
19657c478bd9Sstevel@tonic-gate 	{
19667c478bd9Sstevel@tonic-gate 		result = attemptauth(m, mci, e, &(mci->mci_sai));
19677c478bd9Sstevel@tonic-gate 		if (result == EX_OK)
19687c478bd9Sstevel@tonic-gate 			mci->mci_sasl_auth = true;
19697c478bd9Sstevel@tonic-gate 		else if (result == EX_TEMPFAIL || result == EX_NOPERM)
19707c478bd9Sstevel@tonic-gate 		{
19717c478bd9Sstevel@tonic-gate 			mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH],
19727c478bd9Sstevel@tonic-gate 						      mci->mci_saslcap,
19737c478bd9Sstevel@tonic-gate 						      mci->mci_rpool);
19747c478bd9Sstevel@tonic-gate 			if (mci->mci_saslcap == NULL ||
19757c478bd9Sstevel@tonic-gate 			    *(mci->mci_saslcap) == '\0')
19767c478bd9Sstevel@tonic-gate 				return usedgetauth ? result
19777c478bd9Sstevel@tonic-gate 						   : EX_UNAVAILABLE;
19787c478bd9Sstevel@tonic-gate 		}
19797c478bd9Sstevel@tonic-gate 		else
19807c478bd9Sstevel@tonic-gate 			return result;
19817c478bd9Sstevel@tonic-gate 	} while (result != EX_OK);
19827c478bd9Sstevel@tonic-gate 	return result;
19837c478bd9Sstevel@tonic-gate }
19847c478bd9Sstevel@tonic-gate #endif /* SASL */
19857c478bd9Sstevel@tonic-gate 
19867c478bd9Sstevel@tonic-gate /*
19877c478bd9Sstevel@tonic-gate **  SMTPMAILFROM -- send MAIL command
19887c478bd9Sstevel@tonic-gate **
19897c478bd9Sstevel@tonic-gate **	Parameters:
19907c478bd9Sstevel@tonic-gate **		m -- the mailer.
19917c478bd9Sstevel@tonic-gate **		mci -- the mailer connection structure.
19927c478bd9Sstevel@tonic-gate **		e -- the envelope (including the sender to specify).
19937c478bd9Sstevel@tonic-gate */
19947c478bd9Sstevel@tonic-gate 
19957c478bd9Sstevel@tonic-gate int
smtpmailfrom(m,mci,e)19967c478bd9Sstevel@tonic-gate smtpmailfrom(m, mci, e)
19977c478bd9Sstevel@tonic-gate 	MAILER *m;
19987c478bd9Sstevel@tonic-gate 	MCI *mci;
19997c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
20007c478bd9Sstevel@tonic-gate {
20017c478bd9Sstevel@tonic-gate 	int r;
20027c478bd9Sstevel@tonic-gate 	char *bufp;
20037c478bd9Sstevel@tonic-gate 	char *bodytype;
20047c478bd9Sstevel@tonic-gate 	char *enhsc;
20057c478bd9Sstevel@tonic-gate 	char buf[MAXNAME + 1];
20067c478bd9Sstevel@tonic-gate 	char optbuf[MAXLINE];
20077c478bd9Sstevel@tonic-gate 
20087c478bd9Sstevel@tonic-gate 	if (tTd(18, 2))
20097c478bd9Sstevel@tonic-gate 		sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
20107c478bd9Sstevel@tonic-gate 	enhsc = NULL;
20117c478bd9Sstevel@tonic-gate 
20127c478bd9Sstevel@tonic-gate 	/*
20137c478bd9Sstevel@tonic-gate 	**  Check if connection is gone, if so
20147c478bd9Sstevel@tonic-gate 	**  it's a tempfail and we use mci_errno
20157c478bd9Sstevel@tonic-gate 	**  for the reason.
20167c478bd9Sstevel@tonic-gate 	*/
20177c478bd9Sstevel@tonic-gate 
20187c478bd9Sstevel@tonic-gate 	if (mci->mci_state == MCIS_CLOSED)
20197c478bd9Sstevel@tonic-gate 	{
20207c478bd9Sstevel@tonic-gate 		errno = mci->mci_errno;
20217c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
20227c478bd9Sstevel@tonic-gate 	}
20237c478bd9Sstevel@tonic-gate 
20247c478bd9Sstevel@tonic-gate 	/* set up appropriate options to include */
20257c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
20267c478bd9Sstevel@tonic-gate 	{
2027058561cbSjbeck 		(void) sm_snprintf(optbuf, sizeof(optbuf), " SIZE=%ld",
20287c478bd9Sstevel@tonic-gate 			e->e_msgsize);
20297c478bd9Sstevel@tonic-gate 		bufp = &optbuf[strlen(optbuf)];
20307c478bd9Sstevel@tonic-gate 	}
20317c478bd9Sstevel@tonic-gate 	else
20327c478bd9Sstevel@tonic-gate 	{
20337c478bd9Sstevel@tonic-gate 		optbuf[0] = '\0';
20347c478bd9Sstevel@tonic-gate 		bufp = optbuf;
20357c478bd9Sstevel@tonic-gate 	}
20367c478bd9Sstevel@tonic-gate 
20377c478bd9Sstevel@tonic-gate 	bodytype = e->e_bodytype;
20387c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_8BITMIME, mci->mci_flags))
20397c478bd9Sstevel@tonic-gate 	{
20407c478bd9Sstevel@tonic-gate 		if (bodytype == NULL &&
20417c478bd9Sstevel@tonic-gate 		    bitset(MM_MIME8BIT, MimeMode) &&
20427c478bd9Sstevel@tonic-gate 		    bitset(EF_HAS8BIT, e->e_flags) &&
20437c478bd9Sstevel@tonic-gate 		    !bitset(EF_DONT_MIME, e->e_flags) &&
20447c478bd9Sstevel@tonic-gate 		    !bitnset(M_8BITS, m->m_flags))
20457c478bd9Sstevel@tonic-gate 			bodytype = "8BITMIME";
20467c478bd9Sstevel@tonic-gate 		if (bodytype != NULL &&
20477c478bd9Sstevel@tonic-gate 		    SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
20487c478bd9Sstevel@tonic-gate 		{
20497c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
20507c478bd9Sstevel@tonic-gate 				 " BODY=%s", bodytype);
20517c478bd9Sstevel@tonic-gate 			bufp += strlen(bufp);
20527c478bd9Sstevel@tonic-gate 		}
20537c478bd9Sstevel@tonic-gate 	}
20547c478bd9Sstevel@tonic-gate 	else if (bitnset(M_8BITS, m->m_flags) ||
20557c478bd9Sstevel@tonic-gate 		 !bitset(EF_HAS8BIT, e->e_flags) ||
20567c478bd9Sstevel@tonic-gate 		 bitset(MCIF_8BITOK, mci->mci_flags))
20577c478bd9Sstevel@tonic-gate 	{
20587c478bd9Sstevel@tonic-gate 		/* EMPTY */
20597c478bd9Sstevel@tonic-gate 		/* just pass it through */
20607c478bd9Sstevel@tonic-gate 	}
20617c478bd9Sstevel@tonic-gate #if MIME8TO7
20627c478bd9Sstevel@tonic-gate 	else if (bitset(MM_CVTMIME, MimeMode) &&
20637c478bd9Sstevel@tonic-gate 		 !bitset(EF_DONT_MIME, e->e_flags) &&
20647c478bd9Sstevel@tonic-gate 		 (!bitset(MM_PASS8BIT, MimeMode) ||
20657c478bd9Sstevel@tonic-gate 		  bitset(EF_IS_MIME, e->e_flags)))
20667c478bd9Sstevel@tonic-gate 	{
20677c478bd9Sstevel@tonic-gate 		/* must convert from 8bit MIME format to 7bit encoded */
20687c478bd9Sstevel@tonic-gate 		mci->mci_flags |= MCIF_CVT8TO7;
20697c478bd9Sstevel@tonic-gate 	}
20707c478bd9Sstevel@tonic-gate #endif /* MIME8TO7 */
20717c478bd9Sstevel@tonic-gate 	else if (!bitset(MM_PASS8BIT, MimeMode))
20727c478bd9Sstevel@tonic-gate 	{
20737c478bd9Sstevel@tonic-gate 		/* cannot just send a 8-bit version */
20747c478bd9Sstevel@tonic-gate 		extern char MsgBuf[];
20757c478bd9Sstevel@tonic-gate 
20767c478bd9Sstevel@tonic-gate 		usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
20777c478bd9Sstevel@tonic-gate 		mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
20787c478bd9Sstevel@tonic-gate 		return EX_DATAERR;
20797c478bd9Sstevel@tonic-gate 	}
20807c478bd9Sstevel@tonic-gate 
20817c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_DSN, mci->mci_flags))
20827c478bd9Sstevel@tonic-gate 	{
20837c478bd9Sstevel@tonic-gate 		if (e->e_envid != NULL &&
20847c478bd9Sstevel@tonic-gate 		    SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
20857c478bd9Sstevel@tonic-gate 		{
20867c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
20877c478bd9Sstevel@tonic-gate 				 " ENVID=%s", e->e_envid);
20887c478bd9Sstevel@tonic-gate 			bufp += strlen(bufp);
20897c478bd9Sstevel@tonic-gate 		}
20907c478bd9Sstevel@tonic-gate 
20917c478bd9Sstevel@tonic-gate 		/* RET= parameter */
20927c478bd9Sstevel@tonic-gate 		if (bitset(EF_RET_PARAM, e->e_flags) &&
20937c478bd9Sstevel@tonic-gate 		    SPACELEFT(optbuf, bufp) > 9)
20947c478bd9Sstevel@tonic-gate 		{
20957c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
20967c478bd9Sstevel@tonic-gate 				 " RET=%s",
20977c478bd9Sstevel@tonic-gate 				 bitset(EF_NO_BODY_RETN, e->e_flags) ?
20987c478bd9Sstevel@tonic-gate 					"HDRS" : "FULL");
20997c478bd9Sstevel@tonic-gate 			bufp += strlen(bufp);
21007c478bd9Sstevel@tonic-gate 		}
21017c478bd9Sstevel@tonic-gate 	}
21027c478bd9Sstevel@tonic-gate 
21037c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
21047c478bd9Sstevel@tonic-gate 	    SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
21057c478bd9Sstevel@tonic-gate #if SASL
21067c478bd9Sstevel@tonic-gate 	     && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
21077c478bd9Sstevel@tonic-gate #endif /* SASL */
21087c478bd9Sstevel@tonic-gate 	    )
21097c478bd9Sstevel@tonic-gate 	{
21107c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
21117c478bd9Sstevel@tonic-gate 			 " AUTH=%s", e->e_auth_param);
21127c478bd9Sstevel@tonic-gate 		bufp += strlen(bufp);
21137c478bd9Sstevel@tonic-gate 	}
21147c478bd9Sstevel@tonic-gate 
21157c478bd9Sstevel@tonic-gate 	/*
21167c478bd9Sstevel@tonic-gate 	**  17 is the max length required, we could use log() to compute
21177c478bd9Sstevel@tonic-gate 	**  the exact length (and check IS_DLVR_TRACE())
21187c478bd9Sstevel@tonic-gate 	*/
21197c478bd9Sstevel@tonic-gate 
21207c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_DLVR_BY, mci->mci_flags) &&
21217c478bd9Sstevel@tonic-gate 	    IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17)
21227c478bd9Sstevel@tonic-gate 	{
21237c478bd9Sstevel@tonic-gate 		long dby;
21247c478bd9Sstevel@tonic-gate 
21257c478bd9Sstevel@tonic-gate 		/*
21267c478bd9Sstevel@tonic-gate 		**  Avoid problems with delays (for R) since the check
21277c478bd9Sstevel@tonic-gate 		**  in deliver() whether min-deliver-time is sufficient.
21287c478bd9Sstevel@tonic-gate 		**  Alternatively we could pass the computed time to this
21297c478bd9Sstevel@tonic-gate 		**  function.
21307c478bd9Sstevel@tonic-gate 		*/
21317c478bd9Sstevel@tonic-gate 
21327c478bd9Sstevel@tonic-gate 		dby = e->e_deliver_by - (curtime() - e->e_ctime);
21337c478bd9Sstevel@tonic-gate 		if (dby <= 0 && IS_DLVR_RETURN(e))
21347c478bd9Sstevel@tonic-gate 			dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by;
21357c478bd9Sstevel@tonic-gate 		(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
21367c478bd9Sstevel@tonic-gate 			" BY=%ld;%c%s",
21377c478bd9Sstevel@tonic-gate 			dby,
21387c478bd9Sstevel@tonic-gate 			IS_DLVR_RETURN(e) ? 'R' : 'N',
21397c478bd9Sstevel@tonic-gate 			IS_DLVR_TRACE(e) ? "T" : "");
21407c478bd9Sstevel@tonic-gate 		bufp += strlen(bufp);
21417c478bd9Sstevel@tonic-gate 	}
21427c478bd9Sstevel@tonic-gate 
21437c478bd9Sstevel@tonic-gate 	/*
21447c478bd9Sstevel@tonic-gate 	**  Send the MAIL command.
21457c478bd9Sstevel@tonic-gate 	**	Designates the sender.
21467c478bd9Sstevel@tonic-gate 	*/
21477c478bd9Sstevel@tonic-gate 
21487c478bd9Sstevel@tonic-gate 	mci->mci_state = MCIS_MAIL;
21497c478bd9Sstevel@tonic-gate 
21507c478bd9Sstevel@tonic-gate 	if (bitset(EF_RESPONSE, e->e_flags) &&
21517c478bd9Sstevel@tonic-gate 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
21527c478bd9Sstevel@tonic-gate 		buf[0] = '\0';
21537c478bd9Sstevel@tonic-gate 	else
2154058561cbSjbeck 		expand("\201g", buf, sizeof(buf), e);
21557c478bd9Sstevel@tonic-gate 	if (buf[0] == '<')
21567c478bd9Sstevel@tonic-gate 	{
21577c478bd9Sstevel@tonic-gate 		/* strip off <angle brackets> (put back on below) */
21587c478bd9Sstevel@tonic-gate 		bufp = &buf[strlen(buf) - 1];
21597c478bd9Sstevel@tonic-gate 		if (*bufp == '>')
21607c478bd9Sstevel@tonic-gate 			*bufp = '\0';
21617c478bd9Sstevel@tonic-gate 		bufp = &buf[1];
21627c478bd9Sstevel@tonic-gate 	}
21637c478bd9Sstevel@tonic-gate 	else
21647c478bd9Sstevel@tonic-gate 		bufp = buf;
21657c478bd9Sstevel@tonic-gate 	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
21667c478bd9Sstevel@tonic-gate 	    !bitnset(M_FROMPATH, m->m_flags))
21677c478bd9Sstevel@tonic-gate 	{
21687c478bd9Sstevel@tonic-gate 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
21697c478bd9Sstevel@tonic-gate 	}
21707c478bd9Sstevel@tonic-gate 	else
21717c478bd9Sstevel@tonic-gate 	{
21727c478bd9Sstevel@tonic-gate 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
21737c478bd9Sstevel@tonic-gate 			    *bufp == '@' ? ',' : ':', bufp, optbuf);
21747c478bd9Sstevel@tonic-gate 	}
21757c478bd9Sstevel@tonic-gate 	SmtpPhase = mci->mci_phase = "client MAIL";
21767c478bd9Sstevel@tonic-gate 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
21777c478bd9Sstevel@tonic-gate 			CurHostName, mci->mci_phase);
21787c478bd9Sstevel@tonic-gate 	r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc, XS_DEFAULT);
21797c478bd9Sstevel@tonic-gate 	if (r < 0)
21807c478bd9Sstevel@tonic-gate 	{
21817c478bd9Sstevel@tonic-gate 		/* communications failure */
21827c478bd9Sstevel@tonic-gate 		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
21837c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
21847c478bd9Sstevel@tonic-gate 	}
21857c478bd9Sstevel@tonic-gate 	else if (r == SMTPCLOSING)
21867c478bd9Sstevel@tonic-gate 	{
21877c478bd9Sstevel@tonic-gate 		/* service shutting down: handled by reply() */
21887c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
21897c478bd9Sstevel@tonic-gate 	}
21907c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 4)
21917c478bd9Sstevel@tonic-gate 	{
21927c478bd9Sstevel@tonic-gate 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
21937c478bd9Sstevel@tonic-gate 			    SmtpReplyBuffer);
21947c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
21957c478bd9Sstevel@tonic-gate 	}
21967c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 2)
21977c478bd9Sstevel@tonic-gate 	{
21987c478bd9Sstevel@tonic-gate 		return EX_OK;
21997c478bd9Sstevel@tonic-gate 	}
22007c478bd9Sstevel@tonic-gate 	else if (r == 501)
22017c478bd9Sstevel@tonic-gate 	{
22027c478bd9Sstevel@tonic-gate 		/* syntax error in arguments */
22037c478bd9Sstevel@tonic-gate 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
22047c478bd9Sstevel@tonic-gate 			    SmtpReplyBuffer);
22057c478bd9Sstevel@tonic-gate 		return EX_DATAERR;
22067c478bd9Sstevel@tonic-gate 	}
22077c478bd9Sstevel@tonic-gate 	else if (r == 553)
22087c478bd9Sstevel@tonic-gate 	{
22097c478bd9Sstevel@tonic-gate 		/* mailbox name not allowed */
22107c478bd9Sstevel@tonic-gate 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
22117c478bd9Sstevel@tonic-gate 			    SmtpReplyBuffer);
22127c478bd9Sstevel@tonic-gate 		return EX_DATAERR;
22137c478bd9Sstevel@tonic-gate 	}
22147c478bd9Sstevel@tonic-gate 	else if (r == 552)
22157c478bd9Sstevel@tonic-gate 	{
22167c478bd9Sstevel@tonic-gate 		/* exceeded storage allocation */
22177c478bd9Sstevel@tonic-gate 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
22187c478bd9Sstevel@tonic-gate 			    SmtpReplyBuffer);
22197c478bd9Sstevel@tonic-gate 		if (bitset(MCIF_SIZE, mci->mci_flags))
22207c478bd9Sstevel@tonic-gate 			e->e_flags |= EF_NO_BODY_RETN;
22217c478bd9Sstevel@tonic-gate 		return EX_UNAVAILABLE;
22227c478bd9Sstevel@tonic-gate 	}
22237c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 5)
22247c478bd9Sstevel@tonic-gate 	{
22257c478bd9Sstevel@tonic-gate 		/* unknown error */
22267c478bd9Sstevel@tonic-gate 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
22277c478bd9Sstevel@tonic-gate 			    SmtpReplyBuffer);
22287c478bd9Sstevel@tonic-gate 		return EX_UNAVAILABLE;
22297c478bd9Sstevel@tonic-gate 	}
22307c478bd9Sstevel@tonic-gate 
22317c478bd9Sstevel@tonic-gate 	if (LogLevel > 1)
22327c478bd9Sstevel@tonic-gate 	{
22337c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_CRIT, e->e_id,
22347c478bd9Sstevel@tonic-gate 			  "%.100s: SMTP MAIL protocol error: %s",
22357c478bd9Sstevel@tonic-gate 			  CurHostName,
22367c478bd9Sstevel@tonic-gate 			  shortenstring(SmtpReplyBuffer, 403));
22377c478bd9Sstevel@tonic-gate 	}
22387c478bd9Sstevel@tonic-gate 
22397c478bd9Sstevel@tonic-gate 	/* protocol error -- close up */
22407c478bd9Sstevel@tonic-gate 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
22417c478bd9Sstevel@tonic-gate 		    SmtpReplyBuffer);
22427c478bd9Sstevel@tonic-gate 	smtpquit(m, mci, e);
22437c478bd9Sstevel@tonic-gate 	return EX_PROTOCOL;
22447c478bd9Sstevel@tonic-gate }
22457c478bd9Sstevel@tonic-gate /*
22467c478bd9Sstevel@tonic-gate **  SMTPRCPT -- designate recipient.
22477c478bd9Sstevel@tonic-gate **
22487c478bd9Sstevel@tonic-gate **	Parameters:
22497c478bd9Sstevel@tonic-gate **		to -- address of recipient.
22507c478bd9Sstevel@tonic-gate **		m -- the mailer we are sending to.
22517c478bd9Sstevel@tonic-gate **		mci -- the connection info for this transaction.
22527c478bd9Sstevel@tonic-gate **		e -- the envelope for this transaction.
22537c478bd9Sstevel@tonic-gate **
22547c478bd9Sstevel@tonic-gate **	Returns:
22557c478bd9Sstevel@tonic-gate **		exit status corresponding to recipient status.
22567c478bd9Sstevel@tonic-gate **
22577c478bd9Sstevel@tonic-gate **	Side Effects:
22587c478bd9Sstevel@tonic-gate **		Sends the mail via SMTP.
22597c478bd9Sstevel@tonic-gate */
22607c478bd9Sstevel@tonic-gate 
22617c478bd9Sstevel@tonic-gate int
smtprcpt(to,m,mci,e,ctladdr,xstart)22627c478bd9Sstevel@tonic-gate smtprcpt(to, m, mci, e, ctladdr, xstart)
22637c478bd9Sstevel@tonic-gate 	ADDRESS *to;
22647c478bd9Sstevel@tonic-gate 	register MAILER *m;
22657c478bd9Sstevel@tonic-gate 	MCI *mci;
22667c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
22677c478bd9Sstevel@tonic-gate 	ADDRESS *ctladdr;
22687c478bd9Sstevel@tonic-gate 	time_t xstart;
22697c478bd9Sstevel@tonic-gate {
22707c478bd9Sstevel@tonic-gate 	char *bufp;
22717c478bd9Sstevel@tonic-gate 	char optbuf[MAXLINE];
22727c478bd9Sstevel@tonic-gate 
22737c478bd9Sstevel@tonic-gate #if PIPELINING
22747c478bd9Sstevel@tonic-gate 	/*
22757c478bd9Sstevel@tonic-gate 	**  If there is status waiting from the other end, read it.
22767c478bd9Sstevel@tonic-gate 	**  This should normally happen because of SMTP pipelining.
22777c478bd9Sstevel@tonic-gate 	*/
22787c478bd9Sstevel@tonic-gate 
22797c478bd9Sstevel@tonic-gate 	while (mci->mci_nextaddr != NULL &&
22807c478bd9Sstevel@tonic-gate 	       sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
22817c478bd9Sstevel@tonic-gate 	{
22827c478bd9Sstevel@tonic-gate 		int r;
22837c478bd9Sstevel@tonic-gate 
22847c478bd9Sstevel@tonic-gate 		r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
22857c478bd9Sstevel@tonic-gate 		if (r != EX_OK)
22867c478bd9Sstevel@tonic-gate 		{
22877c478bd9Sstevel@tonic-gate 			markfailure(e, mci->mci_nextaddr, mci, r, false);
22887c478bd9Sstevel@tonic-gate 			giveresponse(r, mci->mci_nextaddr->q_status,  m, mci,
22897c478bd9Sstevel@tonic-gate 				     ctladdr, xstart, e, to);
22907c478bd9Sstevel@tonic-gate 		}
22917c478bd9Sstevel@tonic-gate 		mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
22927c478bd9Sstevel@tonic-gate 	}
22937c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
22947c478bd9Sstevel@tonic-gate 
22957c478bd9Sstevel@tonic-gate 	/*
22967c478bd9Sstevel@tonic-gate 	**  Check if connection is gone, if so
22977c478bd9Sstevel@tonic-gate 	**  it's a tempfail and we use mci_errno
22987c478bd9Sstevel@tonic-gate 	**  for the reason.
22997c478bd9Sstevel@tonic-gate 	*/
23007c478bd9Sstevel@tonic-gate 
23017c478bd9Sstevel@tonic-gate 	if (mci->mci_state == MCIS_CLOSED)
23027c478bd9Sstevel@tonic-gate 	{
23037c478bd9Sstevel@tonic-gate 		errno = mci->mci_errno;
23047c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
23057c478bd9Sstevel@tonic-gate 	}
23067c478bd9Sstevel@tonic-gate 
23077c478bd9Sstevel@tonic-gate 	optbuf[0] = '\0';
23087c478bd9Sstevel@tonic-gate 	bufp = optbuf;
23097c478bd9Sstevel@tonic-gate 
23107c478bd9Sstevel@tonic-gate 	/*
23117c478bd9Sstevel@tonic-gate 	**  Warning: in the following it is assumed that the free space
2312058561cbSjbeck 	**  in bufp is sizeof(optbuf)
23137c478bd9Sstevel@tonic-gate 	*/
23147c478bd9Sstevel@tonic-gate 
23157c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_DSN, mci->mci_flags))
23167c478bd9Sstevel@tonic-gate 	{
23177c478bd9Sstevel@tonic-gate 		if (IS_DLVR_NOTIFY(e) &&
23187c478bd9Sstevel@tonic-gate 		    !bitset(MCIF_DLVR_BY, mci->mci_flags))
23197c478bd9Sstevel@tonic-gate 		{
23207c478bd9Sstevel@tonic-gate 			/* RFC 2852: 4.1.4.2 */
23217c478bd9Sstevel@tonic-gate 			if (!bitset(QHASNOTIFY, to->q_flags))
23227c478bd9Sstevel@tonic-gate 				to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY;
23237c478bd9Sstevel@tonic-gate 			else if (bitset(QPINGONSUCCESS, to->q_flags) ||
23247c478bd9Sstevel@tonic-gate 				 bitset(QPINGONFAILURE, to->q_flags) ||
23257c478bd9Sstevel@tonic-gate 				 bitset(QPINGONDELAY, to->q_flags))
23267c478bd9Sstevel@tonic-gate 				to->q_flags |= QPINGONDELAY;
23277c478bd9Sstevel@tonic-gate 		}
23287c478bd9Sstevel@tonic-gate 
23297c478bd9Sstevel@tonic-gate 		/* NOTIFY= parameter */
23307c478bd9Sstevel@tonic-gate 		if (bitset(QHASNOTIFY, to->q_flags) &&
23317c478bd9Sstevel@tonic-gate 		    bitset(QPRIMARY, to->q_flags) &&
23327c478bd9Sstevel@tonic-gate 		    !bitnset(M_LOCALMAILER, m->m_flags))
23337c478bd9Sstevel@tonic-gate 		{
23347c478bd9Sstevel@tonic-gate 			bool firstone = true;
23357c478bd9Sstevel@tonic-gate 
2336058561cbSjbeck 			(void) sm_strlcat(bufp, " NOTIFY=", sizeof(optbuf));
23377c478bd9Sstevel@tonic-gate 			if (bitset(QPINGONSUCCESS, to->q_flags))
23387c478bd9Sstevel@tonic-gate 			{
2339058561cbSjbeck 				(void) sm_strlcat(bufp, "SUCCESS", sizeof(optbuf));
23407c478bd9Sstevel@tonic-gate 				firstone = false;
23417c478bd9Sstevel@tonic-gate 			}
23427c478bd9Sstevel@tonic-gate 			if (bitset(QPINGONFAILURE, to->q_flags))
23437c478bd9Sstevel@tonic-gate 			{
23447c478bd9Sstevel@tonic-gate 				if (!firstone)
23457c478bd9Sstevel@tonic-gate 					(void) sm_strlcat(bufp, ",",
2346058561cbSjbeck 						       sizeof(optbuf));
2347058561cbSjbeck 				(void) sm_strlcat(bufp, "FAILURE", sizeof(optbuf));
23487c478bd9Sstevel@tonic-gate 				firstone = false;
23497c478bd9Sstevel@tonic-gate 			}
23507c478bd9Sstevel@tonic-gate 			if (bitset(QPINGONDELAY, to->q_flags))
23517c478bd9Sstevel@tonic-gate 			{
23527c478bd9Sstevel@tonic-gate 				if (!firstone)
23537c478bd9Sstevel@tonic-gate 					(void) sm_strlcat(bufp, ",",
2354058561cbSjbeck 						       sizeof(optbuf));
2355058561cbSjbeck 				(void) sm_strlcat(bufp, "DELAY", sizeof(optbuf));
23567c478bd9Sstevel@tonic-gate 				firstone = false;
23577c478bd9Sstevel@tonic-gate 			}
23587c478bd9Sstevel@tonic-gate 			if (firstone)
2359058561cbSjbeck 				(void) sm_strlcat(bufp, "NEVER", sizeof(optbuf));
23607c478bd9Sstevel@tonic-gate 			bufp += strlen(bufp);
23617c478bd9Sstevel@tonic-gate 		}
23627c478bd9Sstevel@tonic-gate 
23637c478bd9Sstevel@tonic-gate 		/* ORCPT= parameter */
23647c478bd9Sstevel@tonic-gate 		if (to->q_orcpt != NULL &&
23657c478bd9Sstevel@tonic-gate 		    SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
23667c478bd9Sstevel@tonic-gate 		{
23677c478bd9Sstevel@tonic-gate 			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
23687c478bd9Sstevel@tonic-gate 				 " ORCPT=%s", to->q_orcpt);
23697c478bd9Sstevel@tonic-gate 			bufp += strlen(bufp);
23707c478bd9Sstevel@tonic-gate 		}
23717c478bd9Sstevel@tonic-gate 	}
23727c478bd9Sstevel@tonic-gate 
23737c478bd9Sstevel@tonic-gate 	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
23747c478bd9Sstevel@tonic-gate 	mci->mci_state = MCIS_RCPT;
23757c478bd9Sstevel@tonic-gate 
23767c478bd9Sstevel@tonic-gate 	SmtpPhase = mci->mci_phase = "client RCPT";
23777c478bd9Sstevel@tonic-gate 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
23787c478bd9Sstevel@tonic-gate 			CurHostName, mci->mci_phase);
23797c478bd9Sstevel@tonic-gate 
23807c478bd9Sstevel@tonic-gate #if PIPELINING
23817c478bd9Sstevel@tonic-gate 	/*
23827c478bd9Sstevel@tonic-gate 	**  If running SMTP pipelining, we will pick up status later
23837c478bd9Sstevel@tonic-gate 	*/
23847c478bd9Sstevel@tonic-gate 
23857c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_PIPELINED, mci->mci_flags))
23867c478bd9Sstevel@tonic-gate 		return EX_OK;
23877c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
23887c478bd9Sstevel@tonic-gate 
23897c478bd9Sstevel@tonic-gate 	return smtprcptstat(to, m, mci, e);
23907c478bd9Sstevel@tonic-gate }
23917c478bd9Sstevel@tonic-gate /*
23927c478bd9Sstevel@tonic-gate **  SMTPRCPTSTAT -- get recipient status
23937c478bd9Sstevel@tonic-gate **
23947c478bd9Sstevel@tonic-gate **	This is only called during SMTP pipelining
23957c478bd9Sstevel@tonic-gate **
23967c478bd9Sstevel@tonic-gate **	Parameters:
23977c478bd9Sstevel@tonic-gate **		to -- address of recipient.
23987c478bd9Sstevel@tonic-gate **		m -- mailer being sent to.
23997c478bd9Sstevel@tonic-gate **		mci -- the mailer connection information.
24007c478bd9Sstevel@tonic-gate **		e -- the envelope for this message.
24017c478bd9Sstevel@tonic-gate **
24027c478bd9Sstevel@tonic-gate **	Returns:
24037c478bd9Sstevel@tonic-gate **		EX_* -- protocol status
24047c478bd9Sstevel@tonic-gate */
24057c478bd9Sstevel@tonic-gate 
24067c478bd9Sstevel@tonic-gate static int
smtprcptstat(to,m,mci,e)24077c478bd9Sstevel@tonic-gate smtprcptstat(to, m, mci, e)
24087c478bd9Sstevel@tonic-gate 	ADDRESS *to;
24097c478bd9Sstevel@tonic-gate 	MAILER *m;
24107c478bd9Sstevel@tonic-gate 	register MCI *mci;
24117c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
24127c478bd9Sstevel@tonic-gate {
24137c478bd9Sstevel@tonic-gate 	int r;
24147c478bd9Sstevel@tonic-gate 	int save_errno;
24157c478bd9Sstevel@tonic-gate 	char *enhsc;
24167c478bd9Sstevel@tonic-gate 
24177c478bd9Sstevel@tonic-gate 	/*
24187c478bd9Sstevel@tonic-gate 	**  Check if connection is gone, if so
24197c478bd9Sstevel@tonic-gate 	**  it's a tempfail and we use mci_errno
24207c478bd9Sstevel@tonic-gate 	**  for the reason.
24217c478bd9Sstevel@tonic-gate 	*/
24227c478bd9Sstevel@tonic-gate 
24237c478bd9Sstevel@tonic-gate 	if (mci->mci_state == MCIS_CLOSED)
24247c478bd9Sstevel@tonic-gate 	{
24257c478bd9Sstevel@tonic-gate 		errno = mci->mci_errno;
24267c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
24277c478bd9Sstevel@tonic-gate 	}
24287c478bd9Sstevel@tonic-gate 
24297c478bd9Sstevel@tonic-gate 	enhsc = NULL;
24307c478bd9Sstevel@tonic-gate 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc, XS_DEFAULT);
24317c478bd9Sstevel@tonic-gate 	save_errno = errno;
24327c478bd9Sstevel@tonic-gate 	to->q_rstatus = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer);
24337c478bd9Sstevel@tonic-gate 	to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool);
24347c478bd9Sstevel@tonic-gate 	if (!bitnset(M_LMTP, m->m_flags))
24357c478bd9Sstevel@tonic-gate 		to->q_statmta = mci->mci_host;
24367c478bd9Sstevel@tonic-gate 	if (r < 0 || REPLYTYPE(r) == 4)
24377c478bd9Sstevel@tonic-gate 	{
24387c478bd9Sstevel@tonic-gate 		mci->mci_retryrcpt = true;
24397c478bd9Sstevel@tonic-gate 		errno = save_errno;
24407c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
24417c478bd9Sstevel@tonic-gate 	}
24427c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 2)
24437c478bd9Sstevel@tonic-gate 	{
24447c478bd9Sstevel@tonic-gate 		char *t;
24457c478bd9Sstevel@tonic-gate 
24467c478bd9Sstevel@tonic-gate 		if ((t = mci->mci_tolist) != NULL)
24477c478bd9Sstevel@tonic-gate 		{
24487c478bd9Sstevel@tonic-gate 			char *p;
24497c478bd9Sstevel@tonic-gate 
24507c478bd9Sstevel@tonic-gate 			*t++ = ',';
24517c478bd9Sstevel@tonic-gate 			for (p = to->q_paddr; *p != '\0'; *t++ = *p++)
24527c478bd9Sstevel@tonic-gate 				continue;
24537c478bd9Sstevel@tonic-gate 			*t = '\0';
24547c478bd9Sstevel@tonic-gate 			mci->mci_tolist = t;
24557c478bd9Sstevel@tonic-gate 		}
24567c478bd9Sstevel@tonic-gate #if PIPELINING
24577c478bd9Sstevel@tonic-gate 		mci->mci_okrcpts++;
24587c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
24597c478bd9Sstevel@tonic-gate 		return EX_OK;
24607c478bd9Sstevel@tonic-gate 	}
24617c478bd9Sstevel@tonic-gate 	else if (r == 550)
24627c478bd9Sstevel@tonic-gate 	{
24637c478bd9Sstevel@tonic-gate 		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool);
24647c478bd9Sstevel@tonic-gate 		return EX_NOUSER;
24657c478bd9Sstevel@tonic-gate 	}
24667c478bd9Sstevel@tonic-gate 	else if (r == 551)
24677c478bd9Sstevel@tonic-gate 	{
24687c478bd9Sstevel@tonic-gate 		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool);
24697c478bd9Sstevel@tonic-gate 		return EX_NOUSER;
24707c478bd9Sstevel@tonic-gate 	}
24717c478bd9Sstevel@tonic-gate 	else if (r == 553)
24727c478bd9Sstevel@tonic-gate 	{
24737c478bd9Sstevel@tonic-gate 		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool);
24747c478bd9Sstevel@tonic-gate 		return EX_NOUSER;
24757c478bd9Sstevel@tonic-gate 	}
24767c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 5)
24777c478bd9Sstevel@tonic-gate 	{
24787c478bd9Sstevel@tonic-gate 		return EX_UNAVAILABLE;
24797c478bd9Sstevel@tonic-gate 	}
24807c478bd9Sstevel@tonic-gate 
24817c478bd9Sstevel@tonic-gate 	if (LogLevel > 1)
24827c478bd9Sstevel@tonic-gate 	{
24837c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_CRIT, e->e_id,
24847c478bd9Sstevel@tonic-gate 			  "%.100s: SMTP RCPT protocol error: %s",
24857c478bd9Sstevel@tonic-gate 			  CurHostName,
24867c478bd9Sstevel@tonic-gate 			  shortenstring(SmtpReplyBuffer, 403));
24877c478bd9Sstevel@tonic-gate 	}
24887c478bd9Sstevel@tonic-gate 
24897c478bd9Sstevel@tonic-gate 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
24907c478bd9Sstevel@tonic-gate 		    SmtpReplyBuffer);
24917c478bd9Sstevel@tonic-gate 	return EX_PROTOCOL;
24927c478bd9Sstevel@tonic-gate }
24937c478bd9Sstevel@tonic-gate /*
24947c478bd9Sstevel@tonic-gate **  SMTPDATA -- send the data and clean up the transaction.
24957c478bd9Sstevel@tonic-gate **
24967c478bd9Sstevel@tonic-gate **	Parameters:
24977c478bd9Sstevel@tonic-gate **		m -- mailer being sent to.
24987c478bd9Sstevel@tonic-gate **		mci -- the mailer connection information.
24997c478bd9Sstevel@tonic-gate **		e -- the envelope for this message.
25007c478bd9Sstevel@tonic-gate **
25017c478bd9Sstevel@tonic-gate **	Returns:
25027c478bd9Sstevel@tonic-gate **		exit status corresponding to DATA command.
25037c478bd9Sstevel@tonic-gate */
25047c478bd9Sstevel@tonic-gate 
25057c478bd9Sstevel@tonic-gate int
smtpdata(m,mci,e,ctladdr,xstart)25067c478bd9Sstevel@tonic-gate smtpdata(m, mci, e, ctladdr, xstart)
25077c478bd9Sstevel@tonic-gate 	MAILER *m;
25087c478bd9Sstevel@tonic-gate 	register MCI *mci;
25097c478bd9Sstevel@tonic-gate 	register ENVELOPE *e;
25107c478bd9Sstevel@tonic-gate 	ADDRESS *ctladdr;
25117c478bd9Sstevel@tonic-gate 	time_t xstart;
25127c478bd9Sstevel@tonic-gate {
25137c478bd9Sstevel@tonic-gate 	register int r;
25147c478bd9Sstevel@tonic-gate 	int rstat;
25157c478bd9Sstevel@tonic-gate 	int xstat;
2516445f2479Sjbeck 	int timeout;
25177c478bd9Sstevel@tonic-gate 	char *enhsc;
25187c478bd9Sstevel@tonic-gate 
25197c478bd9Sstevel@tonic-gate 	/*
25207c478bd9Sstevel@tonic-gate 	**  Check if connection is gone, if so
25217c478bd9Sstevel@tonic-gate 	**  it's a tempfail and we use mci_errno
25227c478bd9Sstevel@tonic-gate 	**  for the reason.
25237c478bd9Sstevel@tonic-gate 	*/
25247c478bd9Sstevel@tonic-gate 
25257c478bd9Sstevel@tonic-gate 	if (mci->mci_state == MCIS_CLOSED)
25267c478bd9Sstevel@tonic-gate 	{
25277c478bd9Sstevel@tonic-gate 		errno = mci->mci_errno;
25287c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
25297c478bd9Sstevel@tonic-gate 	}
25307c478bd9Sstevel@tonic-gate 
25317c478bd9Sstevel@tonic-gate 	enhsc = NULL;
25327c478bd9Sstevel@tonic-gate 
25337c478bd9Sstevel@tonic-gate 	/*
25347c478bd9Sstevel@tonic-gate 	**  Send the data.
25357c478bd9Sstevel@tonic-gate 	**	First send the command and check that it is ok.
25367c478bd9Sstevel@tonic-gate 	**	Then send the data (if there are valid recipients).
25377c478bd9Sstevel@tonic-gate 	**	Follow it up with a dot to terminate.
25387c478bd9Sstevel@tonic-gate 	**	Finally get the results of the transaction.
25397c478bd9Sstevel@tonic-gate 	*/
25407c478bd9Sstevel@tonic-gate 
25417c478bd9Sstevel@tonic-gate 	/* send the command and check ok to proceed */
25427c478bd9Sstevel@tonic-gate 	smtpmessage("DATA", m, mci);
25437c478bd9Sstevel@tonic-gate 
25447c478bd9Sstevel@tonic-gate #if PIPELINING
25457c478bd9Sstevel@tonic-gate 	if (mci->mci_nextaddr != NULL)
25467c478bd9Sstevel@tonic-gate 	{
25477c478bd9Sstevel@tonic-gate 		char *oldto = e->e_to;
25487c478bd9Sstevel@tonic-gate 
25497c478bd9Sstevel@tonic-gate 		/* pick up any pending RCPT responses for SMTP pipelining */
25507c478bd9Sstevel@tonic-gate 		while (mci->mci_nextaddr != NULL)
25517c478bd9Sstevel@tonic-gate 		{
25527c478bd9Sstevel@tonic-gate 			int r;
25537c478bd9Sstevel@tonic-gate 
25547c478bd9Sstevel@tonic-gate 			e->e_to = mci->mci_nextaddr->q_paddr;
25557c478bd9Sstevel@tonic-gate 			r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
25567c478bd9Sstevel@tonic-gate 			if (r != EX_OK)
25577c478bd9Sstevel@tonic-gate 			{
25587c478bd9Sstevel@tonic-gate 				markfailure(e, mci->mci_nextaddr, mci, r,
25597c478bd9Sstevel@tonic-gate 					    false);
25607c478bd9Sstevel@tonic-gate 				giveresponse(r, mci->mci_nextaddr->q_status, m,
25617c478bd9Sstevel@tonic-gate 					     mci, ctladdr, xstart, e,
25627c478bd9Sstevel@tonic-gate 					     mci->mci_nextaddr);
25637c478bd9Sstevel@tonic-gate 				if (r == EX_TEMPFAIL)
25647c478bd9Sstevel@tonic-gate 					mci->mci_nextaddr->q_state = QS_RETRY;
25657c478bd9Sstevel@tonic-gate 			}
25667c478bd9Sstevel@tonic-gate 			mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
25677c478bd9Sstevel@tonic-gate 		}
25687c478bd9Sstevel@tonic-gate 		e->e_to = oldto;
25697c478bd9Sstevel@tonic-gate 
25707c478bd9Sstevel@tonic-gate 		/*
25717c478bd9Sstevel@tonic-gate 		**  Connection might be closed in response to a RCPT command,
25727c478bd9Sstevel@tonic-gate 		**  i.e., the server responded with 421. In that case (at
25737c478bd9Sstevel@tonic-gate 		**  least) one RCPT has a temporary failure, hence we don't
25747c478bd9Sstevel@tonic-gate 		**  need to check mci_okrcpts (as it is done below) to figure
25757c478bd9Sstevel@tonic-gate 		**  out which error to return.
25767c478bd9Sstevel@tonic-gate 		*/
25777c478bd9Sstevel@tonic-gate 
25787c478bd9Sstevel@tonic-gate 		if (mci->mci_state == MCIS_CLOSED)
25797c478bd9Sstevel@tonic-gate 		{
25807c478bd9Sstevel@tonic-gate 			errno = mci->mci_errno;
25817c478bd9Sstevel@tonic-gate 			return EX_TEMPFAIL;
25827c478bd9Sstevel@tonic-gate 		}
25837c478bd9Sstevel@tonic-gate 	}
25847c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
25857c478bd9Sstevel@tonic-gate 
25867c478bd9Sstevel@tonic-gate 	/* now proceed with DATA phase */
25877c478bd9Sstevel@tonic-gate 	SmtpPhase = mci->mci_phase = "client DATA 354";
25887c478bd9Sstevel@tonic-gate 	mci->mci_state = MCIS_DATA;
25897c478bd9Sstevel@tonic-gate 	sm_setproctitle(true, e, "%s %s: %s",
25907c478bd9Sstevel@tonic-gate 			qid_printname(e), CurHostName, mci->mci_phase);
25917c478bd9Sstevel@tonic-gate 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc, XS_DEFAULT);
25927c478bd9Sstevel@tonic-gate 	if (r < 0 || REPLYTYPE(r) == 4)
25937c478bd9Sstevel@tonic-gate 	{
25947c478bd9Sstevel@tonic-gate 		if (r >= 0)
25957c478bd9Sstevel@tonic-gate 			smtpquit(m, mci, e);
25967c478bd9Sstevel@tonic-gate 		errno = mci->mci_errno;
25977c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
25987c478bd9Sstevel@tonic-gate 	}
25997c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 5)
26007c478bd9Sstevel@tonic-gate 	{
26017c478bd9Sstevel@tonic-gate 		smtprset(m, mci, e);
26027c478bd9Sstevel@tonic-gate #if PIPELINING
26037c478bd9Sstevel@tonic-gate 		if (mci->mci_okrcpts <= 0)
26047c478bd9Sstevel@tonic-gate 			return mci->mci_retryrcpt ? EX_TEMPFAIL
26057c478bd9Sstevel@tonic-gate 						  : EX_UNAVAILABLE;
26067c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
26077c478bd9Sstevel@tonic-gate 		return EX_UNAVAILABLE;
26087c478bd9Sstevel@tonic-gate 	}
26097c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) != 3)
26107c478bd9Sstevel@tonic-gate 	{
26117c478bd9Sstevel@tonic-gate 		if (LogLevel > 1)
26127c478bd9Sstevel@tonic-gate 		{
26137c478bd9Sstevel@tonic-gate 			sm_syslog(LOG_CRIT, e->e_id,
26147c478bd9Sstevel@tonic-gate 				  "%.100s: SMTP DATA-1 protocol error: %s",
26157c478bd9Sstevel@tonic-gate 				  CurHostName,
26167c478bd9Sstevel@tonic-gate 				  shortenstring(SmtpReplyBuffer, 403));
26177c478bd9Sstevel@tonic-gate 		}
26187c478bd9Sstevel@tonic-gate 		smtprset(m, mci, e);
26197c478bd9Sstevel@tonic-gate 		mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
26207c478bd9Sstevel@tonic-gate 			    SmtpReplyBuffer);
26217c478bd9Sstevel@tonic-gate #if PIPELINING
26227c478bd9Sstevel@tonic-gate 		if (mci->mci_okrcpts <= 0)
26237c478bd9Sstevel@tonic-gate 			return mci->mci_retryrcpt ? EX_TEMPFAIL
26247c478bd9Sstevel@tonic-gate 						  : EX_PROTOCOL;
26257c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
26267c478bd9Sstevel@tonic-gate 		return EX_PROTOCOL;
26277c478bd9Sstevel@tonic-gate 	}
26287c478bd9Sstevel@tonic-gate 
26297c478bd9Sstevel@tonic-gate #if PIPELINING
26307c478bd9Sstevel@tonic-gate 	if (mci->mci_okrcpts > 0)
26317c478bd9Sstevel@tonic-gate 	{
26327c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
26337c478bd9Sstevel@tonic-gate 
26347c478bd9Sstevel@tonic-gate 	/*
26357c478bd9Sstevel@tonic-gate 	**  Set timeout around data writes.  Make it at least large
26367c478bd9Sstevel@tonic-gate 	**  enough for DNS timeouts on all recipients plus some fudge
26377c478bd9Sstevel@tonic-gate 	**  factor.  The main thing is that it should not be infinite.
26387c478bd9Sstevel@tonic-gate 	*/
26397c478bd9Sstevel@tonic-gate 
26407c478bd9Sstevel@tonic-gate 	if (tTd(18, 101))
26417c478bd9Sstevel@tonic-gate 	{
26427c478bd9Sstevel@tonic-gate 		/* simulate a DATA timeout */
2643445f2479Sjbeck 		timeout = 10;
26447c478bd9Sstevel@tonic-gate 	}
26457c478bd9Sstevel@tonic-gate 	else
2646445f2479Sjbeck 		timeout = DATA_PROGRESS_TIMEOUT * 1000;
2647445f2479Sjbeck 	sm_io_setinfo(mci->mci_out, SM_IO_WHAT_TIMEOUT, &timeout);
26487c478bd9Sstevel@tonic-gate 
26497c478bd9Sstevel@tonic-gate 
26507c478bd9Sstevel@tonic-gate 	/*
26517c478bd9Sstevel@tonic-gate 	**  Output the actual message.
26527c478bd9Sstevel@tonic-gate 	*/
26537c478bd9Sstevel@tonic-gate 
2654445f2479Sjbeck 	if (!(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER))
2655445f2479Sjbeck 		goto writeerr;
26567c478bd9Sstevel@tonic-gate 
26577c478bd9Sstevel@tonic-gate 	if (tTd(18, 101))
26587c478bd9Sstevel@tonic-gate 	{
26597c478bd9Sstevel@tonic-gate 		/* simulate a DATA timeout */
26607c478bd9Sstevel@tonic-gate 		(void) sleep(2);
26617c478bd9Sstevel@tonic-gate 	}
26627c478bd9Sstevel@tonic-gate 
2663445f2479Sjbeck 	if (!(*e->e_putbody)(mci, e, NULL))
2664445f2479Sjbeck 		goto writeerr;
26657c478bd9Sstevel@tonic-gate 
26667c478bd9Sstevel@tonic-gate 	/*
26677c478bd9Sstevel@tonic-gate 	**  Cleanup after sending message.
26687c478bd9Sstevel@tonic-gate 	*/
26697c478bd9Sstevel@tonic-gate 
26707c478bd9Sstevel@tonic-gate 
26717c478bd9Sstevel@tonic-gate #if PIPELINING
26727c478bd9Sstevel@tonic-gate 	}
26737c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
26747c478bd9Sstevel@tonic-gate 
26757c478bd9Sstevel@tonic-gate #if _FFR_CATCH_BROKEN_MTAS
26767c478bd9Sstevel@tonic-gate 	if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
26777c478bd9Sstevel@tonic-gate 	{
26787c478bd9Sstevel@tonic-gate 		/* terminate the message */
26797c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s",
26807c478bd9Sstevel@tonic-gate 				     m->m_eol);
26817c478bd9Sstevel@tonic-gate 		if (TrafficLogFile != NULL)
26827c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
26837c478bd9Sstevel@tonic-gate 					     "%05d >>> .\n", (int) CurrentPid);
26847c478bd9Sstevel@tonic-gate 		if (Verbose)
26857c478bd9Sstevel@tonic-gate 			nmessage(">>> .");
26867c478bd9Sstevel@tonic-gate 
26877c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_CRIT, e->e_id,
26887c478bd9Sstevel@tonic-gate 			  "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
26897c478bd9Sstevel@tonic-gate 			  CurHostName);
26907c478bd9Sstevel@tonic-gate 		mci->mci_errno = EIO;
26917c478bd9Sstevel@tonic-gate 		mci->mci_state = MCIS_ERROR;
26927c478bd9Sstevel@tonic-gate 		mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
26937c478bd9Sstevel@tonic-gate 		smtpquit(m, mci, e);
26947c478bd9Sstevel@tonic-gate 		return EX_PROTOCOL;
26957c478bd9Sstevel@tonic-gate 	}
26967c478bd9Sstevel@tonic-gate #endif /* _FFR_CATCH_BROKEN_MTAS */
26977c478bd9Sstevel@tonic-gate 
26987c478bd9Sstevel@tonic-gate 	if (sm_io_error(mci->mci_out))
26997c478bd9Sstevel@tonic-gate 	{
27007c478bd9Sstevel@tonic-gate 		/* error during processing -- don't send the dot */
27017c478bd9Sstevel@tonic-gate 		mci->mci_errno = EIO;
27027c478bd9Sstevel@tonic-gate 		mci->mci_state = MCIS_ERROR;
27037c478bd9Sstevel@tonic-gate 		mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
27047c478bd9Sstevel@tonic-gate 		smtpquit(m, mci, e);
27057c478bd9Sstevel@tonic-gate 		return EX_IOERR;
27067c478bd9Sstevel@tonic-gate 	}
27077c478bd9Sstevel@tonic-gate 
27087c478bd9Sstevel@tonic-gate 	/* terminate the message */
27097800901eSjbeck 	if (sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s.%s",
27107800901eSjbeck 			bitset(MCIF_INLONGLINE, mci->mci_flags) ? m->m_eol : "",
27117800901eSjbeck 			m->m_eol) == SM_IO_EOF)
2712445f2479Sjbeck 		goto writeerr;
27137c478bd9Sstevel@tonic-gate 	if (TrafficLogFile != NULL)
27147c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
27157c478bd9Sstevel@tonic-gate 				     "%05d >>> .\n", (int) CurrentPid);
27167c478bd9Sstevel@tonic-gate 	if (Verbose)
27177c478bd9Sstevel@tonic-gate 		nmessage(">>> .");
27187c478bd9Sstevel@tonic-gate 
27197c478bd9Sstevel@tonic-gate 	/* check for the results of the transaction */
27207c478bd9Sstevel@tonic-gate 	SmtpPhase = mci->mci_phase = "client DATA status";
27217c478bd9Sstevel@tonic-gate 	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
27227c478bd9Sstevel@tonic-gate 			CurHostName, mci->mci_phase);
27237c478bd9Sstevel@tonic-gate 	if (bitnset(M_LMTP, m->m_flags))
27247c478bd9Sstevel@tonic-gate 		return EX_OK;
27257c478bd9Sstevel@tonic-gate 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DEFAULT);
27267c478bd9Sstevel@tonic-gate 	if (r < 0)
27277c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
27287c478bd9Sstevel@tonic-gate 	if (mci->mci_state == MCIS_DATA)
27297c478bd9Sstevel@tonic-gate 		mci->mci_state = MCIS_OPEN;
27307c478bd9Sstevel@tonic-gate 	xstat = EX_NOTSTICKY;
27317c478bd9Sstevel@tonic-gate 	if (r == 452)
27327c478bd9Sstevel@tonic-gate 		rstat = EX_TEMPFAIL;
27337c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 4)
27347c478bd9Sstevel@tonic-gate 		rstat = xstat = EX_TEMPFAIL;
27357c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 2)
27367c478bd9Sstevel@tonic-gate 		rstat = xstat = EX_OK;
27377c478bd9Sstevel@tonic-gate 	else if (REPLYCLASS(r) != 5)
27387c478bd9Sstevel@tonic-gate 		rstat = xstat = EX_PROTOCOL;
27397c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 5)
27407c478bd9Sstevel@tonic-gate 		rstat = EX_UNAVAILABLE;
27417c478bd9Sstevel@tonic-gate 	else
27427c478bd9Sstevel@tonic-gate 		rstat = EX_PROTOCOL;
27437c478bd9Sstevel@tonic-gate 	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
27447c478bd9Sstevel@tonic-gate 		    SmtpReplyBuffer);
27457c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
27467c478bd9Sstevel@tonic-gate 	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
27477c478bd9Sstevel@tonic-gate 		r += 5;
27487c478bd9Sstevel@tonic-gate 	else
27497c478bd9Sstevel@tonic-gate 		r = 4;
27507c478bd9Sstevel@tonic-gate 	e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
27517c478bd9Sstevel@tonic-gate 	SmtpPhase = mci->mci_phase = "idle";
27527c478bd9Sstevel@tonic-gate 	sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase);
27537c478bd9Sstevel@tonic-gate 	if (rstat != EX_PROTOCOL)
27547c478bd9Sstevel@tonic-gate 		return rstat;
27557c478bd9Sstevel@tonic-gate 	if (LogLevel > 1)
27567c478bd9Sstevel@tonic-gate 	{
27577c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_CRIT, e->e_id,
27587c478bd9Sstevel@tonic-gate 			  "%.100s: SMTP DATA-2 protocol error: %s",
27597c478bd9Sstevel@tonic-gate 			  CurHostName,
27607c478bd9Sstevel@tonic-gate 			  shortenstring(SmtpReplyBuffer, 403));
27617c478bd9Sstevel@tonic-gate 	}
27627c478bd9Sstevel@tonic-gate 	return rstat;
27637c478bd9Sstevel@tonic-gate 
2764445f2479Sjbeck   writeerr:
2765445f2479Sjbeck 	mci->mci_errno = errno;
2766445f2479Sjbeck 	mci->mci_state = MCIS_ERROR;
2767445f2479Sjbeck 	mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
27687c478bd9Sstevel@tonic-gate 
27697c478bd9Sstevel@tonic-gate 	/*
2770445f2479Sjbeck 	**  If putbody() couldn't finish due to a timeout,
2771445f2479Sjbeck 	**  rewind it here in the timeout handler.  See
2772445f2479Sjbeck 	**  comments at the end of putbody() for reasoning.
27737c478bd9Sstevel@tonic-gate 	*/
27747c478bd9Sstevel@tonic-gate 
2775445f2479Sjbeck 	if (e->e_dfp != NULL)
2776445f2479Sjbeck 		(void) bfrewind(e->e_dfp);
27777c478bd9Sstevel@tonic-gate 
2778445f2479Sjbeck 	errno = mci->mci_errno;
2779445f2479Sjbeck 	syserr("451 4.4.1 timeout writing message to %s", CurHostName);
2780445f2479Sjbeck 	smtpquit(m, mci, e);
2781445f2479Sjbeck 	return EX_TEMPFAIL;
27827c478bd9Sstevel@tonic-gate }
2783445f2479Sjbeck 
27847c478bd9Sstevel@tonic-gate /*
27857c478bd9Sstevel@tonic-gate **  SMTPGETSTAT -- get status code from DATA in LMTP
27867c478bd9Sstevel@tonic-gate **
27877c478bd9Sstevel@tonic-gate **	Parameters:
27887c478bd9Sstevel@tonic-gate **		m -- the mailer to which we are sending the message.
27897c478bd9Sstevel@tonic-gate **		mci -- the mailer connection structure.
27907c478bd9Sstevel@tonic-gate **		e -- the current envelope.
27917c478bd9Sstevel@tonic-gate **
27927c478bd9Sstevel@tonic-gate **	Returns:
27937c478bd9Sstevel@tonic-gate **		The exit status corresponding to the reply code.
27947c478bd9Sstevel@tonic-gate */
27957c478bd9Sstevel@tonic-gate 
27967c478bd9Sstevel@tonic-gate int
smtpgetstat(m,mci,e)27977c478bd9Sstevel@tonic-gate smtpgetstat(m, mci, e)
27987c478bd9Sstevel@tonic-gate 	MAILER *m;
27997c478bd9Sstevel@tonic-gate 	MCI *mci;
28007c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
28017c478bd9Sstevel@tonic-gate {
28027c478bd9Sstevel@tonic-gate 	int r;
28037c478bd9Sstevel@tonic-gate 	int off;
28047c478bd9Sstevel@tonic-gate 	int status, xstat;
28057c478bd9Sstevel@tonic-gate 	char *enhsc;
28067c478bd9Sstevel@tonic-gate 
28077c478bd9Sstevel@tonic-gate 	enhsc = NULL;
28087c478bd9Sstevel@tonic-gate 
28097c478bd9Sstevel@tonic-gate 	/* check for the results of the transaction */
28107c478bd9Sstevel@tonic-gate 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DEFAULT);
28117c478bd9Sstevel@tonic-gate 	if (r < 0)
28127c478bd9Sstevel@tonic-gate 		return EX_TEMPFAIL;
28137c478bd9Sstevel@tonic-gate 	xstat = EX_NOTSTICKY;
28147c478bd9Sstevel@tonic-gate 	if (REPLYTYPE(r) == 4)
28157c478bd9Sstevel@tonic-gate 		status = EX_TEMPFAIL;
28167c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 2)
28177c478bd9Sstevel@tonic-gate 		status = xstat = EX_OK;
28187c478bd9Sstevel@tonic-gate 	else if (REPLYCLASS(r) != 5)
28197c478bd9Sstevel@tonic-gate 		status = xstat = EX_PROTOCOL;
28207c478bd9Sstevel@tonic-gate 	else if (REPLYTYPE(r) == 5)
28217c478bd9Sstevel@tonic-gate 		status = EX_UNAVAILABLE;
28227c478bd9Sstevel@tonic-gate 	else
28237c478bd9Sstevel@tonic-gate 		status = EX_PROTOCOL;
28247c478bd9Sstevel@tonic-gate 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
28257c478bd9Sstevel@tonic-gate 	    (off = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
28267c478bd9Sstevel@tonic-gate 		off += 5;
28277c478bd9Sstevel@tonic-gate 	else
28287c478bd9Sstevel@tonic-gate 		off = 4;
28297c478bd9Sstevel@tonic-gate 	e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[off]);
28307c478bd9Sstevel@tonic-gate 	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)), SmtpReplyBuffer);
28317c478bd9Sstevel@tonic-gate 	if (LogLevel > 1 && status == EX_PROTOCOL)
28327c478bd9Sstevel@tonic-gate 	{
28337c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_CRIT, e->e_id,
28347c478bd9Sstevel@tonic-gate 			  "%.100s: SMTP DATA-3 protocol error: %s",
28357c478bd9Sstevel@tonic-gate 			  CurHostName,
28367c478bd9Sstevel@tonic-gate 			  shortenstring(SmtpReplyBuffer, 403));
28377c478bd9Sstevel@tonic-gate 	}
28387c478bd9Sstevel@tonic-gate 	return status;
28397c478bd9Sstevel@tonic-gate }
28407c478bd9Sstevel@tonic-gate /*
28417c478bd9Sstevel@tonic-gate **  SMTPQUIT -- close the SMTP connection.
28427c478bd9Sstevel@tonic-gate **
28437c478bd9Sstevel@tonic-gate **	Parameters:
28447c478bd9Sstevel@tonic-gate **		m -- a pointer to the mailer.
28457c478bd9Sstevel@tonic-gate **		mci -- the mailer connection information.
28467c478bd9Sstevel@tonic-gate **		e -- the current envelope.
28477c478bd9Sstevel@tonic-gate **
28487c478bd9Sstevel@tonic-gate **	Returns:
28497c478bd9Sstevel@tonic-gate **		none.
28507c478bd9Sstevel@tonic-gate **
28517c478bd9Sstevel@tonic-gate **	Side Effects:
28527c478bd9Sstevel@tonic-gate **		sends the final protocol and closes the connection.
28537c478bd9Sstevel@tonic-gate */
28547c478bd9Sstevel@tonic-gate 
28557c478bd9Sstevel@tonic-gate void
smtpquit(m,mci,e)28567c478bd9Sstevel@tonic-gate smtpquit(m, mci, e)
28577c478bd9Sstevel@tonic-gate 	register MAILER *m;
28587c478bd9Sstevel@tonic-gate 	register MCI *mci;
28597c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
28607c478bd9Sstevel@tonic-gate {
28617c478bd9Sstevel@tonic-gate 	bool oldSuprErrs = SuprErrs;
28627c478bd9Sstevel@tonic-gate 	int rcode;
28637c478bd9Sstevel@tonic-gate 	char *oldcurhost;
28647c478bd9Sstevel@tonic-gate 
28657c478bd9Sstevel@tonic-gate 	if (mci->mci_state == MCIS_CLOSED)
28667c478bd9Sstevel@tonic-gate 	{
28677c478bd9Sstevel@tonic-gate 		mci_close(mci, "smtpquit:1");
28687c478bd9Sstevel@tonic-gate 		return;
28697c478bd9Sstevel@tonic-gate 	}
28707c478bd9Sstevel@tonic-gate 
28717c478bd9Sstevel@tonic-gate 	oldcurhost = CurHostName;
28727c478bd9Sstevel@tonic-gate 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
28737c478bd9Sstevel@tonic-gate 	if (CurHostName == NULL)
28747c478bd9Sstevel@tonic-gate 		CurHostName = MyHostName;
28757c478bd9Sstevel@tonic-gate 
28767c478bd9Sstevel@tonic-gate #if PIPELINING
28777c478bd9Sstevel@tonic-gate 	mci->mci_okrcpts = 0;
28787c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
28797c478bd9Sstevel@tonic-gate 
28807c478bd9Sstevel@tonic-gate 	/*
28817c478bd9Sstevel@tonic-gate 	**	Suppress errors here -- we may be processing a different
28827c478bd9Sstevel@tonic-gate 	**	job when we do the quit connection, and we don't want the
28837c478bd9Sstevel@tonic-gate 	**	new job to be penalized for something that isn't it's
28847c478bd9Sstevel@tonic-gate 	**	problem.
28857c478bd9Sstevel@tonic-gate 	*/
28867c478bd9Sstevel@tonic-gate 
28877c478bd9Sstevel@tonic-gate 	SuprErrs = true;
28887c478bd9Sstevel@tonic-gate 
28897c478bd9Sstevel@tonic-gate 	/* send the quit message if we haven't gotten I/O error */
28907c478bd9Sstevel@tonic-gate 	if (mci->mci_state != MCIS_ERROR &&
28917c478bd9Sstevel@tonic-gate 	    mci->mci_state != MCIS_QUITING)
28927c478bd9Sstevel@tonic-gate 	{
28937c478bd9Sstevel@tonic-gate 		SmtpPhase = "client QUIT";
28947c478bd9Sstevel@tonic-gate 		mci->mci_state = MCIS_QUITING;
28957c478bd9Sstevel@tonic-gate 		smtpmessage("QUIT", m, mci);
28967c478bd9Sstevel@tonic-gate 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL,
28977c478bd9Sstevel@tonic-gate 				XS_DEFAULT);
28987c478bd9Sstevel@tonic-gate 		SuprErrs = oldSuprErrs;
28997c478bd9Sstevel@tonic-gate 		if (mci->mci_state == MCIS_CLOSED)
29007c478bd9Sstevel@tonic-gate 			goto end;
29017c478bd9Sstevel@tonic-gate 	}
29027c478bd9Sstevel@tonic-gate 
29037c478bd9Sstevel@tonic-gate 	/* now actually close the connection and pick up the zombie */
29047c478bd9Sstevel@tonic-gate 	rcode = endmailer(mci, e, NULL);
29057c478bd9Sstevel@tonic-gate 	if (rcode != EX_OK)
29067c478bd9Sstevel@tonic-gate 	{
29077c478bd9Sstevel@tonic-gate 		char *mailer = NULL;
29087c478bd9Sstevel@tonic-gate 
29097c478bd9Sstevel@tonic-gate 		if (mci->mci_mailer != NULL &&
29107c478bd9Sstevel@tonic-gate 		    mci->mci_mailer->m_name != NULL)
29117c478bd9Sstevel@tonic-gate 			mailer = mci->mci_mailer->m_name;
29127c478bd9Sstevel@tonic-gate 
29137c478bd9Sstevel@tonic-gate 		/* look for naughty mailers */
29147c478bd9Sstevel@tonic-gate 		sm_syslog(LOG_ERR, e->e_id,
29157c478bd9Sstevel@tonic-gate 			  "smtpquit: mailer%s%s exited with exit value %d",
29167c478bd9Sstevel@tonic-gate 			  mailer == NULL ? "" : " ",
29177c478bd9Sstevel@tonic-gate 			  mailer == NULL ? "" : mailer,
29187c478bd9Sstevel@tonic-gate 			  rcode);
29197c478bd9Sstevel@tonic-gate 	}
29207c478bd9Sstevel@tonic-gate 
29217c478bd9Sstevel@tonic-gate 	SuprErrs = oldSuprErrs;
29227c478bd9Sstevel@tonic-gate 
29237c478bd9Sstevel@tonic-gate   end:
29247c478bd9Sstevel@tonic-gate 	CurHostName = oldcurhost;
29257c478bd9Sstevel@tonic-gate 	return;
29267c478bd9Sstevel@tonic-gate }
29277c478bd9Sstevel@tonic-gate /*
29287c478bd9Sstevel@tonic-gate **  SMTPRSET -- send a RSET (reset) command
29297c478bd9Sstevel@tonic-gate **
29307c478bd9Sstevel@tonic-gate **	Parameters:
29317c478bd9Sstevel@tonic-gate **		m -- a pointer to the mailer.
29327c478bd9Sstevel@tonic-gate **		mci -- the mailer connection information.
29337c478bd9Sstevel@tonic-gate **		e -- the current envelope.
29347c478bd9Sstevel@tonic-gate **
29357c478bd9Sstevel@tonic-gate **	Returns:
29367c478bd9Sstevel@tonic-gate **		none.
29377c478bd9Sstevel@tonic-gate **
29387c478bd9Sstevel@tonic-gate **	Side Effects:
29397c478bd9Sstevel@tonic-gate **		closes the connection if there is no reply to RSET.
29407c478bd9Sstevel@tonic-gate */
29417c478bd9Sstevel@tonic-gate 
29427c478bd9Sstevel@tonic-gate void
smtprset(m,mci,e)29437c478bd9Sstevel@tonic-gate smtprset(m, mci, e)
29447c478bd9Sstevel@tonic-gate 	register MAILER *m;
29457c478bd9Sstevel@tonic-gate 	register MCI *mci;
29467c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
29477c478bd9Sstevel@tonic-gate {
29487c478bd9Sstevel@tonic-gate 	int r;
29497c478bd9Sstevel@tonic-gate 
29507c478bd9Sstevel@tonic-gate 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
29517c478bd9Sstevel@tonic-gate 	if (CurHostName == NULL)
29527c478bd9Sstevel@tonic-gate 		CurHostName = MyHostName;
29537c478bd9Sstevel@tonic-gate 
29547c478bd9Sstevel@tonic-gate #if PIPELINING
29557c478bd9Sstevel@tonic-gate 	mci->mci_okrcpts = 0;
29567c478bd9Sstevel@tonic-gate #endif /* PIPELINING */
29577c478bd9Sstevel@tonic-gate 
29587c478bd9Sstevel@tonic-gate 	/*
29597c478bd9Sstevel@tonic-gate 	**  Check if connection is gone, if so
29607c478bd9Sstevel@tonic-gate 	**  it's a tempfail and we use mci_errno
29617c478bd9Sstevel@tonic-gate 	**  for the reason.
29627c478bd9Sstevel@tonic-gate 	*/
29637c478bd9Sstevel@tonic-gate 
29647c478bd9Sstevel@tonic-gate 	if (mci->mci_state == MCIS_CLOSED)
29657c478bd9Sstevel@tonic-gate 	{
29667c478bd9Sstevel@tonic-gate 		errno = mci->mci_errno;
29677c478bd9Sstevel@tonic-gate 		return;
29687c478bd9Sstevel@tonic-gate 	}
29697c478bd9Sstevel@tonic-gate 
29707c478bd9Sstevel@tonic-gate 	SmtpPhase = "client RSET";
29717c478bd9Sstevel@tonic-gate 	smtpmessage("RSET", m, mci);
29727c478bd9Sstevel@tonic-gate 	r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL, XS_DEFAULT);
29737c478bd9Sstevel@tonic-gate 	if (r < 0)
29747c478bd9Sstevel@tonic-gate 		return;
29757c478bd9Sstevel@tonic-gate 
29767c478bd9Sstevel@tonic-gate 	/*
29777c478bd9Sstevel@tonic-gate 	**  Any response is deemed to be acceptable.
29787c478bd9Sstevel@tonic-gate 	**  The standard does not state the proper action
29797c478bd9Sstevel@tonic-gate 	**  to take when a value other than 250 is received.
29807c478bd9Sstevel@tonic-gate 	**
29817c478bd9Sstevel@tonic-gate 	**  However, if 421 is returned for the RSET, leave
29827c478bd9Sstevel@tonic-gate 	**  mci_state alone (MCIS_SSD can be set in reply()
29837c478bd9Sstevel@tonic-gate 	**  and MCIS_CLOSED can be set in smtpquit() if
29847c478bd9Sstevel@tonic-gate 	**  reply() gets a 421 and calls smtpquit()).
29857c478bd9Sstevel@tonic-gate 	*/
29867c478bd9Sstevel@tonic-gate 
29877c478bd9Sstevel@tonic-gate 	if (mci->mci_state != MCIS_SSD && mci->mci_state != MCIS_CLOSED)
29887c478bd9Sstevel@tonic-gate 		mci->mci_state = MCIS_OPEN;
2989445f2479Sjbeck 	else if (mci->mci_exitstat == EX_OK)
2990445f2479Sjbeck 		mci_setstat(mci, EX_TEMPFAIL, "4.5.0", NULL);
29917c478bd9Sstevel@tonic-gate }
29927c478bd9Sstevel@tonic-gate /*
29937c478bd9Sstevel@tonic-gate **  SMTPPROBE -- check the connection state
29947c478bd9Sstevel@tonic-gate **
29957c478bd9Sstevel@tonic-gate **	Parameters:
29967c478bd9Sstevel@tonic-gate **		mci -- the mailer connection information.
29977c478bd9Sstevel@tonic-gate **
29987c478bd9Sstevel@tonic-gate **	Returns:
29997c478bd9Sstevel@tonic-gate **		none.
30007c478bd9Sstevel@tonic-gate **
30017c478bd9Sstevel@tonic-gate **	Side Effects:
30027c478bd9Sstevel@tonic-gate **		closes the connection if there is no reply to RSET.
30037c478bd9Sstevel@tonic-gate */
30047c478bd9Sstevel@tonic-gate 
30057c478bd9Sstevel@tonic-gate int
smtpprobe(mci)30067c478bd9Sstevel@tonic-gate smtpprobe(mci)
30077c478bd9Sstevel@tonic-gate 	register MCI *mci;
30087c478bd9Sstevel@tonic-gate {
30097c478bd9Sstevel@tonic-gate 	int r;
30107c478bd9Sstevel@tonic-gate 	MAILER *m = mci->mci_mailer;
30117c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
30127c478bd9Sstevel@tonic-gate 	extern ENVELOPE BlankEnvelope;
30137c478bd9Sstevel@tonic-gate 
30147c478bd9Sstevel@tonic-gate 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
30157c478bd9Sstevel@tonic-gate 	if (CurHostName == NULL)
30167c478bd9Sstevel@tonic-gate 		CurHostName = MyHostName;
30177c478bd9Sstevel@tonic-gate 
30187c478bd9Sstevel@tonic-gate 	e = &BlankEnvelope;
30197c478bd9Sstevel@tonic-gate 	SmtpPhase = "client probe";
30207c478bd9Sstevel@tonic-gate 	smtpmessage("RSET", m, mci);
30217c478bd9Sstevel@tonic-gate 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL, XS_DEFAULT);
30227c478bd9Sstevel@tonic-gate 	if (REPLYTYPE(r) != 2)
30237c478bd9Sstevel@tonic-gate 		smtpquit(m, mci, e);
30247c478bd9Sstevel@tonic-gate 	return r;
30257c478bd9Sstevel@tonic-gate }
30267c478bd9Sstevel@tonic-gate /*
30277c478bd9Sstevel@tonic-gate **  REPLY -- read arpanet reply
30287c478bd9Sstevel@tonic-gate **
30297c478bd9Sstevel@tonic-gate **	Parameters:
30307c478bd9Sstevel@tonic-gate **		m -- the mailer we are reading the reply from.
30317c478bd9Sstevel@tonic-gate **		mci -- the mailer connection info structure.
30327c478bd9Sstevel@tonic-gate **		e -- the current envelope.
30337c478bd9Sstevel@tonic-gate **		timeout -- the timeout for reads.
30347c478bd9Sstevel@tonic-gate **		pfunc -- processing function called on each line of response.
30357c478bd9Sstevel@tonic-gate **			If null, no special processing is done.
30367c478bd9Sstevel@tonic-gate **		enhstat -- optional, returns enhanced error code string (if set)
30377c478bd9Sstevel@tonic-gate **		rtype -- type of SmtpMsgBuffer: does it contains secret data?
30387c478bd9Sstevel@tonic-gate **
30397c478bd9Sstevel@tonic-gate **	Returns:
30407c478bd9Sstevel@tonic-gate **		reply code it reads.
30417c478bd9Sstevel@tonic-gate **
30427c478bd9Sstevel@tonic-gate **	Side Effects:
30437c478bd9Sstevel@tonic-gate **		flushes the mail file.
30447c478bd9Sstevel@tonic-gate */
30457c478bd9Sstevel@tonic-gate 
30467c478bd9Sstevel@tonic-gate int
reply(m,mci,e,timeout,pfunc,enhstat,rtype)30477c478bd9Sstevel@tonic-gate reply(m, mci, e, timeout, pfunc, enhstat, rtype)
30487c478bd9Sstevel@tonic-gate 	MAILER *m;
30497c478bd9Sstevel@tonic-gate 	MCI *mci;
30507c478bd9Sstevel@tonic-gate 	ENVELOPE *e;
30517c478bd9Sstevel@tonic-gate 	time_t timeout;
30527c478bd9Sstevel@tonic-gate 	void (*pfunc) __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
30537c478bd9Sstevel@tonic-gate 	char **enhstat;
30547c478bd9Sstevel@tonic-gate 	int rtype;
30557c478bd9Sstevel@tonic-gate {
30567c478bd9Sstevel@tonic-gate 	register char *bufp;
30577c478bd9Sstevel@tonic-gate 	register int r;
30587c478bd9Sstevel@tonic-gate 	bool firstline = true;
30597c478bd9Sstevel@tonic-gate 	char junkbuf[MAXLINE];
30607c478bd9Sstevel@tonic-gate 	static char enhstatcode[ENHSCLEN];
30617c478bd9Sstevel@tonic-gate 	int save_errno;
30627c478bd9Sstevel@tonic-gate 
30637c478bd9Sstevel@tonic-gate 	/*
30647c478bd9Sstevel@tonic-gate 	**  Flush the output before reading response.
30657c478bd9Sstevel@tonic-gate 	**
30667c478bd9Sstevel@tonic-gate 	**	For SMTP pipelining, it would be better if we didn't do
30677c478bd9Sstevel@tonic-gate 	**	this if there was already data waiting to be read.  But
30687c478bd9Sstevel@tonic-gate 	**	to do it properly means pushing it to the I/O library,
30697c478bd9Sstevel@tonic-gate 	**	since it really needs to be done below the buffer layer.
30707c478bd9Sstevel@tonic-gate 	*/
30717c478bd9Sstevel@tonic-gate 
30727c478bd9Sstevel@tonic-gate 	if (mci->mci_out != NULL)
30737c478bd9Sstevel@tonic-gate 		(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
30747c478bd9Sstevel@tonic-gate 
30757c478bd9Sstevel@tonic-gate 	if (tTd(18, 1))
30767c478bd9Sstevel@tonic-gate 		sm_dprintf("reply\n");
30777c478bd9Sstevel@tonic-gate 
30787c478bd9Sstevel@tonic-gate 	/*
30797c478bd9Sstevel@tonic-gate 	**  Read the input line, being careful not to hang.
30807c478bd9Sstevel@tonic-gate 	*/
30817c478bd9Sstevel@tonic-gate 
30827c478bd9Sstevel@tonic-gate 	bufp = SmtpReplyBuffer;
3083d4660949Sjbeck 	set_tls_rd_tmo(timeout);
30847c478bd9Sstevel@tonic-gate 	for (;;)
30857c478bd9Sstevel@tonic-gate 	{
30867c478bd9Sstevel@tonic-gate 		register char *p;
30877c478bd9Sstevel@tonic-gate 
30887c478bd9Sstevel@tonic-gate 		/* actually do the read */
30897c478bd9Sstevel@tonic-gate 		if (e->e_xfp != NULL)	/* for debugging */
30907c478bd9Sstevel@tonic-gate 			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
30917c478bd9Sstevel@tonic-gate 
30927c478bd9Sstevel@tonic-gate 		/* if we are in the process of closing just give the code */
30937c478bd9Sstevel@tonic-gate 		if (mci->mci_state == MCIS_CLOSED)
30947c478bd9Sstevel@tonic-gate 			return SMTPCLOSING;
30957c478bd9Sstevel@tonic-gate 
30967c478bd9Sstevel@tonic-gate 		/* don't try to read from a non-existent fd */
30977c478bd9Sstevel@tonic-gate 		if (mci->mci_in == NULL)
30987c478bd9Sstevel@tonic-gate 		{
30997c478bd9Sstevel@tonic-gate 			if (mci->mci_errno == 0)
31007c478bd9Sstevel@tonic-gate 				mci->mci_errno = EBADF;
31017c478bd9Sstevel@tonic-gate 
31027c478bd9Sstevel@tonic-gate 			/* errors on QUIT should be ignored */
31037c478bd9Sstevel@tonic-gate 			if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
31047c478bd9Sstevel@tonic-gate 			{
31057c478bd9Sstevel@tonic-gate 				errno = mci->mci_errno;
31067c478bd9Sstevel@tonic-gate 				mci_close(mci, "reply:1");
31077c478bd9Sstevel@tonic-gate 				return -1;
31087c478bd9Sstevel@tonic-gate 			}
31097c478bd9Sstevel@tonic-gate 			mci->mci_state = MCIS_ERROR;
31107c478bd9Sstevel@tonic-gate 			smtpquit(m, mci, e);
31117c478bd9Sstevel@tonic-gate 			errno = mci->mci_errno;
31127c478bd9Sstevel@tonic-gate 			return -1;
31137c478bd9Sstevel@tonic-gate 		}
31147c478bd9Sstevel@tonic-gate 
31157c478bd9Sstevel@tonic-gate 		if (mci->mci_out != NULL)
31167c478bd9Sstevel@tonic-gate 			(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
31177c478bd9Sstevel@tonic-gate 
31187c478bd9Sstevel@tonic-gate 		/* get the line from the other side */
31197c478bd9Sstevel@tonic-gate 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
31207c478bd9Sstevel@tonic-gate 		save_errno = errno;
31217c478bd9Sstevel@tonic-gate 		mci->mci_lastuse = curtime();
31227c478bd9Sstevel@tonic-gate 
31237c478bd9Sstevel@tonic-gate 		if (p == NULL)
31247c478bd9Sstevel@tonic-gate 		{
31257c478bd9Sstevel@tonic-gate 			bool oldholderrs;
31267c478bd9Sstevel@tonic-gate 			extern char MsgBuf[];
31277c478bd9Sstevel@tonic-gate 
31287c478bd9Sstevel@tonic-gate 			/* errors on QUIT should be ignored */
31297c478bd9Sstevel@tonic-gate 			if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
31307c478bd9Sstevel@tonic-gate 			{
31317c478bd9Sstevel@tonic-gate 				mci_close(mci, "reply:2");
31327c478bd9Sstevel@tonic-gate 				return -1;
31337c478bd9Sstevel@tonic-gate 			}
31347c478bd9Sstevel@tonic-gate 
31357c478bd9Sstevel@tonic-gate 			/* if the remote end closed early, fake an error */
31367c478bd9Sstevel@tonic-gate 			errno = save_errno;
31377c478bd9Sstevel@tonic-gate 			if (errno == 0)
31387c478bd9Sstevel@tonic-gate 			{
31397c478bd9Sstevel@tonic-gate 				(void) sm_snprintf(SmtpReplyBuffer,
3140058561cbSjbeck 						   sizeof(SmtpReplyBuffer),
31417c478bd9Sstevel@tonic-gate 						   "421 4.4.1 Connection reset by %s",
31427c478bd9Sstevel@tonic-gate 						   CURHOSTNAME);
31437c478bd9Sstevel@tonic-gate #ifdef ECONNRESET
31447c478bd9Sstevel@tonic-gate 				errno = ECONNRESET;
31457c478bd9Sstevel@tonic-gate #else /* ECONNRESET */
31467c478bd9Sstevel@tonic-gate 				errno = EPIPE;
31477c478bd9Sstevel@tonic-gate #endif /* ECONNRESET */
31487c478bd9Sstevel@tonic-gate 			}
31497c478bd9Sstevel@tonic-gate 
31507c478bd9Sstevel@tonic-gate 			mci->mci_errno = errno;
31517c478bd9Sstevel@tonic-gate 			oldholderrs = HoldErrs;
31527c478bd9Sstevel@tonic-gate 			HoldErrs = true;
31537c478bd9Sstevel@tonic-gate 			usrerr("451 4.4.1 reply: read error from %s",
31547c478bd9Sstevel@tonic-gate 			       CURHOSTNAME);
31557c478bd9Sstevel@tonic-gate 			mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
31567c478bd9Sstevel@tonic-gate 
31577c478bd9Sstevel@tonic-gate 			/* if debugging, pause so we can see state */
31587c478bd9Sstevel@tonic-gate 			if (tTd(18, 100))
31597c478bd9Sstevel@tonic-gate 				(void) pause();
31607c478bd9Sstevel@tonic-gate 			mci->mci_state = MCIS_ERROR;
31617c478bd9Sstevel@tonic-gate 			smtpquit(m, mci, e);
31627c478bd9Sstevel@tonic-gate #if XDEBUG
31637c478bd9Sstevel@tonic-gate 			{
31647c478bd9Sstevel@tonic-gate 				char wbuf[MAXLINE];
31657c478bd9Sstevel@tonic-gate 
31667c478bd9Sstevel@tonic-gate 				p = wbuf;
31677c478bd9Sstevel@tonic-gate 				if (e->e_to != NULL)
31687c478bd9Sstevel@tonic-gate 				{
31697c478bd9Sstevel@tonic-gate 					(void) sm_snprintf(p,
31707c478bd9Sstevel@tonic-gate 							   SPACELEFT(wbuf, p),
31717c478bd9Sstevel@tonic-gate 							   "%s... ",
31727c478bd9Sstevel@tonic-gate 							   shortenstring(e->e_to, MAXSHORTSTR));
31737c478bd9Sstevel@tonic-gate 					p += strlen(p);
31747c478bd9Sstevel@tonic-gate 				}
31757c478bd9Sstevel@tonic-gate 				(void) sm_snprintf(p, SPACELEFT(wbuf, p),
31767c478bd9Sstevel@tonic-gate 						   "reply(%.100s) during %s",
31777c478bd9Sstevel@tonic-gate 						   CURHOSTNAME, SmtpPhase);
31787c478bd9Sstevel@tonic-gate 				checkfd012(wbuf);
31797c478bd9Sstevel@tonic-gate 			}
31807c478bd9Sstevel@tonic-gate #endif /* XDEBUG */
31817c478bd9Sstevel@tonic-gate 			HoldErrs = oldholderrs;
31827c478bd9Sstevel@tonic-gate 			errno = save_errno;
31837c478bd9Sstevel@tonic-gate 			return -1;
31847c478bd9Sstevel@tonic-gate 		}
31857c478bd9Sstevel@tonic-gate 		fixcrlf(bufp, true);
31867c478bd9Sstevel@tonic-gate 
31877c478bd9Sstevel@tonic-gate 		/* EHLO failure is not a real error */
31887c478bd9Sstevel@tonic-gate 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
31897c478bd9Sstevel@tonic-gate 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
31907c478bd9Sstevel@tonic-gate 		{
31917c478bd9Sstevel@tonic-gate 			/* serious error -- log the previous command */
31927c478bd9Sstevel@tonic-gate 			if (SmtpNeedIntro)
31937c478bd9Sstevel@tonic-gate 			{
31947c478bd9Sstevel@tonic-gate 				/* inform user who we are chatting with */
31957c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(CurEnv->e_xfp,
31967c478bd9Sstevel@tonic-gate 						     SM_TIME_DEFAULT,
31977c478bd9Sstevel@tonic-gate 						     "... while talking to %s:\n",
31987c478bd9Sstevel@tonic-gate 						     CURHOSTNAME);
31997c478bd9Sstevel@tonic-gate 				SmtpNeedIntro = false;
32007c478bd9Sstevel@tonic-gate 			}
32017c478bd9Sstevel@tonic-gate 			if (SmtpMsgBuffer[0] != '\0')
32027c478bd9Sstevel@tonic-gate 			{
32037c478bd9Sstevel@tonic-gate 				(void) sm_io_fprintf(e->e_xfp,
32047c478bd9Sstevel@tonic-gate 					SM_TIME_DEFAULT,
32057c478bd9Sstevel@tonic-gate 					">>> %s\n",
32067c478bd9Sstevel@tonic-gate 					(rtype == XS_STARTTLS)
32077c478bd9Sstevel@tonic-gate 					? "STARTTLS dialogue"
32087c478bd9Sstevel@tonic-gate 					: ((rtype == XS_AUTH)
32097c478bd9Sstevel@tonic-gate 					   ? "AUTH dialogue"
32107c478bd9Sstevel@tonic-gate 					   : SmtpMsgBuffer));
32117c478bd9Sstevel@tonic-gate 				SmtpMsgBuffer[0] = '\0';
32127c478bd9Sstevel@tonic-gate 			}
32137c478bd9Sstevel@tonic-gate 
32147c478bd9Sstevel@tonic-gate 			/* now log the message as from the other side */
32157c478bd9Sstevel@tonic-gate 			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
32167c478bd9Sstevel@tonic-gate 					     "<<< %s\n", bufp);
32177c478bd9Sstevel@tonic-gate 		}
32187c478bd9Sstevel@tonic-gate 
32197c478bd9Sstevel@tonic-gate 		/* display the input for verbose mode */
32207c478bd9Sstevel@tonic-gate 		if (Verbose)
32217c478bd9Sstevel@tonic-gate 			nmessage("050 %s", bufp);
32227c478bd9Sstevel@tonic-gate 
32237c478bd9Sstevel@tonic-gate 		/* ignore improperly formatted input */
32247c478bd9Sstevel@tonic-gate 		if (!ISSMTPREPLY(bufp))
32257c478bd9Sstevel@tonic-gate 			continue;
32267c478bd9Sstevel@tonic-gate 
32277c478bd9Sstevel@tonic-gate 		if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
32287c478bd9Sstevel@tonic-gate 		    enhstat != NULL &&
32297c478bd9Sstevel@tonic-gate 		    extenhsc(bufp + 4, ' ', enhstatcode) > 0)
32307c478bd9Sstevel@tonic-gate 			*enhstat = enhstatcode;
32317c478bd9Sstevel@tonic-gate 
32327c478bd9Sstevel@tonic-gate 		/* process the line */
32337c478bd9Sstevel@tonic-gate 		if (pfunc != NULL)
32347c478bd9Sstevel@tonic-gate 			(*pfunc)(bufp, firstline, m, mci, e);
32357c478bd9Sstevel@tonic-gate 
32367c478bd9Sstevel@tonic-gate 		firstline = false;
32377c478bd9Sstevel@tonic-gate 
32387c478bd9Sstevel@tonic-gate 		/* decode the reply code */
32397c478bd9Sstevel@tonic-gate 		r = atoi(bufp);
32407c478bd9Sstevel@tonic-gate 
32417c478bd9Sstevel@tonic-gate 		/* extra semantics: 0xx codes are "informational" */
32427c478bd9Sstevel@tonic-gate 		if (r < 100)
32437c478bd9Sstevel@tonic-gate 			continue;
32447c478bd9Sstevel@tonic-gate 
32457c478bd9Sstevel@tonic-gate 		/* if no continuation lines, return this line */
32467c478bd9Sstevel@tonic-gate 		if (bufp[3] != '-')
32477c478bd9Sstevel@tonic-gate 			break;
32487c478bd9Sstevel@tonic-gate 
32497c478bd9Sstevel@tonic-gate 		/* first line of real reply -- ignore rest */
32507c478bd9Sstevel@tonic-gate 		bufp = junkbuf;
32517c478bd9Sstevel@tonic-gate 	}
32527c478bd9Sstevel@tonic-gate 
32537c478bd9Sstevel@tonic-gate 	/*
32547c478bd9Sstevel@tonic-gate 	**  Now look at SmtpReplyBuffer -- only care about the first
32557c478bd9Sstevel@tonic-gate 	**  line of the response from here on out.
32567c478bd9Sstevel@tonic-gate 	*/
32577c478bd9Sstevel@tonic-gate 
32587c478bd9Sstevel@tonic-gate 	/* save temporary failure messages for posterity */
32597c478bd9Sstevel@tonic-gate 	if (SmtpReplyBuffer[0] == '4')
3260058561cbSjbeck 		(void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof(SmtpError));
32617c478bd9Sstevel@tonic-gate 
32627c478bd9Sstevel@tonic-gate 	/* reply code 421 is "Service Shutting Down" */
32637c478bd9Sstevel@tonic-gate 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD &&
32647c478bd9Sstevel@tonic-gate 	    mci->mci_state != MCIS_QUITING)
32657c478bd9Sstevel@tonic-gate 	{
32667c478bd9Sstevel@tonic-gate 		/* send the quit protocol */
32677c478bd9Sstevel@tonic-gate 		mci->mci_state = MCIS_SSD;
32687c478bd9Sstevel@tonic-gate 		smtpquit(m, mci, e);
32697c478bd9Sstevel@tonic-gate 	}
32707c478bd9Sstevel@tonic-gate 
32717c478bd9Sstevel@tonic-gate 	return r;
32727c478bd9Sstevel@tonic-gate }
32737c478bd9Sstevel@tonic-gate /*
32747c478bd9Sstevel@tonic-gate **  SMTPMESSAGE -- send message to server
32757c478bd9Sstevel@tonic-gate **
32767c478bd9Sstevel@tonic-gate **	Parameters:
32777c478bd9Sstevel@tonic-gate **		f -- format
32787c478bd9Sstevel@tonic-gate **		m -- the mailer to control formatting.
32797c478bd9Sstevel@tonic-gate **		a, b, c -- parameters
32807c478bd9Sstevel@tonic-gate **
32817c478bd9Sstevel@tonic-gate **	Returns:
32827c478bd9Sstevel@tonic-gate **		none.
32837c478bd9Sstevel@tonic-gate **
32847c478bd9Sstevel@tonic-gate **	Side Effects:
32857c478bd9Sstevel@tonic-gate **		writes message to mci->mci_out.
32867c478bd9Sstevel@tonic-gate */
32877c478bd9Sstevel@tonic-gate 
32887c478bd9Sstevel@tonic-gate /*VARARGS1*/
32897c478bd9Sstevel@tonic-gate void
32907c478bd9Sstevel@tonic-gate #ifdef __STDC__
smtpmessage(char * f,MAILER * m,MCI * mci,...)32917c478bd9Sstevel@tonic-gate smtpmessage(char *f, MAILER *m, MCI *mci, ...)
32927c478bd9Sstevel@tonic-gate #else /* __STDC__ */
32937c478bd9Sstevel@tonic-gate smtpmessage(f, m, mci, va_alist)
32947c478bd9Sstevel@tonic-gate 	char *f;
32957c478bd9Sstevel@tonic-gate 	MAILER *m;
32967c478bd9Sstevel@tonic-gate 	MCI *mci;
32977c478bd9Sstevel@tonic-gate 	va_dcl
32987c478bd9Sstevel@tonic-gate #endif /* __STDC__ */
32997c478bd9Sstevel@tonic-gate {
33007c478bd9Sstevel@tonic-gate 	SM_VA_LOCAL_DECL
33017c478bd9Sstevel@tonic-gate 
33027c478bd9Sstevel@tonic-gate 	SM_VA_START(ap, mci);
3303058561cbSjbeck 	(void) sm_vsnprintf(SmtpMsgBuffer, sizeof(SmtpMsgBuffer), f, ap);
33047c478bd9Sstevel@tonic-gate 	SM_VA_END(ap);
33057c478bd9Sstevel@tonic-gate 
33067c478bd9Sstevel@tonic-gate 	if (tTd(18, 1) || Verbose)
33077c478bd9Sstevel@tonic-gate 		nmessage(">>> %s", SmtpMsgBuffer);
33087c478bd9Sstevel@tonic-gate 	if (TrafficLogFile != NULL)
33097c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
33107c478bd9Sstevel@tonic-gate 				     "%05d >>> %s\n", (int) CurrentPid,
33117c478bd9Sstevel@tonic-gate 				     SmtpMsgBuffer);
33127c478bd9Sstevel@tonic-gate 	if (mci->mci_out != NULL)
33137c478bd9Sstevel@tonic-gate 	{
33147c478bd9Sstevel@tonic-gate 		(void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s",
33157c478bd9Sstevel@tonic-gate 				     SmtpMsgBuffer, m == NULL ? "\r\n"
33167c478bd9Sstevel@tonic-gate 							      : m->m_eol);
33177c478bd9Sstevel@tonic-gate 	}
33187c478bd9Sstevel@tonic-gate 	else if (tTd(18, 1))
33197c478bd9Sstevel@tonic-gate 	{
33207c478bd9Sstevel@tonic-gate 		sm_dprintf("smtpmessage: NULL mci_out\n");
33217c478bd9Sstevel@tonic-gate 	}
33227c478bd9Sstevel@tonic-gate }
3323