usersmtp.c revision 182352
1/*
2 * Copyright (c) 1998-2006, 2008 Sendmail, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#include <sendmail.h>
15
16SM_RCSID("@(#)$Id: usersmtp.c,v 8.472 2008/01/31 18:48:29 ca Exp $")
17
18#include <sysexits.h>
19
20
21static void	esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
22static void	helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
23static int	smtprcptstat __P((ADDRESS *, MAILER *, MCI *, ENVELOPE *));
24
25#if SASL
26extern void	*sm_sasl_malloc __P((unsigned long));
27extern void	sm_sasl_free __P((void *));
28#endif /* SASL */
29
30/*
31**  USERSMTP -- run SMTP protocol from the user end.
32**
33**	This protocol is described in RFC821.
34*/
35
36#define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
37#define SMTPCLOSING	421			/* "Service Shutting Down" */
38
39#define ENHSCN(e, d)	((e) == NULL ? (d) : (e))
40
41#define ENHSCN_RPOOL(e, d, rpool) \
42	((e) == NULL ? (d) : sm_rpool_strdup_x(rpool, e))
43
44static char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
45static char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
46static bool	SmtpNeedIntro;		/* need "while talking" in transcript */
47/*
48**  SMTPINIT -- initialize SMTP.
49**
50**	Opens the connection and sends the initial protocol.
51**
52**	Parameters:
53**		m -- mailer to create connection to.
54**		mci -- the mailer connection info.
55**		e -- the envelope.
56**		onlyhelo -- send only helo command?
57**
58**	Returns:
59**		none.
60**
61**	Side Effects:
62**		creates connection and sends initial protocol.
63*/
64
65void
66smtpinit(m, mci, e, onlyhelo)
67	MAILER *m;
68	register MCI *mci;
69	ENVELOPE *e;
70	bool onlyhelo;
71{
72	register int r;
73	int state;
74	register char *p;
75	register char *hn;
76	char *enhsc;
77
78	enhsc = NULL;
79	if (tTd(18, 1))
80	{
81		sm_dprintf("smtpinit ");
82		mci_dump(sm_debug_file(), mci, false);
83	}
84
85	/*
86	**  Open the connection to the mailer.
87	*/
88
89	SmtpError[0] = '\0';
90	SmtpMsgBuffer[0] = '\0';
91	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
92	if (CurHostName == NULL)
93		CurHostName = MyHostName;
94	SmtpNeedIntro = true;
95	state = mci->mci_state;
96	switch (state)
97	{
98	  case MCIS_MAIL:
99	  case MCIS_RCPT:
100	  case MCIS_DATA:
101		/* need to clear old information */
102		smtprset(m, mci, e);
103		/* FALLTHROUGH */
104
105	  case MCIS_OPEN:
106		if (!onlyhelo)
107			return;
108		break;
109
110	  case MCIS_ERROR:
111	  case MCIS_QUITING:
112	  case MCIS_SSD:
113		/* shouldn't happen */
114		smtpquit(m, mci, e);
115		/* FALLTHROUGH */
116
117	  case MCIS_CLOSED:
118		syserr("451 4.4.0 smtpinit: state CLOSED (was %d)", state);
119		return;
120
121	  case MCIS_OPENING:
122		break;
123	}
124	if (onlyhelo)
125		goto helo;
126
127	mci->mci_state = MCIS_OPENING;
128	clrsessenvelope(e);
129
130	/*
131	**  Get the greeting message.
132	**	This should appear spontaneously.  Give it five minutes to
133	**	happen.
134	*/
135
136	SmtpPhase = mci->mci_phase = "client greeting";
137	sm_setproctitle(true, e, "%s %s: %s",
138			qid_printname(e), CurHostName, mci->mci_phase);
139	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL,
140		XS_DEFAULT);
141	if (r < 0)
142		goto tempfail1;
143	if (REPLYTYPE(r) == 4)
144		goto tempfail2;
145	if (REPLYTYPE(r) != 2)
146		goto unavailable;
147
148	/*
149	**  Send the HELO command.
150	**	My mother taught me to always introduce myself.
151	*/
152
153helo:
154	if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
155		mci->mci_flags |= MCIF_ESMTP;
156	hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
157
158tryhelo:
159#if _FFR_IGNORE_EXT_ON_HELO
160	mci->mci_flags &= ~MCIF_HELO;
161#endif /* _FFR_IGNORE_EXT_ON_HELO */
162	if (bitnset(M_LMTP, m->m_flags))
163	{
164		smtpmessage("LHLO %s", m, mci, hn);
165		SmtpPhase = mci->mci_phase = "client LHLO";
166	}
167	else if (bitset(MCIF_ESMTP, mci->mci_flags) &&
168		 !bitnset(M_FSMTP, m->m_flags))
169	{
170		smtpmessage("EHLO %s", m, mci, hn);
171		SmtpPhase = mci->mci_phase = "client EHLO";
172	}
173	else
174	{
175		smtpmessage("HELO %s", m, mci, hn);
176		SmtpPhase = mci->mci_phase = "client HELO";
177#if _FFR_IGNORE_EXT_ON_HELO
178		mci->mci_flags |= MCIF_HELO;
179#endif /* _FFR_IGNORE_EXT_ON_HELO */
180	}
181	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
182			CurHostName, mci->mci_phase);
183	r = reply(m, mci, e,
184		  bitnset(M_LMTP, m->m_flags) ? TimeOuts.to_lhlo
185					      : TimeOuts.to_helo,
186		  helo_options, NULL, XS_DEFAULT);
187	if (r < 0)
188		goto tempfail1;
189	else if (REPLYTYPE(r) == 5)
190	{
191		if (bitset(MCIF_ESMTP, mci->mci_flags) &&
192		    !bitnset(M_LMTP, m->m_flags))
193		{
194			/* try old SMTP instead */
195			mci->mci_flags &= ~MCIF_ESMTP;
196			goto tryhelo;
197		}
198		goto unavailable;
199	}
200	else if (REPLYTYPE(r) != 2)
201		goto tempfail2;
202
203	/*
204	**  Check to see if we actually ended up talking to ourself.
205	**  This means we didn't know about an alias or MX, or we managed
206	**  to connect to an echo server.
207	*/
208
209	p = strchr(&SmtpReplyBuffer[4], ' ');
210	if (p != NULL)
211		*p = '\0';
212	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
213	    !bitnset(M_LMTP, m->m_flags) &&
214	    sm_strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
215	{
216		syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
217			CurHostName);
218		mci_setstat(mci, EX_CONFIG, "5.3.5",
219			    "553 5.3.5 system config error");
220		mci->mci_errno = 0;
221		smtpquit(m, mci, e);
222		return;
223	}
224
225	/*
226	**  If this is expected to be another sendmail, send some internal
227	**  commands.
228	**  If we're running as MSP, "propagate" -v flag if possible.
229	*/
230
231	if ((UseMSP && Verbose && bitset(MCIF_VERB, mci->mci_flags))
232# if !_FFR_DEPRECATE_MAILER_FLAG_I
233	    || bitnset(M_INTERNAL, m->m_flags)
234# endif /* !_FFR_DEPRECATE_MAILER_FLAG_I */
235	   )
236	{
237		/* tell it to be verbose */
238		smtpmessage("VERB", m, mci);
239		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc,
240			XS_DEFAULT);
241		if (r < 0)
242			goto tempfail1;
243	}
244
245	if (mci->mci_state != MCIS_CLOSED)
246	{
247		mci->mci_state = MCIS_OPEN;
248		return;
249	}
250
251	/* got a 421 error code during startup */
252
253  tempfail1:
254	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
255	if (mci->mci_state != MCIS_CLOSED)
256		smtpquit(m, mci, e);
257	return;
258
259  tempfail2:
260	/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
261	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
262		    SmtpReplyBuffer);
263	if (mci->mci_state != MCIS_CLOSED)
264		smtpquit(m, mci, e);
265	return;
266
267  unavailable:
268	mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
269	smtpquit(m, mci, e);
270	return;
271}
272/*
273**  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
274**
275**	Parameters:
276**		line -- the response line.
277**		firstline -- set if this is the first line of the reply.
278**		m -- the mailer.
279**		mci -- the mailer connection info.
280**		e -- the envelope.
281**
282**	Returns:
283**		none.
284*/
285
286static void
287esmtp_check(line, firstline, m, mci, e)
288	char *line;
289	bool firstline;
290	MAILER *m;
291	register MCI *mci;
292	ENVELOPE *e;
293{
294	if (strstr(line, "ESMTP") != NULL)
295		mci->mci_flags |= MCIF_ESMTP;
296
297	/*
298	**  Dirty hack below. Quoting the author:
299	**  This was a response to people who wanted SMTP transmission to be
300	**  just-send-8 by default.  Essentially, you could put this tag into
301	**  your greeting message to behave as though the F=8 flag was set on
302	**  the mailer.
303	*/
304
305	if (strstr(line, "8BIT-OK") != NULL)
306		mci->mci_flags |= MCIF_8BITOK;
307}
308
309#if SASL
310/* specify prototype so compiler can check calls */
311static char *str_union __P((char *, char *, SM_RPOOL_T *));
312
313/*
314**  STR_UNION -- create the union of two lists
315**
316**	Parameters:
317**		s1, s2 -- lists of items (separated by single blanks).
318**		rpool -- resource pool from which result is allocated.
319**
320**	Returns:
321**		the union of both lists.
322*/
323
324static char *
325str_union(s1, s2, rpool)
326	char *s1, *s2;
327	SM_RPOOL_T *rpool;
328{
329	char *hr, *h1, *h, *res;
330	int l1, l2, rl;
331
332	if (s1 == NULL || *s1 == '\0')
333		return s2;
334	if (s2 == NULL || *s2 == '\0')
335		return s1;
336	l1 = strlen(s1);
337	l2 = strlen(s2);
338	rl = l1 + l2;
339	res = (char *) sm_rpool_malloc(rpool, rl + 2);
340	if (res == NULL)
341	{
342		if (l1 > l2)
343			return s1;
344		return s2;
345	}
346	(void) sm_strlcpy(res, s1, rl);
347	hr = res + l1;
348	h1 = s2;
349	h = s2;
350
351	/* walk through s2 */
352	while (h != NULL && *h1 != '\0')
353	{
354		/* is there something after the current word? */
355		if ((h = strchr(h1, ' ')) != NULL)
356			*h = '\0';
357		l1 = strlen(h1);
358
359		/* does the current word appear in s1 ? */
360		if (iteminlist(h1, s1, " ") == NULL)
361		{
362			/* add space as delimiter */
363			*hr++ = ' ';
364
365			/* copy the item */
366			memcpy(hr, h1, l1);
367
368			/* advance pointer in result list */
369			hr += l1;
370			*hr = '\0';
371		}
372		if (h != NULL)
373		{
374			/* there are more items */
375			*h = ' ';
376			h1 = h + 1;
377		}
378	}
379	return res;
380}
381#endif /* SASL */
382
383/*
384**  HELO_OPTIONS -- process the options on a HELO line.
385**
386**	Parameters:
387**		line -- the response line.
388**		firstline -- set if this is the first line of the reply.
389**		m -- the mailer.
390**		mci -- the mailer connection info.
391**		e -- the envelope (unused).
392**
393**	Returns:
394**		none.
395*/
396
397static void
398helo_options(line, firstline, m, mci, e)
399	char *line;
400	bool firstline;
401	MAILER *m;
402	register MCI *mci;
403	ENVELOPE *e;
404{
405	register char *p;
406#if _FFR_IGNORE_EXT_ON_HELO
407	static bool logged = false;
408#endif /* _FFR_IGNORE_EXT_ON_HELO */
409
410	if (firstline)
411	{
412#if SASL
413		mci->mci_saslcap = NULL;
414#endif /* SASL */
415#if _FFR_IGNORE_EXT_ON_HELO
416		logged = false;
417#endif /* _FFR_IGNORE_EXT_ON_HELO */
418		return;
419	}
420#if _FFR_IGNORE_EXT_ON_HELO
421	else if (bitset(MCIF_HELO, mci->mci_flags))
422	{
423		if (LogLevel > 8 && !logged)
424		{
425			sm_syslog(LOG_WARNING, NOQID,
426				  "server=%s [%s] returned extensions despite HELO command",
427				  macvalue(macid("{server_name}"), e),
428				  macvalue(macid("{server_addr}"), e));
429			logged = true;
430		}
431		return;
432	}
433#endif /* _FFR_IGNORE_EXT_ON_HELO */
434
435	if (strlen(line) < 5)
436		return;
437	line += 4;
438	p = strpbrk(line, " =");
439	if (p != NULL)
440		*p++ = '\0';
441	if (sm_strcasecmp(line, "size") == 0)
442	{
443		mci->mci_flags |= MCIF_SIZE;
444		if (p != NULL)
445			mci->mci_maxsize = atol(p);
446	}
447	else if (sm_strcasecmp(line, "8bitmime") == 0)
448	{
449		mci->mci_flags |= MCIF_8BITMIME;
450		mci->mci_flags &= ~MCIF_7BIT;
451	}
452	else if (sm_strcasecmp(line, "expn") == 0)
453		mci->mci_flags |= MCIF_EXPN;
454	else if (sm_strcasecmp(line, "dsn") == 0)
455		mci->mci_flags |= MCIF_DSN;
456	else if (sm_strcasecmp(line, "enhancedstatuscodes") == 0)
457		mci->mci_flags |= MCIF_ENHSTAT;
458	else if (sm_strcasecmp(line, "pipelining") == 0)
459		mci->mci_flags |= MCIF_PIPELINED;
460	else if (sm_strcasecmp(line, "verb") == 0)
461		mci->mci_flags |= MCIF_VERB;
462#if STARTTLS
463	else if (sm_strcasecmp(line, "starttls") == 0)
464		mci->mci_flags |= MCIF_TLS;
465#endif /* STARTTLS */
466	else if (sm_strcasecmp(line, "deliverby") == 0)
467	{
468		mci->mci_flags |= MCIF_DLVR_BY;
469		if (p != NULL)
470			mci->mci_min_by = atol(p);
471	}
472#if SASL
473	else if (sm_strcasecmp(line, "auth") == 0)
474	{
475		if (p != NULL && *p != '\0')
476		{
477			if (mci->mci_saslcap != NULL)
478			{
479				/*
480				**  Create the union with previous auth
481				**  offerings because we recognize "auth "
482				**  and "auth=" (old format).
483				*/
484
485				mci->mci_saslcap = str_union(mci->mci_saslcap,
486							     p, mci->mci_rpool);
487				mci->mci_flags |= MCIF_AUTH;
488			}
489			else
490			{
491				int l;
492
493				l = strlen(p) + 1;
494				mci->mci_saslcap = (char *)
495					sm_rpool_malloc(mci->mci_rpool, l);
496				if (mci->mci_saslcap != NULL)
497				{
498					(void) sm_strlcpy(mci->mci_saslcap, p,
499							  l);
500					mci->mci_flags |= MCIF_AUTH;
501				}
502			}
503		}
504	}
505#endif /* SASL */
506}
507#if SASL
508
509static int getsimple	__P((void *, int, const char **, unsigned *));
510static int getsecret	__P((sasl_conn_t *, void *, int, sasl_secret_t **));
511static int saslgetrealm	__P((void *, int, const char **, const char **));
512static int readauth	__P((char *, bool, SASL_AI_T *m, SM_RPOOL_T *));
513static int getauth	__P((MCI *, ENVELOPE *, SASL_AI_T *));
514static char *removemech	__P((char *, char *, SM_RPOOL_T *));
515static int attemptauth	__P((MAILER *, MCI *, ENVELOPE *, SASL_AI_T *));
516
517static sasl_callback_t callbacks[] =
518{
519	{	SASL_CB_GETREALM,	&saslgetrealm,	NULL	},
520#define CB_GETREALM_IDX	0
521	{	SASL_CB_PASS,		&getsecret,	NULL	},
522#define CB_PASS_IDX	1
523	{	SASL_CB_USER,		&getsimple,	NULL	},
524#define CB_USER_IDX	2
525	{	SASL_CB_AUTHNAME,	&getsimple,	NULL	},
526#define CB_AUTHNAME_IDX	3
527	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
528#define CB_SAFESASL_IDX	4
529	{	SASL_CB_LIST_END,	NULL,		NULL	}
530};
531
532/*
533**  INIT_SASL_CLIENT -- initialize client side of Cyrus-SASL
534**
535**	Parameters:
536**		none.
537**
538**	Returns:
539**		SASL_OK -- if successful.
540**		SASL error code -- otherwise.
541**
542**	Side Effects:
543**		checks/sets sasl_clt_init.
544**
545**	Note:
546**	Callbacks are ignored if sasl_client_init() has
547**	been called before (by a library such as libnss_ldap)
548*/
549
550static bool sasl_clt_init = false;
551
552static int
553init_sasl_client()
554{
555	int result;
556
557	if (sasl_clt_init)
558		return SASL_OK;
559	result = sasl_client_init(callbacks);
560
561	/* should we retry later again or just remember that it failed? */
562	if (result == SASL_OK)
563		sasl_clt_init = true;
564	return result;
565}
566/*
567**  STOP_SASL_CLIENT -- shutdown client side of Cyrus-SASL
568**
569**	Parameters:
570**		none.
571**
572**	Returns:
573**		none.
574**
575**	Side Effects:
576**		checks/sets sasl_clt_init.
577*/
578
579void
580stop_sasl_client()
581{
582	if (!sasl_clt_init)
583		return;
584	sasl_clt_init = false;
585	sasl_done();
586}
587/*
588**  GETSASLDATA -- process the challenges from the SASL protocol
589**
590**	This gets the relevant sasl response data out of the reply
591**	from the server.
592**
593**	Parameters:
594**		line -- the response line.
595**		firstline -- set if this is the first line of the reply.
596**		m -- the mailer.
597**		mci -- the mailer connection info.
598**		e -- the envelope (unused).
599**
600**	Returns:
601**		none.
602*/
603
604static void getsasldata __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
605
606static void
607getsasldata(line, firstline, m, mci, e)
608	char *line;
609	bool firstline;
610	MAILER *m;
611	register MCI *mci;
612	ENVELOPE *e;
613{
614	int len;
615	int result;
616# if SASL < 20000
617	char *out;
618# endif /* SASL < 20000 */
619
620	/* if not a continue we don't care about it */
621	len = strlen(line);
622	if ((len <= 4) ||
623	    (line[0] != '3') ||
624	     !isascii(line[1]) || !isdigit(line[1]) ||
625	     !isascii(line[2]) || !isdigit(line[2]))
626	{
627		SM_FREE_CLR(mci->mci_sasl_string);
628		return;
629	}
630
631	/* forget about "334 " */
632	line += 4;
633	len -= 4;
634# if SASL >= 20000
635	/* XXX put this into a macro/function? It's duplicated below */
636	if (mci->mci_sasl_string != NULL)
637	{
638		if (mci->mci_sasl_string_len <= len)
639		{
640			sm_free(mci->mci_sasl_string); /* XXX */
641			mci->mci_sasl_string = xalloc(len + 1);
642		}
643	}
644	else
645		mci->mci_sasl_string = xalloc(len + 1);
646
647	result = sasl_decode64(line, len, mci->mci_sasl_string, len + 1,
648			       (unsigned int *) &mci->mci_sasl_string_len);
649	if (result != SASL_OK)
650	{
651		mci->mci_sasl_string_len = 0;
652		*mci->mci_sasl_string = '\0';
653	}
654# else /* SASL >= 20000 */
655	out = (char *) sm_rpool_malloc_x(mci->mci_rpool, len + 1);
656	result = sasl_decode64(line, len, out, (unsigned int *) &len);
657	if (result != SASL_OK)
658	{
659		len = 0;
660		*out = '\0';
661	}
662
663	/*
664	**  mci_sasl_string is "shared" with Cyrus-SASL library; hence
665	**	it can't be in an rpool unless we use the same memory
666	**	management mechanism (with same rpool!) for Cyrus SASL.
667	*/
668
669	if (mci->mci_sasl_string != NULL)
670	{
671		if (mci->mci_sasl_string_len <= len)
672		{
673			sm_free(mci->mci_sasl_string); /* XXX */
674			mci->mci_sasl_string = xalloc(len + 1);
675		}
676	}
677	else
678		mci->mci_sasl_string = xalloc(len + 1);
679
680	memcpy(mci->mci_sasl_string, out, len);
681	mci->mci_sasl_string[len] = '\0';
682	mci->mci_sasl_string_len = len;
683# endif /* SASL >= 20000 */
684	return;
685}
686/*
687**  READAUTH -- read auth values from a file
688**
689**	Parameters:
690**		filename -- name of file to read.
691**		safe -- if set, this is a safe read.
692**		sai -- where to store auth_info.
693**		rpool -- resource pool for sai.
694**
695**	Returns:
696**		EX_OK -- data succesfully read.
697**		EX_UNAVAILABLE -- no valid filename.
698**		EX_TEMPFAIL -- temporary failure.
699*/
700
701static char *sasl_info_name[] =
702{
703	"user id",
704	"authentication id",
705	"password",
706	"realm",
707	"mechlist"
708};
709static int
710readauth(filename, safe, sai, rpool)
711	char *filename;
712	bool safe;
713	SASL_AI_T *sai;
714	SM_RPOOL_T *rpool;
715{
716	SM_FILE_T *f;
717	long sff;
718	pid_t pid;
719	int lc;
720	char *s;
721	char buf[MAXLINE];
722
723	if (filename == NULL || filename[0] == '\0')
724		return EX_UNAVAILABLE;
725
726#if !_FFR_ALLOW_SASLINFO
727	/*
728	**  make sure we don't use a program that is not
729	**  accesible to the user who specified a different authinfo file.
730	**  However, currently we don't pass this info (authinfo file
731	**  specified by user) around, so we just turn off program access.
732	*/
733
734	if (filename[0] == '|')
735	{
736		auto int fd;
737		int i;
738		char *p;
739		char *argv[MAXPV + 1];
740
741		i = 0;
742		for (p = strtok(&filename[1], " \t"); p != NULL;
743		     p = strtok(NULL, " \t"))
744		{
745			if (i >= MAXPV)
746				break;
747			argv[i++] = p;
748		}
749		argv[i] = NULL;
750		pid = prog_open(argv, &fd, CurEnv);
751		if (pid < 0)
752			f = NULL;
753		else
754			f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
755				       (void *) &fd, SM_IO_RDONLY, NULL);
756	}
757	else
758#endif /* !_FFR_ALLOW_SASLINFO */
759	{
760		pid = -1;
761		sff = SFF_REGONLY|SFF_SAFEDIRPATH|SFF_NOWLINK
762		      |SFF_NOGWFILES|SFF_NOWWFILES|SFF_NOWRFILES;
763# if _FFR_GROUPREADABLEAUTHINFOFILE
764		if (!bitnset(DBS_GROUPREADABLEAUTHINFOFILE, DontBlameSendmail))
765# endif /* _FFR_GROUPREADABLEAUTHINFOFILE */
766			sff |= SFF_NOGRFILES;
767		if (DontLockReadFiles)
768			sff |= SFF_NOLOCK;
769
770#if _FFR_ALLOW_SASLINFO
771		/*
772		**  XXX: make sure we don't read or open files that are not
773		**  accesible to the user who specified a different authinfo
774		**  file.
775		*/
776
777		sff |= SFF_MUSTOWN;
778#else /* _FFR_ALLOW_SASLINFO */
779		if (safe)
780			sff |= SFF_OPENASROOT;
781#endif /* _FFR_ALLOW_SASLINFO */
782
783		f = safefopen(filename, O_RDONLY, 0, sff);
784	}
785	if (f == NULL)
786	{
787		if (LogLevel > 5)
788			sm_syslog(LOG_ERR, NOQID,
789				  "AUTH=client, error: can't open %s: %s",
790				  filename, sm_errstring(errno));
791		return EX_TEMPFAIL;
792	}
793
794	lc = 0;
795	while (lc <= SASL_MECHLIST &&
796		sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) != NULL)
797	{
798		if (buf[0] != '#')
799		{
800			(*sai)[lc] = sm_rpool_strdup_x(rpool, buf);
801			if ((s = strchr((*sai)[lc], '\n')) != NULL)
802				*s = '\0';
803			lc++;
804		}
805	}
806
807	(void) sm_io_close(f, SM_TIME_DEFAULT);
808	if (pid > 0)
809		(void) waitfor(pid);
810	if (lc < SASL_PASSWORD)
811	{
812		if (LogLevel > 8)
813			sm_syslog(LOG_ERR, NOQID,
814				  "AUTH=client, error: can't read %s from %s",
815				  sasl_info_name[lc + 1], filename);
816		return EX_TEMPFAIL;
817	}
818	return EX_OK;
819}
820
821/*
822**  GETAUTH -- get authinfo from ruleset call
823**
824**	{server_name}, {server_addr} must be set
825**
826**	Parameters:
827**		mci -- the mailer connection structure.
828**		e -- the envelope (including the sender to specify).
829**		sai -- pointer to authinfo (result).
830**
831**	Returns:
832**		EX_OK -- ruleset was succesfully called, data may not
833**			be available, sai must be checked.
834**		EX_UNAVAILABLE -- ruleset unavailable (or failed).
835**		EX_TEMPFAIL -- temporary failure (from ruleset).
836**
837**	Side Effects:
838**		Fills in sai if successful.
839*/
840
841static int
842getauth(mci, e, sai)
843	MCI *mci;
844	ENVELOPE *e;
845	SASL_AI_T *sai;
846{
847	int i, r, l, got, ret;
848	char **pvp;
849	char pvpbuf[PSBUFSIZE];
850
851	r = rscap("authinfo", macvalue(macid("{server_name}"), e),
852		   macvalue(macid("{server_addr}"), e), e,
853		   &pvp, pvpbuf, sizeof(pvpbuf));
854
855	if (r != EX_OK)
856		return EX_UNAVAILABLE;
857
858	/* other than expected return value: ok (i.e., no auth) */
859	if (pvp == NULL || pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
860		return EX_OK;
861	if (pvp[1] != NULL && sm_strncasecmp(pvp[1], "temp", 4) == 0)
862		return EX_TEMPFAIL;
863
864	/*
865	**  parse the data, put it into sai
866	**  format: "TDstring" (including the '"' !)
867	**  where T is a tag: 'U', ...
868	**  D is a delimiter: ':' or '='
869	*/
870
871	ret = EX_OK;	/* default return value */
872	i = 0;
873	got = 0;
874	while (i < SASL_ENTRIES)
875	{
876		if (pvp[i + 1] == NULL)
877			break;
878		if (pvp[i + 1][0] != '"')
879			break;
880		switch (pvp[i + 1][1])
881		{
882		  case 'U':
883		  case 'u':
884			r = SASL_USER;
885			break;
886		  case 'I':
887		  case 'i':
888			r = SASL_AUTHID;
889			break;
890		  case 'P':
891		  case 'p':
892			r = SASL_PASSWORD;
893			break;
894		  case 'R':
895		  case 'r':
896			r = SASL_DEFREALM;
897			break;
898		  case 'M':
899		  case 'm':
900			r = SASL_MECHLIST;
901			break;
902		  default:
903			goto fail;
904		}
905		l = strlen(pvp[i + 1]);
906
907		/* check syntax */
908		if (l <= 3 || pvp[i + 1][l - 1] != '"')
909			goto fail;
910
911		/* remove closing quote */
912		pvp[i + 1][l - 1] = '\0';
913
914		/* remove "TD and " */
915		l -= 4;
916		(*sai)[r] = (char *) sm_rpool_malloc(mci->mci_rpool, l + 1);
917		if ((*sai)[r] == NULL)
918			goto tempfail;
919		if (pvp[i + 1][2] == ':')
920		{
921			/* ':text' (just copy) */
922			(void) sm_strlcpy((*sai)[r], pvp[i + 1] + 3, l + 1);
923			got |= 1 << r;
924		}
925		else if (pvp[i + 1][2] == '=')
926		{
927			unsigned int len;
928
929			/* '=base64' (decode) */
930# if SASL >= 20000
931			ret = sasl_decode64(pvp[i + 1] + 3,
932					  (unsigned int) l, (*sai)[r],
933					  (unsigned int) l + 1, &len);
934# else /* SASL >= 20000 */
935			ret = sasl_decode64(pvp[i + 1] + 3,
936					  (unsigned int) l, (*sai)[r], &len);
937# endif /* SASL >= 20000 */
938			if (ret != SASL_OK)
939				goto fail;
940			got |= 1 << r;
941		}
942		else
943			goto fail;
944		if (tTd(95, 5))
945			sm_syslog(LOG_DEBUG, NOQID, "getauth %s=%s",
946				  sasl_info_name[r], (*sai)[r]);
947		++i;
948	}
949
950	/* did we get the expected data? */
951	/* XXX: EXTERNAL mechanism only requires (and only uses) SASL_USER */
952	if (!(bitset(SASL_USER_BIT|SASL_AUTHID_BIT, got) &&
953	      bitset(SASL_PASSWORD_BIT, got)))
954		goto fail;
955
956	/* no authid? copy uid */
957	if (!bitset(SASL_AUTHID_BIT, got))
958	{
959		l = strlen((*sai)[SASL_USER]) + 1;
960		(*sai)[SASL_AUTHID] = (char *) sm_rpool_malloc(mci->mci_rpool,
961							       l + 1);
962		if ((*sai)[SASL_AUTHID] == NULL)
963			goto tempfail;
964		(void) sm_strlcpy((*sai)[SASL_AUTHID], (*sai)[SASL_USER], l);
965	}
966
967	/* no uid? copy authid */
968	if (!bitset(SASL_USER_BIT, got))
969	{
970		l = strlen((*sai)[SASL_AUTHID]) + 1;
971		(*sai)[SASL_USER] = (char *) sm_rpool_malloc(mci->mci_rpool,
972							     l + 1);
973		if ((*sai)[SASL_USER] == NULL)
974			goto tempfail;
975		(void) sm_strlcpy((*sai)[SASL_USER], (*sai)[SASL_AUTHID], l);
976	}
977	return EX_OK;
978
979  tempfail:
980	ret = EX_TEMPFAIL;
981  fail:
982	if (LogLevel > 8)
983		sm_syslog(LOG_WARNING, NOQID,
984			  "AUTH=client, relay=%.64s [%.16s], authinfo %sfailed",
985			  macvalue(macid("{server_name}"), e),
986			  macvalue(macid("{server_addr}"), e),
987			  ret == EX_TEMPFAIL ? "temp" : "");
988	for (i = 0; i <= SASL_MECHLIST; i++)
989		(*sai)[i] = NULL;	/* just clear; rpool */
990	return ret;
991}
992
993# if SASL >= 20000
994/*
995**  GETSIMPLE -- callback to get userid or authid
996**
997**	Parameters:
998**		context -- sai
999**		id -- what to do
1000**		result -- (pointer to) result
1001**		len -- (pointer to) length of result
1002**
1003**	Returns:
1004**		OK/failure values
1005*/
1006
1007static int
1008getsimple(context, id, result, len)
1009	void *context;
1010	int id;
1011	const char **result;
1012	unsigned *len;
1013{
1014	SASL_AI_T *sai;
1015
1016	if (result == NULL || context == NULL)
1017		return SASL_BADPARAM;
1018	sai = (SASL_AI_T *) context;
1019
1020	switch (id)
1021	{
1022	  case SASL_CB_USER:
1023		*result = (*sai)[SASL_USER];
1024		if (tTd(95, 5))
1025			sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1026				  *result);
1027		if (len != NULL)
1028			*len = *result != NULL ? strlen(*result) : 0;
1029		break;
1030
1031	  case SASL_CB_AUTHNAME:
1032		*result = (*sai)[SASL_AUTHID];
1033		if (tTd(95, 5))
1034			sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1035				  *result);
1036		if (len != NULL)
1037			*len = *result != NULL ? strlen(*result) : 0;
1038		break;
1039
1040	  case SASL_CB_LANGUAGE:
1041		*result = NULL;
1042		if (len != NULL)
1043			*len = 0;
1044		break;
1045
1046	  default:
1047		return SASL_BADPARAM;
1048	}
1049	return SASL_OK;
1050}
1051/*
1052**  GETSECRET -- callback to get password
1053**
1054**	Parameters:
1055**		conn -- connection information
1056**		context -- sai
1057**		id -- what to do
1058**		psecret -- (pointer to) result
1059**
1060**	Returns:
1061**		OK/failure values
1062*/
1063
1064static int
1065getsecret(conn, context, id, psecret)
1066	sasl_conn_t *conn;
1067	SM_UNUSED(void *context);
1068	int id;
1069	sasl_secret_t **psecret;
1070{
1071	int len;
1072	char *authpass;
1073	MCI *mci;
1074
1075	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1076		return SASL_BADPARAM;
1077
1078	mci = (MCI *) context;
1079	authpass = mci->mci_sai[SASL_PASSWORD];
1080	len = strlen(authpass);
1081
1082	/*
1083	**  use an rpool because we are responsible for free()ing the secret,
1084	**  but we can't free() it until after the auth completes
1085	*/
1086
1087	*psecret = (sasl_secret_t *) sm_rpool_malloc(mci->mci_rpool,
1088						     sizeof(sasl_secret_t) +
1089						     len + 1);
1090	if (*psecret == NULL)
1091		return SASL_FAIL;
1092	(void) sm_strlcpy((char *) (*psecret)->data, authpass, len + 1);
1093	(*psecret)->len = (unsigned long) len;
1094	return SASL_OK;
1095}
1096# else /* SASL >= 20000 */
1097/*
1098**  GETSIMPLE -- callback to get userid or authid
1099**
1100**	Parameters:
1101**		context -- sai
1102**		id -- what to do
1103**		result -- (pointer to) result
1104**		len -- (pointer to) length of result
1105**
1106**	Returns:
1107**		OK/failure values
1108*/
1109
1110static int
1111getsimple(context, id, result, len)
1112	void *context;
1113	int id;
1114	const char **result;
1115	unsigned *len;
1116{
1117	char *h, *s;
1118# if SASL > 10509
1119	bool addrealm;
1120# endif /* SASL > 10509 */
1121	size_t l;
1122	SASL_AI_T *sai;
1123	char *authid = NULL;
1124
1125	if (result == NULL || context == NULL)
1126		return SASL_BADPARAM;
1127	sai = (SASL_AI_T *) context;
1128
1129	/*
1130	**  Unfortunately it is not clear whether this routine should
1131	**  return a copy of a string or just a pointer to a string.
1132	**  The Cyrus-SASL plugins treat these return values differently, e.g.,
1133	**  plugins/cram.c free()s authid, plugings/digestmd5.c does not.
1134	**  The best solution to this problem is to fix Cyrus-SASL, but it
1135	**  seems there is nobody who creates patches... Hello CMU!?
1136	**  The second best solution is to have flags that tell this routine
1137	**  whether to return an malloc()ed copy.
1138	**  The next best solution is to always return an malloc()ed copy,
1139	**  and suffer from some memory leak, which is ugly for persistent
1140	**  queue runners.
1141	**  For now we go with the last solution...
1142	**  We can't use rpools (which would avoid this particular problem)
1143	**  as explained in sasl.c.
1144	*/
1145
1146	switch (id)
1147	{
1148	  case SASL_CB_USER:
1149		l = strlen((*sai)[SASL_USER]) + 1;
1150		s = sm_sasl_malloc(l);
1151		if (s == NULL)
1152		{
1153			if (len != NULL)
1154				*len = 0;
1155			*result = NULL;
1156			return SASL_NOMEM;
1157		}
1158		(void) sm_strlcpy(s, (*sai)[SASL_USER], l);
1159		*result = s;
1160		if (tTd(95, 5))
1161			sm_syslog(LOG_DEBUG, NOQID, "AUTH username '%s'",
1162				  *result);
1163		if (len != NULL)
1164			*len = *result != NULL ? strlen(*result) : 0;
1165		break;
1166
1167	  case SASL_CB_AUTHNAME:
1168		h = (*sai)[SASL_AUTHID];
1169# if SASL > 10509
1170		/* XXX maybe other mechanisms too?! */
1171		addrealm = (*sai)[SASL_MECH] != NULL &&
1172			   sm_strcasecmp((*sai)[SASL_MECH], "CRAM-MD5") == 0;
1173
1174		/*
1175		**  Add realm to authentication id unless authid contains
1176		**  '@' (i.e., a realm) or the default realm is empty.
1177		*/
1178
1179		if (addrealm && h != NULL && strchr(h, '@') == NULL)
1180		{
1181			/* has this been done before? */
1182			if ((*sai)[SASL_ID_REALM] == NULL)
1183			{
1184				char *realm;
1185
1186				realm = (*sai)[SASL_DEFREALM];
1187
1188				/* do not add an empty realm */
1189				if (*realm == '\0')
1190				{
1191					authid = h;
1192					(*sai)[SASL_ID_REALM] = NULL;
1193				}
1194				else
1195				{
1196					l = strlen(h) + strlen(realm) + 2;
1197
1198					/* should use rpool, but from where? */
1199					authid = sm_sasl_malloc(l);
1200					if (authid != NULL)
1201					{
1202						(void) sm_snprintf(authid, l,
1203								  "%s@%s",
1204								   h, realm);
1205						(*sai)[SASL_ID_REALM] = authid;
1206					}
1207					else
1208					{
1209						authid = h;
1210						(*sai)[SASL_ID_REALM] = NULL;
1211					}
1212				}
1213			}
1214			else
1215				authid = (*sai)[SASL_ID_REALM];
1216		}
1217		else
1218# endif /* SASL > 10509 */
1219			authid = h;
1220		l = strlen(authid) + 1;
1221		s = sm_sasl_malloc(l);
1222		if (s == NULL)
1223		{
1224			if (len != NULL)
1225				*len = 0;
1226			*result = NULL;
1227			return SASL_NOMEM;
1228		}
1229		(void) sm_strlcpy(s, authid, l);
1230		*result = s;
1231		if (tTd(95, 5))
1232			sm_syslog(LOG_DEBUG, NOQID, "AUTH authid '%s'",
1233				  *result);
1234		if (len != NULL)
1235			*len = authid ? strlen(authid) : 0;
1236		break;
1237
1238	  case SASL_CB_LANGUAGE:
1239		*result = NULL;
1240		if (len != NULL)
1241			*len = 0;
1242		break;
1243
1244	  default:
1245		return SASL_BADPARAM;
1246	}
1247	return SASL_OK;
1248}
1249/*
1250**  GETSECRET -- callback to get password
1251**
1252**	Parameters:
1253**		conn -- connection information
1254**		context -- sai
1255**		id -- what to do
1256**		psecret -- (pointer to) result
1257**
1258**	Returns:
1259**		OK/failure values
1260*/
1261
1262static int
1263getsecret(conn, context, id, psecret)
1264	sasl_conn_t *conn;
1265	SM_UNUSED(void *context);
1266	int id;
1267	sasl_secret_t **psecret;
1268{
1269	int len;
1270	char *authpass;
1271	SASL_AI_T *sai;
1272
1273	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
1274		return SASL_BADPARAM;
1275
1276	sai = (SASL_AI_T *) context;
1277	authpass = (*sai)[SASL_PASSWORD];
1278	len = strlen(authpass);
1279	*psecret = (sasl_secret_t *) sm_sasl_malloc(sizeof(sasl_secret_t) +
1280						    len + 1);
1281	if (*psecret == NULL)
1282		return SASL_FAIL;
1283	(void) sm_strlcpy((*psecret)->data, authpass, len + 1);
1284	(*psecret)->len = (unsigned long) len;
1285	return SASL_OK;
1286}
1287# endif /* SASL >= 20000 */
1288
1289/*
1290**  SAFESASLFILE -- callback for sasl: is file safe?
1291**
1292**	Parameters:
1293**		context -- pointer to context between invocations (unused)
1294**		file -- name of file to check
1295**		type -- type of file to check
1296**
1297**	Returns:
1298**		SASL_OK -- file can be used
1299**		SASL_CONTINUE -- don't use file
1300**		SASL_FAIL -- failure (not used here)
1301**
1302*/
1303
1304int
1305#if SASL > 10515
1306safesaslfile(context, file, type)
1307#else /* SASL > 10515 */
1308safesaslfile(context, file)
1309#endif /* SASL > 10515 */
1310	void *context;
1311# if SASL >= 20000
1312	const char *file;
1313# else /* SASL >= 20000 */
1314	char *file;
1315# endif /* SASL >= 20000 */
1316#if SASL > 10515
1317# if SASL >= 20000
1318	sasl_verify_type_t type;
1319# else /* SASL >= 20000 */
1320	int type;
1321# endif /* SASL >= 20000 */
1322#endif /* SASL > 10515 */
1323{
1324	long sff;
1325	int r;
1326#if SASL <= 10515
1327	size_t len;
1328#endif /* SASL <= 10515 */
1329	char *p;
1330
1331	if (file == NULL || *file == '\0')
1332		return SASL_OK;
1333	sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOWWFILES|SFF_ROOTOK;
1334#if SASL <= 10515
1335	if ((p = strrchr(file, '/')) == NULL)
1336		p = file;
1337	else
1338		++p;
1339
1340	/* everything beside libs and .conf files must not be readable */
1341	len = strlen(p);
1342	if ((len <= 3 || strncmp(p, "lib", 3) != 0) &&
1343	    (len <= 5 || strncmp(p + len - 5, ".conf", 5) != 0))
1344	{
1345		if (!bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1346			sff |= SFF_NORFILES;
1347		if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1348			sff |= SFF_NOGWFILES;
1349	}
1350#else /* SASL <= 10515 */
1351	/* files containing passwords should be not readable */
1352	if (type == SASL_VRFY_PASSWD)
1353	{
1354		if (bitnset(DBS_GROUPREADABLESASLDBFILE, DontBlameSendmail))
1355			sff |= SFF_NOWRFILES;
1356		else
1357			sff |= SFF_NORFILES;
1358		if (!bitnset(DBS_GROUPWRITABLESASLDBFILE, DontBlameSendmail))
1359			sff |= SFF_NOGWFILES;
1360	}
1361#endif /* SASL <= 10515 */
1362
1363	p = (char *) file;
1364	if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
1365			  S_IRUSR, NULL)) == 0)
1366		return SASL_OK;
1367	if (LogLevel > (r != ENOENT ? 8 : 10))
1368		sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
1369			  p, sm_errstring(r));
1370	return SASL_CONTINUE;
1371}
1372
1373/*
1374**  SASLGETREALM -- return the realm for SASL
1375**
1376**	return the realm for the client
1377**
1378**	Parameters:
1379**		context -- context shared between invocations
1380**		availrealms -- list of available realms
1381**			{realm, realm, ...}
1382**		result -- pointer to result
1383**
1384**	Returns:
1385**		failure/success
1386*/
1387
1388static int
1389saslgetrealm(context, id, availrealms, result)
1390	void *context;
1391	int id;
1392	const char **availrealms;
1393	const char **result;
1394{
1395	char *r;
1396	SASL_AI_T *sai;
1397
1398	sai = (SASL_AI_T *) context;
1399	if (sai == NULL)
1400		return SASL_FAIL;
1401	r = (*sai)[SASL_DEFREALM];
1402
1403	if (LogLevel > 12)
1404		sm_syslog(LOG_INFO, NOQID,
1405			  "AUTH=client, realm=%s, available realms=%s",
1406			  r == NULL ? "<No Realm>" : r,
1407			  (availrealms == NULL || *availrealms == NULL)
1408				? "<No Realms>" : *availrealms);
1409
1410	/* check whether context is in list */
1411	if (availrealms != NULL && *availrealms != NULL)
1412	{
1413		if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
1414		    NULL)
1415		{
1416			if (LogLevel > 8)
1417				sm_syslog(LOG_ERR, NOQID,
1418					  "AUTH=client, realm=%s not in list=%s",
1419					  r, *availrealms);
1420			return SASL_FAIL;
1421		}
1422	}
1423	*result = r;
1424	return SASL_OK;
1425}
1426/*
1427**  ITEMINLIST -- does item appear in list?
1428**
1429**	Check whether item appears in list (which must be separated by a
1430**	character in delim) as a "word", i.e. it must appear at the begin
1431**	of the list or after a space, and it must end with a space or the
1432**	end of the list.
1433**
1434**	Parameters:
1435**		item -- item to search.
1436**		list -- list of items.
1437**		delim -- list of delimiters.
1438**
1439**	Returns:
1440**		pointer to occurrence (NULL if not found).
1441*/
1442
1443char *
1444iteminlist(item, list, delim)
1445	char *item;
1446	char *list;
1447	char *delim;
1448{
1449	char *s;
1450	int len;
1451
1452	if (list == NULL || *list == '\0')
1453		return NULL;
1454	if (item == NULL || *item == '\0')
1455		return NULL;
1456	s = list;
1457	len = strlen(item);
1458	while (s != NULL && *s != '\0')
1459	{
1460		if (sm_strncasecmp(s, item, len) == 0 &&
1461		    (s[len] == '\0' || strchr(delim, s[len]) != NULL))
1462			return s;
1463		s = strpbrk(s, delim);
1464		if (s != NULL)
1465			while (*++s == ' ')
1466				continue;
1467	}
1468	return NULL;
1469}
1470/*
1471**  REMOVEMECH -- remove item [rem] from list [list]
1472**
1473**	Parameters:
1474**		rem -- item to remove
1475**		list -- list of items
1476**		rpool -- resource pool from which result is allocated.
1477**
1478**	Returns:
1479**		pointer to new list (NULL in case of error).
1480*/
1481
1482static char *
1483removemech(rem, list, rpool)
1484	char *rem;
1485	char *list;
1486	SM_RPOOL_T *rpool;
1487{
1488	char *ret;
1489	char *needle;
1490	int len;
1491
1492	if (list == NULL)
1493		return NULL;
1494	if (rem == NULL || *rem == '\0')
1495	{
1496		/* take out what? */
1497		return NULL;
1498	}
1499
1500	/* find the item in the list */
1501	if ((needle = iteminlist(rem, list, " ")) == NULL)
1502	{
1503		/* not in there: return original */
1504		return list;
1505	}
1506
1507	/* length of string without rem */
1508	len = strlen(list) - strlen(rem);
1509	if (len <= 0)
1510	{
1511		ret = (char *) sm_rpool_malloc_x(rpool, 1);
1512		*ret = '\0';
1513		return ret;
1514	}
1515	ret = (char *) sm_rpool_malloc_x(rpool, len);
1516	memset(ret, '\0', len);
1517
1518	/* copy from start to removed item */
1519	memcpy(ret, list, needle - list);
1520
1521	/* length of rest of string past removed item */
1522	len = strlen(needle) - strlen(rem) - 1;
1523	if (len > 0)
1524	{
1525		/* not last item -- copy into string */
1526		memcpy(ret + (needle - list),
1527		       list + (needle - list) + strlen(rem) + 1,
1528		       len);
1529	}
1530	else
1531		ret[(needle - list) - 1] = '\0';
1532	return ret;
1533}
1534/*
1535**  ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1536**
1537**	Parameters:
1538**		m -- the mailer.
1539**		mci -- the mailer connection structure.
1540**		e -- the envelope (including the sender to specify).
1541**		sai - sasl authinfo
1542**
1543**	Returns:
1544**		EX_OK -- authentication was successful.
1545**		EX_NOPERM -- authentication failed.
1546**		EX_IOERR -- authentication dialogue failed (I/O problem?).
1547**		EX_TEMPFAIL -- temporary failure.
1548**
1549*/
1550
1551static int
1552attemptauth(m, mci, e, sai)
1553	MAILER *m;
1554	MCI *mci;
1555	ENVELOPE *e;
1556	SASL_AI_T *sai;
1557{
1558	int saslresult, smtpresult;
1559# if SASL >= 20000
1560	sasl_ssf_t ssf;
1561	const char *auth_id;
1562	const char *out;
1563# else /* SASL >= 20000 */
1564	sasl_external_properties_t ssf;
1565	char *out;
1566# endif /* SASL >= 20000 */
1567	unsigned int outlen;
1568	sasl_interact_t *client_interact = NULL;
1569	char *mechusing;
1570	sasl_security_properties_t ssp;
1571	char in64[MAXOUTLEN];
1572#if NETINET || (NETINET6 && SASL >= 20000)
1573	extern SOCKADDR CurHostAddr;
1574#endif /* NETINET || (NETINET6 && SASL >= 20000) */
1575
1576	/* no mechanism selected (yet) */
1577	(*sai)[SASL_MECH] = NULL;
1578
1579	/* dispose old connection */
1580	if (mci->mci_conn != NULL)
1581		sasl_dispose(&(mci->mci_conn));
1582
1583	/* make a new client sasl connection */
1584# if SASL >= 20000
1585	/*
1586	**  We provide the callbacks again because global callbacks in
1587	**  sasl_client_init() are ignored if SASL has been initialized
1588	**  before, for example, by a library such as libnss-ldap.
1589	*/
1590
1591	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1592								 : "smtp",
1593				     CurHostName, NULL, NULL, callbacks, 0,
1594				     &mci->mci_conn);
1595# else /* SASL >= 20000 */
1596	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1597								 : "smtp",
1598				     CurHostName, NULL, 0, &mci->mci_conn);
1599# endif /* SASL >= 20000 */
1600	if (saslresult != SASL_OK)
1601		return EX_TEMPFAIL;
1602
1603	/* set properties */
1604	(void) memset(&ssp, '\0', sizeof(ssp));
1605
1606	/* XXX should these be options settable via .cf ? */
1607	{
1608		ssp.max_ssf = MaxSLBits;
1609		ssp.maxbufsize = MAXOUTLEN;
1610#  if 0
1611		ssp.security_flags = SASL_SEC_NOPLAINTEXT;
1612#  endif /* 0 */
1613	}
1614	saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
1615	if (saslresult != SASL_OK)
1616		return EX_TEMPFAIL;
1617
1618# if SASL >= 20000
1619	/* external security strength factor, authentication id */
1620	ssf = 0;
1621	auth_id = NULL;
1622#  if STARTTLS
1623	out = macvalue(macid("{cert_subject}"), e);
1624	if (out != NULL && *out != '\0')
1625		auth_id = out;
1626	out = macvalue(macid("{cipher_bits}"), e);
1627	if (out != NULL && *out != '\0')
1628		ssf = atoi(out);
1629#  endif /* STARTTLS */
1630	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1631	if (saslresult != SASL_OK)
1632		return EX_TEMPFAIL;
1633	saslresult = sasl_setprop(mci->mci_conn, SASL_AUTH_EXTERNAL, auth_id);
1634	if (saslresult != SASL_OK)
1635		return EX_TEMPFAIL;
1636
1637#  if NETINET || NETINET6
1638	/* set local/remote ipv4 addresses */
1639	if (mci->mci_out != NULL && (
1640#   if NETINET6
1641		CurHostAddr.sa.sa_family == AF_INET6 ||
1642#   endif /* NETINET6 */
1643		CurHostAddr.sa.sa_family == AF_INET))
1644	{
1645		SOCKADDR_LEN_T addrsize;
1646		SOCKADDR saddr_l;
1647		char localip[60], remoteip[60];
1648
1649		switch (CurHostAddr.sa.sa_family)
1650		{
1651		  case AF_INET:
1652			addrsize = sizeof(struct sockaddr_in);
1653			break;
1654#   if NETINET6
1655		  case AF_INET6:
1656			addrsize = sizeof(struct sockaddr_in6);
1657			break;
1658#   endif /* NETINET6 */
1659		  default:
1660			break;
1661		}
1662		if (iptostring(&CurHostAddr, addrsize,
1663			       remoteip, sizeof(remoteip)))
1664		{
1665			if (sasl_setprop(mci->mci_conn, SASL_IPREMOTEPORT,
1666					 remoteip) != SASL_OK)
1667				return EX_TEMPFAIL;
1668		}
1669		addrsize = sizeof(saddr_l);
1670		if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1671					      NULL),
1672				(struct sockaddr *) &saddr_l, &addrsize) == 0)
1673		{
1674			if (iptostring(&saddr_l, addrsize,
1675				       localip, sizeof(localip)))
1676			{
1677				if (sasl_setprop(mci->mci_conn,
1678						 SASL_IPLOCALPORT,
1679						 localip) != SASL_OK)
1680					return EX_TEMPFAIL;
1681			}
1682		}
1683	}
1684#  endif /* NETINET || NETINET6 */
1685
1686	/* start client side of sasl */
1687	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1688				       &client_interact,
1689				       &out, &outlen,
1690				       (const char **) &mechusing);
1691# else /* SASL >= 20000 */
1692	/* external security strength factor, authentication id */
1693	ssf.ssf = 0;
1694	ssf.auth_id = NULL;
1695#  if STARTTLS
1696	out = macvalue(macid("{cert_subject}"), e);
1697	if (out != NULL && *out != '\0')
1698		ssf.auth_id = out;
1699	out = macvalue(macid("{cipher_bits}"), e);
1700	if (out != NULL && *out != '\0')
1701		ssf.ssf = atoi(out);
1702#  endif /* STARTTLS */
1703	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1704	if (saslresult != SASL_OK)
1705		return EX_TEMPFAIL;
1706
1707#  if NETINET
1708	/* set local/remote ipv4 addresses */
1709	if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
1710	{
1711		SOCKADDR_LEN_T addrsize;
1712		struct sockaddr_in saddr_l;
1713
1714		if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
1715				 (struct sockaddr_in *) &CurHostAddr)
1716		    != SASL_OK)
1717			return EX_TEMPFAIL;
1718		addrsize = sizeof(struct sockaddr_in);
1719		if (getsockname(sm_io_getinfo(mci->mci_out, SM_IO_WHAT_FD,
1720					      NULL),
1721				(struct sockaddr *) &saddr_l, &addrsize) == 0)
1722		{
1723			if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
1724					 &saddr_l) != SASL_OK)
1725				return EX_TEMPFAIL;
1726		}
1727	}
1728#  endif /* NETINET */
1729
1730	/* start client side of sasl */
1731	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1732				       NULL, &client_interact,
1733				       &out, &outlen,
1734				       (const char **) &mechusing);
1735# endif /* SASL >= 20000 */
1736
1737	if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1738	{
1739		if (saslresult == SASL_NOMECH && LogLevel > 8)
1740		{
1741			sm_syslog(LOG_NOTICE, e->e_id,
1742				  "AUTH=client, available mechanisms do not fulfill requirements");
1743		}
1744		return EX_TEMPFAIL;
1745	}
1746
1747	/* just point current mechanism to the data in the sasl library */
1748	(*sai)[SASL_MECH] = mechusing;
1749
1750	/* send the info across the wire */
1751	if (out == NULL
1752		/* login and digest-md5 up to 1.5.28 set out="" */
1753	    || (outlen == 0 &&
1754		(sm_strcasecmp(mechusing, "LOGIN") == 0 ||
1755		 sm_strcasecmp(mechusing, "DIGEST-MD5") == 0))
1756	   )
1757	{
1758		/* no initial response */
1759		smtpmessage("AUTH %s", m, mci, mechusing);
1760	}
1761	else if (outlen == 0)
1762	{
1763		/*
1764		**  zero-length initial response, per RFC 2554 4.:
1765		**  "Unlike a zero-length client answer to a 334 reply, a zero-
1766		**  length initial response is sent as a single equals sign"
1767		*/
1768
1769		smtpmessage("AUTH %s =", m, mci, mechusing);
1770	}
1771	else
1772	{
1773		saslresult = sasl_encode64(out, outlen, in64, MAXOUTLEN, NULL);
1774		if (saslresult != SASL_OK) /* internal error */
1775		{
1776			if (LogLevel > 8)
1777				sm_syslog(LOG_ERR, e->e_id,
1778					"encode64 for AUTH failed");
1779			return EX_TEMPFAIL;
1780		}
1781		smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
1782	}
1783# if SASL < 20000
1784	sm_sasl_free(out); /* XXX only if no rpool is used */
1785# endif /* SASL < 20000 */
1786
1787	/* get the reply */
1788	smtpresult = reply(m, mci, e, TimeOuts.to_auth, getsasldata, NULL,
1789			XS_AUTH);
1790
1791	for (;;)
1792	{
1793		/* check return code from server */
1794		if (smtpresult == 235)
1795		{
1796			macdefine(&mci->mci_macro, A_TEMP, macid("{auth_type}"),
1797				  mechusing);
1798			return EX_OK;
1799		}
1800		if (smtpresult == -1)
1801			return EX_IOERR;
1802		if (REPLYTYPE(smtpresult) == 5)
1803			return EX_NOPERM;	/* ugly, but ... */
1804		if (REPLYTYPE(smtpresult) != 3)
1805		{
1806			/* should we fail deliberately, see RFC 2554 4. ? */
1807			/* smtpmessage("*", m, mci); */
1808			return EX_TEMPFAIL;
1809		}
1810
1811		saslresult = sasl_client_step(mci->mci_conn,
1812					      mci->mci_sasl_string,
1813					      mci->mci_sasl_string_len,
1814					      &client_interact,
1815					      &out, &outlen);
1816
1817		if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1818		{
1819			if (tTd(95, 5))
1820				sm_dprintf("AUTH FAIL=%s (%d)\n",
1821					sasl_errstring(saslresult, NULL, NULL),
1822					saslresult);
1823
1824			/* fail deliberately, see RFC 2554 4. */
1825			smtpmessage("*", m, mci);
1826
1827			/*
1828			**  but we should only fail for this authentication
1829			**  mechanism; how to do that?
1830			*/
1831
1832			smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1833					   getsasldata, NULL, XS_AUTH);
1834			return EX_NOPERM;
1835		}
1836
1837		if (outlen > 0)
1838		{
1839			saslresult = sasl_encode64(out, outlen, in64,
1840						   MAXOUTLEN, NULL);
1841			if (saslresult != SASL_OK)
1842			{
1843				/* give an error reply to the other side! */
1844				smtpmessage("*", m, mci);
1845				return EX_TEMPFAIL;
1846			}
1847		}
1848		else
1849			in64[0] = '\0';
1850# if SASL < 20000
1851		sm_sasl_free(out); /* XXX only if no rpool is used */
1852# endif /* SASL < 20000 */
1853		smtpmessage("%s", m, mci, in64);
1854		smtpresult = reply(m, mci, e, TimeOuts.to_auth,
1855				   getsasldata, NULL, XS_AUTH);
1856	}
1857	/* NOTREACHED */
1858}
1859/*
1860**  SMTPAUTH -- try to AUTHenticate
1861**
1862**	This will try mechanisms in the order the sasl library decided until:
1863**	- there are no more mechanisms
1864**	- a mechanism succeeds
1865**	- the sasl library fails initializing
1866**
1867**	Parameters:
1868**		m -- the mailer.
1869**		mci -- the mailer connection info.
1870**		e -- the envelope.
1871**
1872**	Returns:
1873**		EX_OK -- authentication was successful
1874**		EX_UNAVAILABLE -- authentication not possible, e.g.,
1875**			no data available.
1876**		EX_NOPERM -- authentication failed.
1877**		EX_TEMPFAIL -- temporary failure.
1878**
1879**	Notice: AuthInfo is used for all connections, hence we must
1880**		return EX_TEMPFAIL only if we really want to retry, i.e.,
1881**		iff getauth() tempfailed or getauth() was used and
1882**		authentication tempfailed.
1883*/
1884
1885int
1886smtpauth(m, mci, e)
1887	MAILER *m;
1888	MCI *mci;
1889	ENVELOPE *e;
1890{
1891	int result;
1892	int i;
1893	bool usedgetauth;
1894
1895	mci->mci_sasl_auth = false;
1896	for (i = 0; i < SASL_MECH ; i++)
1897		mci->mci_sai[i] = NULL;
1898
1899	result = getauth(mci, e, &(mci->mci_sai));
1900	if (result == EX_TEMPFAIL)
1901		return result;
1902	usedgetauth = true;
1903
1904	/* no data available: don't try to authenticate */
1905	if (result == EX_OK && mci->mci_sai[SASL_AUTHID] == NULL)
1906		return result;
1907	if (result != EX_OK)
1908	{
1909		if (SASLInfo == NULL)
1910			return EX_UNAVAILABLE;
1911
1912		/* read authinfo from file */
1913		result = readauth(SASLInfo, true, &(mci->mci_sai),
1914				  mci->mci_rpool);
1915		if (result != EX_OK)
1916			return result;
1917		usedgetauth = false;
1918	}
1919
1920	/* check whether sufficient data is available */
1921	if (mci->mci_sai[SASL_PASSWORD] == NULL ||
1922	    *(mci->mci_sai)[SASL_PASSWORD] == '\0')
1923		return EX_UNAVAILABLE;
1924	if ((mci->mci_sai[SASL_AUTHID] == NULL ||
1925	     *(mci->mci_sai)[SASL_AUTHID] == '\0') &&
1926	    (mci->mci_sai[SASL_USER] == NULL ||
1927	     *(mci->mci_sai)[SASL_USER] == '\0'))
1928		return EX_UNAVAILABLE;
1929
1930	/* set the context for the callback function to sai */
1931# if SASL >= 20000
1932	callbacks[CB_PASS_IDX].context = (void *) mci;
1933# else /* SASL >= 20000 */
1934	callbacks[CB_PASS_IDX].context = (void *) &mci->mci_sai;
1935# endif /* SASL >= 20000 */
1936	callbacks[CB_USER_IDX].context = (void *) &mci->mci_sai;
1937	callbacks[CB_AUTHNAME_IDX].context = (void *) &mci->mci_sai;
1938	callbacks[CB_GETREALM_IDX].context = (void *) &mci->mci_sai;
1939#if 0
1940	callbacks[CB_SAFESASL_IDX].context = (void *) &mci->mci_sai;
1941#endif /* 0 */
1942
1943	/* set default value for realm */
1944	if ((mci->mci_sai)[SASL_DEFREALM] == NULL)
1945		(mci->mci_sai)[SASL_DEFREALM] = sm_rpool_strdup_x(e->e_rpool,
1946							macvalue('j', CurEnv));
1947
1948	/* set default value for list of mechanism to use */
1949	if ((mci->mci_sai)[SASL_MECHLIST] == NULL ||
1950	    *(mci->mci_sai)[SASL_MECHLIST] == '\0')
1951		(mci->mci_sai)[SASL_MECHLIST] = AuthMechanisms;
1952
1953	/* create list of mechanisms to try */
1954	mci->mci_saslcap = intersect((mci->mci_sai)[SASL_MECHLIST],
1955				     mci->mci_saslcap, mci->mci_rpool);
1956
1957	/* initialize sasl client library */
1958	result = init_sasl_client();
1959	if (result != SASL_OK)
1960		return usedgetauth ? EX_TEMPFAIL : EX_UNAVAILABLE;
1961	do
1962	{
1963		result = attemptauth(m, mci, e, &(mci->mci_sai));
1964		if (result == EX_OK)
1965			mci->mci_sasl_auth = true;
1966		else if (result == EX_TEMPFAIL || result == EX_NOPERM)
1967		{
1968			mci->mci_saslcap = removemech((mci->mci_sai)[SASL_MECH],
1969						      mci->mci_saslcap,
1970						      mci->mci_rpool);
1971			if (mci->mci_saslcap == NULL ||
1972			    *(mci->mci_saslcap) == '\0')
1973				return usedgetauth ? result
1974						   : EX_UNAVAILABLE;
1975		}
1976		else
1977			return result;
1978	} while (result != EX_OK);
1979	return result;
1980}
1981#endif /* SASL */
1982
1983/*
1984**  SMTPMAILFROM -- send MAIL command
1985**
1986**	Parameters:
1987**		m -- the mailer.
1988**		mci -- the mailer connection structure.
1989**		e -- the envelope (including the sender to specify).
1990*/
1991
1992int
1993smtpmailfrom(m, mci, e)
1994	MAILER *m;
1995	MCI *mci;
1996	ENVELOPE *e;
1997{
1998	int r;
1999	char *bufp;
2000	char *bodytype;
2001	char *enhsc;
2002	char buf[MAXNAME + 1];
2003	char optbuf[MAXLINE];
2004
2005	if (tTd(18, 2))
2006		sm_dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
2007	enhsc = NULL;
2008
2009	/*
2010	**  Check if connection is gone, if so
2011	**  it's a tempfail and we use mci_errno
2012	**  for the reason.
2013	*/
2014
2015	if (mci->mci_state == MCIS_CLOSED)
2016	{
2017		errno = mci->mci_errno;
2018		return EX_TEMPFAIL;
2019	}
2020
2021	/* set up appropriate options to include */
2022	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
2023	{
2024		(void) sm_snprintf(optbuf, sizeof(optbuf), " SIZE=%ld",
2025			e->e_msgsize);
2026		bufp = &optbuf[strlen(optbuf)];
2027	}
2028	else
2029	{
2030		optbuf[0] = '\0';
2031		bufp = optbuf;
2032	}
2033
2034	bodytype = e->e_bodytype;
2035	if (bitset(MCIF_8BITMIME, mci->mci_flags))
2036	{
2037		if (bodytype == NULL &&
2038		    bitset(MM_MIME8BIT, MimeMode) &&
2039		    bitset(EF_HAS8BIT, e->e_flags) &&
2040		    !bitset(EF_DONT_MIME, e->e_flags) &&
2041		    !bitnset(M_8BITS, m->m_flags))
2042			bodytype = "8BITMIME";
2043		if (bodytype != NULL &&
2044		    SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
2045		{
2046			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2047				 " BODY=%s", bodytype);
2048			bufp += strlen(bufp);
2049		}
2050	}
2051	else if (bitnset(M_8BITS, m->m_flags) ||
2052		 !bitset(EF_HAS8BIT, e->e_flags) ||
2053		 bitset(MCIF_8BITOK, mci->mci_flags))
2054	{
2055		/* EMPTY */
2056		/* just pass it through */
2057	}
2058#if MIME8TO7
2059	else if (bitset(MM_CVTMIME, MimeMode) &&
2060		 !bitset(EF_DONT_MIME, e->e_flags) &&
2061		 (!bitset(MM_PASS8BIT, MimeMode) ||
2062		  bitset(EF_IS_MIME, e->e_flags)))
2063	{
2064		/* must convert from 8bit MIME format to 7bit encoded */
2065		mci->mci_flags |= MCIF_CVT8TO7;
2066	}
2067#endif /* MIME8TO7 */
2068	else if (!bitset(MM_PASS8BIT, MimeMode))
2069	{
2070		/* cannot just send a 8-bit version */
2071		extern char MsgBuf[];
2072
2073		usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
2074		mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
2075		return EX_DATAERR;
2076	}
2077
2078	if (bitset(MCIF_DSN, mci->mci_flags))
2079	{
2080		if (e->e_envid != NULL &&
2081		    SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
2082		{
2083			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2084				 " ENVID=%s", e->e_envid);
2085			bufp += strlen(bufp);
2086		}
2087
2088		/* RET= parameter */
2089		if (bitset(EF_RET_PARAM, e->e_flags) &&
2090		    SPACELEFT(optbuf, bufp) > 9)
2091		{
2092			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2093				 " RET=%s",
2094				 bitset(EF_NO_BODY_RETN, e->e_flags) ?
2095					"HDRS" : "FULL");
2096			bufp += strlen(bufp);
2097		}
2098	}
2099
2100	if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
2101	    SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
2102#if SASL
2103	     && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
2104#endif /* SASL */
2105	    )
2106	{
2107		(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2108			 " AUTH=%s", e->e_auth_param);
2109		bufp += strlen(bufp);
2110	}
2111
2112	/*
2113	**  17 is the max length required, we could use log() to compute
2114	**  the exact length (and check IS_DLVR_TRACE())
2115	*/
2116
2117	if (bitset(MCIF_DLVR_BY, mci->mci_flags) &&
2118	    IS_DLVR_BY(e) && SPACELEFT(optbuf, bufp) > 17)
2119	{
2120		long dby;
2121
2122		/*
2123		**  Avoid problems with delays (for R) since the check
2124		**  in deliver() whether min-deliver-time is sufficient.
2125		**  Alternatively we could pass the computed time to this
2126		**  function.
2127		*/
2128
2129		dby = e->e_deliver_by - (curtime() - e->e_ctime);
2130		if (dby <= 0 && IS_DLVR_RETURN(e))
2131			dby = mci->mci_min_by <= 0 ? 1 : mci->mci_min_by;
2132		(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2133			" BY=%ld;%c%s",
2134			dby,
2135			IS_DLVR_RETURN(e) ? 'R' : 'N',
2136			IS_DLVR_TRACE(e) ? "T" : "");
2137		bufp += strlen(bufp);
2138	}
2139
2140	/*
2141	**  Send the MAIL command.
2142	**	Designates the sender.
2143	*/
2144
2145	mci->mci_state = MCIS_MAIL;
2146
2147	if (bitset(EF_RESPONSE, e->e_flags) &&
2148	    !bitnset(M_NO_NULL_FROM, m->m_flags))
2149		buf[0] = '\0';
2150	else
2151		expand("\201g", buf, sizeof(buf), e);
2152	if (buf[0] == '<')
2153	{
2154		/* strip off <angle brackets> (put back on below) */
2155		bufp = &buf[strlen(buf) - 1];
2156		if (*bufp == '>')
2157			*bufp = '\0';
2158		bufp = &buf[1];
2159	}
2160	else
2161		bufp = buf;
2162	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
2163	    !bitnset(M_FROMPATH, m->m_flags))
2164	{
2165		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
2166	}
2167	else
2168	{
2169		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
2170			    *bufp == '@' ? ',' : ':', bufp, optbuf);
2171	}
2172	SmtpPhase = mci->mci_phase = "client MAIL";
2173	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2174			CurHostName, mci->mci_phase);
2175	r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc, XS_DEFAULT);
2176	if (r < 0)
2177	{
2178		/* communications failure */
2179		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
2180		return EX_TEMPFAIL;
2181	}
2182	else if (r == SMTPCLOSING)
2183	{
2184		/* service shutting down: handled by reply() */
2185		return EX_TEMPFAIL;
2186	}
2187	else if (REPLYTYPE(r) == 4)
2188	{
2189		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
2190			    SmtpReplyBuffer);
2191		return EX_TEMPFAIL;
2192	}
2193	else if (REPLYTYPE(r) == 2)
2194	{
2195		return EX_OK;
2196	}
2197	else if (r == 501)
2198	{
2199		/* syntax error in arguments */
2200		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
2201			    SmtpReplyBuffer);
2202		return EX_DATAERR;
2203	}
2204	else if (r == 553)
2205	{
2206		/* mailbox name not allowed */
2207		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
2208			    SmtpReplyBuffer);
2209		return EX_DATAERR;
2210	}
2211	else if (r == 552)
2212	{
2213		/* exceeded storage allocation */
2214		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
2215			    SmtpReplyBuffer);
2216		if (bitset(MCIF_SIZE, mci->mci_flags))
2217			e->e_flags |= EF_NO_BODY_RETN;
2218		return EX_UNAVAILABLE;
2219	}
2220	else if (REPLYTYPE(r) == 5)
2221	{
2222		/* unknown error */
2223		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
2224			    SmtpReplyBuffer);
2225		return EX_UNAVAILABLE;
2226	}
2227
2228	if (LogLevel > 1)
2229	{
2230		sm_syslog(LOG_CRIT, e->e_id,
2231			  "%.100s: SMTP MAIL protocol error: %s",
2232			  CurHostName,
2233			  shortenstring(SmtpReplyBuffer, 403));
2234	}
2235
2236	/* protocol error -- close up */
2237	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2238		    SmtpReplyBuffer);
2239	smtpquit(m, mci, e);
2240	return EX_PROTOCOL;
2241}
2242/*
2243**  SMTPRCPT -- designate recipient.
2244**
2245**	Parameters:
2246**		to -- address of recipient.
2247**		m -- the mailer we are sending to.
2248**		mci -- the connection info for this transaction.
2249**		e -- the envelope for this transaction.
2250**
2251**	Returns:
2252**		exit status corresponding to recipient status.
2253**
2254**	Side Effects:
2255**		Sends the mail via SMTP.
2256*/
2257
2258int
2259smtprcpt(to, m, mci, e, ctladdr, xstart)
2260	ADDRESS *to;
2261	register MAILER *m;
2262	MCI *mci;
2263	ENVELOPE *e;
2264	ADDRESS *ctladdr;
2265	time_t xstart;
2266{
2267	char *bufp;
2268	char optbuf[MAXLINE];
2269
2270#if PIPELINING
2271	/*
2272	**  If there is status waiting from the other end, read it.
2273	**  This should normally happen because of SMTP pipelining.
2274	*/
2275
2276	while (mci->mci_nextaddr != NULL &&
2277	       sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2278	{
2279		int r;
2280
2281		r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2282		if (r != EX_OK)
2283		{
2284			markfailure(e, mci->mci_nextaddr, mci, r, false);
2285			giveresponse(r, mci->mci_nextaddr->q_status,  m, mci,
2286				     ctladdr, xstart, e, to);
2287		}
2288		mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2289	}
2290#endif /* PIPELINING */
2291
2292	/*
2293	**  Check if connection is gone, if so
2294	**  it's a tempfail and we use mci_errno
2295	**  for the reason.
2296	*/
2297
2298	if (mci->mci_state == MCIS_CLOSED)
2299	{
2300		errno = mci->mci_errno;
2301		return EX_TEMPFAIL;
2302	}
2303
2304	optbuf[0] = '\0';
2305	bufp = optbuf;
2306
2307	/*
2308	**  Warning: in the following it is assumed that the free space
2309	**  in bufp is sizeof(optbuf)
2310	*/
2311
2312	if (bitset(MCIF_DSN, mci->mci_flags))
2313	{
2314		if (IS_DLVR_NOTIFY(e) &&
2315		    !bitset(MCIF_DLVR_BY, mci->mci_flags))
2316		{
2317			/* RFC 2852: 4.1.4.2 */
2318			if (!bitset(QHASNOTIFY, to->q_flags))
2319				to->q_flags |= QPINGONFAILURE|QPINGONDELAY|QHASNOTIFY;
2320			else if (bitset(QPINGONSUCCESS, to->q_flags) ||
2321				 bitset(QPINGONFAILURE, to->q_flags) ||
2322				 bitset(QPINGONDELAY, to->q_flags))
2323				to->q_flags |= QPINGONDELAY;
2324		}
2325
2326		/* NOTIFY= parameter */
2327		if (bitset(QHASNOTIFY, to->q_flags) &&
2328		    bitset(QPRIMARY, to->q_flags) &&
2329		    !bitnset(M_LOCALMAILER, m->m_flags))
2330		{
2331			bool firstone = true;
2332
2333			(void) sm_strlcat(bufp, " NOTIFY=", sizeof(optbuf));
2334			if (bitset(QPINGONSUCCESS, to->q_flags))
2335			{
2336				(void) sm_strlcat(bufp, "SUCCESS", sizeof(optbuf));
2337				firstone = false;
2338			}
2339			if (bitset(QPINGONFAILURE, to->q_flags))
2340			{
2341				if (!firstone)
2342					(void) sm_strlcat(bufp, ",",
2343						       sizeof(optbuf));
2344				(void) sm_strlcat(bufp, "FAILURE", sizeof(optbuf));
2345				firstone = false;
2346			}
2347			if (bitset(QPINGONDELAY, to->q_flags))
2348			{
2349				if (!firstone)
2350					(void) sm_strlcat(bufp, ",",
2351						       sizeof(optbuf));
2352				(void) sm_strlcat(bufp, "DELAY", sizeof(optbuf));
2353				firstone = false;
2354			}
2355			if (firstone)
2356				(void) sm_strlcat(bufp, "NEVER", sizeof(optbuf));
2357			bufp += strlen(bufp);
2358		}
2359
2360		/* ORCPT= parameter */
2361		if (to->q_orcpt != NULL &&
2362		    SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
2363		{
2364			(void) sm_snprintf(bufp, SPACELEFT(optbuf, bufp),
2365				 " ORCPT=%s", to->q_orcpt);
2366			bufp += strlen(bufp);
2367		}
2368	}
2369
2370	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
2371	mci->mci_state = MCIS_RCPT;
2372
2373	SmtpPhase = mci->mci_phase = "client RCPT";
2374	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2375			CurHostName, mci->mci_phase);
2376
2377#if PIPELINING
2378	/*
2379	**  If running SMTP pipelining, we will pick up status later
2380	*/
2381
2382	if (bitset(MCIF_PIPELINED, mci->mci_flags))
2383		return EX_OK;
2384#endif /* PIPELINING */
2385
2386	return smtprcptstat(to, m, mci, e);
2387}
2388/*
2389**  SMTPRCPTSTAT -- get recipient status
2390**
2391**	This is only called during SMTP pipelining
2392**
2393**	Parameters:
2394**		to -- address of recipient.
2395**		m -- mailer being sent to.
2396**		mci -- the mailer connection information.
2397**		e -- the envelope for this message.
2398**
2399**	Returns:
2400**		EX_* -- protocol status
2401*/
2402
2403static int
2404smtprcptstat(to, m, mci, e)
2405	ADDRESS *to;
2406	MAILER *m;
2407	register MCI *mci;
2408	register ENVELOPE *e;
2409{
2410	int r;
2411	int save_errno;
2412	char *enhsc;
2413
2414	/*
2415	**  Check if connection is gone, if so
2416	**  it's a tempfail and we use mci_errno
2417	**  for the reason.
2418	*/
2419
2420	if (mci->mci_state == MCIS_CLOSED)
2421	{
2422		errno = mci->mci_errno;
2423		return EX_TEMPFAIL;
2424	}
2425
2426	enhsc = NULL;
2427	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc, XS_DEFAULT);
2428	save_errno = errno;
2429	to->q_rstatus = sm_rpool_strdup_x(e->e_rpool, SmtpReplyBuffer);
2430	to->q_status = ENHSCN_RPOOL(enhsc, smtptodsn(r), e->e_rpool);
2431	if (!bitnset(M_LMTP, m->m_flags))
2432		to->q_statmta = mci->mci_host;
2433	if (r < 0 || REPLYTYPE(r) == 4)
2434	{
2435		mci->mci_retryrcpt = true;
2436		errno = save_errno;
2437		return EX_TEMPFAIL;
2438	}
2439	else if (REPLYTYPE(r) == 2)
2440	{
2441		char *t;
2442
2443		if ((t = mci->mci_tolist) != NULL)
2444		{
2445			char *p;
2446
2447			*t++ = ',';
2448			for (p = to->q_paddr; *p != '\0'; *t++ = *p++)
2449				continue;
2450			*t = '\0';
2451			mci->mci_tolist = t;
2452		}
2453#if PIPELINING
2454		mci->mci_okrcpts++;
2455#endif /* PIPELINING */
2456		return EX_OK;
2457	}
2458	else if (r == 550)
2459	{
2460		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.1", e->e_rpool);
2461		return EX_NOUSER;
2462	}
2463	else if (r == 551)
2464	{
2465		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.6", e->e_rpool);
2466		return EX_NOUSER;
2467	}
2468	else if (r == 553)
2469	{
2470		to->q_status = ENHSCN_RPOOL(enhsc, "5.1.3", e->e_rpool);
2471		return EX_NOUSER;
2472	}
2473	else if (REPLYTYPE(r) == 5)
2474	{
2475		return EX_UNAVAILABLE;
2476	}
2477
2478	if (LogLevel > 1)
2479	{
2480		sm_syslog(LOG_CRIT, e->e_id,
2481			  "%.100s: SMTP RCPT protocol error: %s",
2482			  CurHostName,
2483			  shortenstring(SmtpReplyBuffer, 403));
2484	}
2485
2486	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2487		    SmtpReplyBuffer);
2488	return EX_PROTOCOL;
2489}
2490/*
2491**  SMTPDATA -- send the data and clean up the transaction.
2492**
2493**	Parameters:
2494**		m -- mailer being sent to.
2495**		mci -- the mailer connection information.
2496**		e -- the envelope for this message.
2497**
2498**	Returns:
2499**		exit status corresponding to DATA command.
2500*/
2501
2502int
2503smtpdata(m, mci, e, ctladdr, xstart)
2504	MAILER *m;
2505	register MCI *mci;
2506	register ENVELOPE *e;
2507	ADDRESS *ctladdr;
2508	time_t xstart;
2509{
2510	register int r;
2511	int rstat;
2512	int xstat;
2513	int timeout;
2514	char *enhsc;
2515
2516	/*
2517	**  Check if connection is gone, if so
2518	**  it's a tempfail and we use mci_errno
2519	**  for the reason.
2520	*/
2521
2522	if (mci->mci_state == MCIS_CLOSED)
2523	{
2524		errno = mci->mci_errno;
2525		return EX_TEMPFAIL;
2526	}
2527
2528	enhsc = NULL;
2529
2530	/*
2531	**  Send the data.
2532	**	First send the command and check that it is ok.
2533	**	Then send the data (if there are valid recipients).
2534	**	Follow it up with a dot to terminate.
2535	**	Finally get the results of the transaction.
2536	*/
2537
2538	/* send the command and check ok to proceed */
2539	smtpmessage("DATA", m, mci);
2540
2541#if PIPELINING
2542	if (mci->mci_nextaddr != NULL)
2543	{
2544		char *oldto = e->e_to;
2545
2546		/* pick up any pending RCPT responses for SMTP pipelining */
2547		while (mci->mci_nextaddr != NULL)
2548		{
2549			int r;
2550
2551			e->e_to = mci->mci_nextaddr->q_paddr;
2552			r = smtprcptstat(mci->mci_nextaddr, m, mci, e);
2553			if (r != EX_OK)
2554			{
2555				markfailure(e, mci->mci_nextaddr, mci, r,
2556					    false);
2557				giveresponse(r, mci->mci_nextaddr->q_status, m,
2558					     mci, ctladdr, xstart, e,
2559					     mci->mci_nextaddr);
2560				if (r == EX_TEMPFAIL)
2561					mci->mci_nextaddr->q_state = QS_RETRY;
2562			}
2563			mci->mci_nextaddr = mci->mci_nextaddr->q_pchain;
2564		}
2565		e->e_to = oldto;
2566
2567		/*
2568		**  Connection might be closed in response to a RCPT command,
2569		**  i.e., the server responded with 421. In that case (at
2570		**  least) one RCPT has a temporary failure, hence we don't
2571		**  need to check mci_okrcpts (as it is done below) to figure
2572		**  out which error to return.
2573		*/
2574
2575		if (mci->mci_state == MCIS_CLOSED)
2576		{
2577			errno = mci->mci_errno;
2578			return EX_TEMPFAIL;
2579		}
2580	}
2581#endif /* PIPELINING */
2582
2583	/* now proceed with DATA phase */
2584	SmtpPhase = mci->mci_phase = "client DATA 354";
2585	mci->mci_state = MCIS_DATA;
2586	sm_setproctitle(true, e, "%s %s: %s",
2587			qid_printname(e), CurHostName, mci->mci_phase);
2588	r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc, XS_DEFAULT);
2589	if (r < 0 || REPLYTYPE(r) == 4)
2590	{
2591		if (r >= 0)
2592			smtpquit(m, mci, e);
2593		errno = mci->mci_errno;
2594		return EX_TEMPFAIL;
2595	}
2596	else if (REPLYTYPE(r) == 5)
2597	{
2598		smtprset(m, mci, e);
2599#if PIPELINING
2600		if (mci->mci_okrcpts <= 0)
2601			return mci->mci_retryrcpt ? EX_TEMPFAIL
2602						  : EX_UNAVAILABLE;
2603#endif /* PIPELINING */
2604		return EX_UNAVAILABLE;
2605	}
2606	else if (REPLYTYPE(r) != 3)
2607	{
2608		if (LogLevel > 1)
2609		{
2610			sm_syslog(LOG_CRIT, e->e_id,
2611				  "%.100s: SMTP DATA-1 protocol error: %s",
2612				  CurHostName,
2613				  shortenstring(SmtpReplyBuffer, 403));
2614		}
2615		smtprset(m, mci, e);
2616		mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
2617			    SmtpReplyBuffer);
2618#if PIPELINING
2619		if (mci->mci_okrcpts <= 0)
2620			return mci->mci_retryrcpt ? EX_TEMPFAIL
2621						  : EX_PROTOCOL;
2622#endif /* PIPELINING */
2623		return EX_PROTOCOL;
2624	}
2625
2626#if PIPELINING
2627	if (mci->mci_okrcpts > 0)
2628	{
2629#endif /* PIPELINING */
2630
2631	/*
2632	**  Set timeout around data writes.  Make it at least large
2633	**  enough for DNS timeouts on all recipients plus some fudge
2634	**  factor.  The main thing is that it should not be infinite.
2635	*/
2636
2637	if (tTd(18, 101))
2638	{
2639		/* simulate a DATA timeout */
2640		timeout = 10;
2641	}
2642	else
2643		timeout = DATA_PROGRESS_TIMEOUT * 1000;
2644	sm_io_setinfo(mci->mci_out, SM_IO_WHAT_TIMEOUT, &timeout);
2645
2646
2647	/*
2648	**  Output the actual message.
2649	*/
2650
2651	if (!(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER))
2652		goto writeerr;
2653
2654	if (tTd(18, 101))
2655	{
2656		/* simulate a DATA timeout */
2657		(void) sleep(2);
2658	}
2659
2660	if (!(*e->e_putbody)(mci, e, NULL))
2661		goto writeerr;
2662
2663	/*
2664	**  Cleanup after sending message.
2665	*/
2666
2667
2668#if PIPELINING
2669	}
2670#endif /* PIPELINING */
2671
2672#if _FFR_CATCH_BROKEN_MTAS
2673	if (sm_io_getinfo(mci->mci_in, SM_IO_IS_READABLE, NULL) > 0)
2674	{
2675		/* terminate the message */
2676		(void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, ".%s",
2677				     m->m_eol);
2678		if (TrafficLogFile != NULL)
2679			(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2680					     "%05d >>> .\n", (int) CurrentPid);
2681		if (Verbose)
2682			nmessage(">>> .");
2683
2684		sm_syslog(LOG_CRIT, e->e_id,
2685			  "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
2686			  CurHostName);
2687		mci->mci_errno = EIO;
2688		mci->mci_state = MCIS_ERROR;
2689		mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
2690		smtpquit(m, mci, e);
2691		return EX_PROTOCOL;
2692	}
2693#endif /* _FFR_CATCH_BROKEN_MTAS */
2694
2695	if (sm_io_error(mci->mci_out))
2696	{
2697		/* error during processing -- don't send the dot */
2698		mci->mci_errno = EIO;
2699		mci->mci_state = MCIS_ERROR;
2700		mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
2701		smtpquit(m, mci, e);
2702		return EX_IOERR;
2703	}
2704
2705	/* terminate the message */
2706	if (sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s.%s",
2707			bitset(MCIF_INLONGLINE, mci->mci_flags) ? m->m_eol : "",
2708			m->m_eol) == SM_IO_EOF)
2709		goto writeerr;
2710	if (TrafficLogFile != NULL)
2711		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
2712				     "%05d >>> .\n", (int) CurrentPid);
2713	if (Verbose)
2714		nmessage(">>> .");
2715
2716	/* check for the results of the transaction */
2717	SmtpPhase = mci->mci_phase = "client DATA status";
2718	sm_setproctitle(true, e, "%s %s: %s", qid_printname(e),
2719			CurHostName, mci->mci_phase);
2720	if (bitnset(M_LMTP, m->m_flags))
2721		return EX_OK;
2722	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DEFAULT);
2723	if (r < 0)
2724		return EX_TEMPFAIL;
2725	if (mci->mci_state == MCIS_DATA)
2726		mci->mci_state = MCIS_OPEN;
2727	xstat = EX_NOTSTICKY;
2728	if (r == 452)
2729		rstat = EX_TEMPFAIL;
2730	else if (REPLYTYPE(r) == 4)
2731		rstat = xstat = EX_TEMPFAIL;
2732	else if (REPLYTYPE(r) == 2)
2733		rstat = xstat = EX_OK;
2734	else if (REPLYCLASS(r) != 5)
2735		rstat = xstat = EX_PROTOCOL;
2736	else if (REPLYTYPE(r) == 5)
2737		rstat = EX_UNAVAILABLE;
2738	else
2739		rstat = EX_PROTOCOL;
2740	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
2741		    SmtpReplyBuffer);
2742	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2743	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2744		r += 5;
2745	else
2746		r = 4;
2747	e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[r]);
2748	SmtpPhase = mci->mci_phase = "idle";
2749	sm_setproctitle(true, e, "%s: %s", CurHostName, mci->mci_phase);
2750	if (rstat != EX_PROTOCOL)
2751		return rstat;
2752	if (LogLevel > 1)
2753	{
2754		sm_syslog(LOG_CRIT, e->e_id,
2755			  "%.100s: SMTP DATA-2 protocol error: %s",
2756			  CurHostName,
2757			  shortenstring(SmtpReplyBuffer, 403));
2758	}
2759	return rstat;
2760
2761  writeerr:
2762	mci->mci_errno = errno;
2763	mci->mci_state = MCIS_ERROR;
2764	mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
2765
2766	/*
2767	**  If putbody() couldn't finish due to a timeout,
2768	**  rewind it here in the timeout handler.  See
2769	**  comments at the end of putbody() for reasoning.
2770	*/
2771
2772	if (e->e_dfp != NULL)
2773		(void) bfrewind(e->e_dfp);
2774
2775	errno = mci->mci_errno;
2776	syserr("451 4.4.1 timeout writing message to %s", CurHostName);
2777	smtpquit(m, mci, e);
2778	return EX_TEMPFAIL;
2779}
2780
2781/*
2782**  SMTPGETSTAT -- get status code from DATA in LMTP
2783**
2784**	Parameters:
2785**		m -- the mailer to which we are sending the message.
2786**		mci -- the mailer connection structure.
2787**		e -- the current envelope.
2788**
2789**	Returns:
2790**		The exit status corresponding to the reply code.
2791*/
2792
2793int
2794smtpgetstat(m, mci, e)
2795	MAILER *m;
2796	MCI *mci;
2797	ENVELOPE *e;
2798{
2799	int r;
2800	int off;
2801	int status, xstat;
2802	char *enhsc;
2803
2804	enhsc = NULL;
2805
2806	/* check for the results of the transaction */
2807	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc, XS_DEFAULT);
2808	if (r < 0)
2809		return EX_TEMPFAIL;
2810	xstat = EX_NOTSTICKY;
2811	if (REPLYTYPE(r) == 4)
2812		status = EX_TEMPFAIL;
2813	else if (REPLYTYPE(r) == 2)
2814		status = xstat = EX_OK;
2815	else if (REPLYCLASS(r) != 5)
2816		status = xstat = EX_PROTOCOL;
2817	else if (REPLYTYPE(r) == 5)
2818		status = EX_UNAVAILABLE;
2819	else
2820		status = EX_PROTOCOL;
2821	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2822	    (off = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2823		off += 5;
2824	else
2825		off = 4;
2826	e->e_statmsg = sm_rpool_strdup_x(e->e_rpool, &SmtpReplyBuffer[off]);
2827	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)), SmtpReplyBuffer);
2828	if (LogLevel > 1 && status == EX_PROTOCOL)
2829	{
2830		sm_syslog(LOG_CRIT, e->e_id,
2831			  "%.100s: SMTP DATA-3 protocol error: %s",
2832			  CurHostName,
2833			  shortenstring(SmtpReplyBuffer, 403));
2834	}
2835	return status;
2836}
2837/*
2838**  SMTPQUIT -- close the SMTP connection.
2839**
2840**	Parameters:
2841**		m -- a pointer to the mailer.
2842**		mci -- the mailer connection information.
2843**		e -- the current envelope.
2844**
2845**	Returns:
2846**		none.
2847**
2848**	Side Effects:
2849**		sends the final protocol and closes the connection.
2850*/
2851
2852void
2853smtpquit(m, mci, e)
2854	register MAILER *m;
2855	register MCI *mci;
2856	ENVELOPE *e;
2857{
2858	bool oldSuprErrs = SuprErrs;
2859	int rcode;
2860	char *oldcurhost;
2861
2862	if (mci->mci_state == MCIS_CLOSED)
2863	{
2864		mci_close(mci, "smtpquit:1");
2865		return;
2866	}
2867
2868	oldcurhost = CurHostName;
2869	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2870	if (CurHostName == NULL)
2871		CurHostName = MyHostName;
2872
2873#if PIPELINING
2874	mci->mci_okrcpts = 0;
2875#endif /* PIPELINING */
2876
2877	/*
2878	**	Suppress errors here -- we may be processing a different
2879	**	job when we do the quit connection, and we don't want the
2880	**	new job to be penalized for something that isn't it's
2881	**	problem.
2882	*/
2883
2884	SuprErrs = true;
2885
2886	/* send the quit message if we haven't gotten I/O error */
2887	if (mci->mci_state != MCIS_ERROR &&
2888	    mci->mci_state != MCIS_QUITING)
2889	{
2890		SmtpPhase = "client QUIT";
2891		mci->mci_state = MCIS_QUITING;
2892		smtpmessage("QUIT", m, mci);
2893		(void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL,
2894				XS_DEFAULT);
2895		SuprErrs = oldSuprErrs;
2896		if (mci->mci_state == MCIS_CLOSED)
2897			goto end;
2898	}
2899
2900	/* now actually close the connection and pick up the zombie */
2901	rcode = endmailer(mci, e, NULL);
2902	if (rcode != EX_OK)
2903	{
2904		char *mailer = NULL;
2905
2906		if (mci->mci_mailer != NULL &&
2907		    mci->mci_mailer->m_name != NULL)
2908			mailer = mci->mci_mailer->m_name;
2909
2910		/* look for naughty mailers */
2911		sm_syslog(LOG_ERR, e->e_id,
2912			  "smtpquit: mailer%s%s exited with exit value %d",
2913			  mailer == NULL ? "" : " ",
2914			  mailer == NULL ? "" : mailer,
2915			  rcode);
2916	}
2917
2918	SuprErrs = oldSuprErrs;
2919
2920  end:
2921	CurHostName = oldcurhost;
2922	return;
2923}
2924/*
2925**  SMTPRSET -- send a RSET (reset) command
2926**
2927**	Parameters:
2928**		m -- a pointer to the mailer.
2929**		mci -- the mailer connection information.
2930**		e -- the current envelope.
2931**
2932**	Returns:
2933**		none.
2934**
2935**	Side Effects:
2936**		closes the connection if there is no reply to RSET.
2937*/
2938
2939void
2940smtprset(m, mci, e)
2941	register MAILER *m;
2942	register MCI *mci;
2943	ENVELOPE *e;
2944{
2945	int r;
2946
2947	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2948	if (CurHostName == NULL)
2949		CurHostName = MyHostName;
2950
2951#if PIPELINING
2952	mci->mci_okrcpts = 0;
2953#endif /* PIPELINING */
2954
2955	/*
2956	**  Check if connection is gone, if so
2957	**  it's a tempfail and we use mci_errno
2958	**  for the reason.
2959	*/
2960
2961	if (mci->mci_state == MCIS_CLOSED)
2962	{
2963		errno = mci->mci_errno;
2964		return;
2965	}
2966
2967	SmtpPhase = "client RSET";
2968	smtpmessage("RSET", m, mci);
2969	r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL, XS_DEFAULT);
2970	if (r < 0)
2971		return;
2972
2973	/*
2974	**  Any response is deemed to be acceptable.
2975	**  The standard does not state the proper action
2976	**  to take when a value other than 250 is received.
2977	**
2978	**  However, if 421 is returned for the RSET, leave
2979	**  mci_state alone (MCIS_SSD can be set in reply()
2980	**  and MCIS_CLOSED can be set in smtpquit() if
2981	**  reply() gets a 421 and calls smtpquit()).
2982	*/
2983
2984	if (mci->mci_state != MCIS_SSD && mci->mci_state != MCIS_CLOSED)
2985		mci->mci_state = MCIS_OPEN;
2986	else if (mci->mci_exitstat == EX_OK)
2987		mci_setstat(mci, EX_TEMPFAIL, "4.5.0", NULL);
2988}
2989/*
2990**  SMTPPROBE -- check the connection state
2991**
2992**	Parameters:
2993**		mci -- the mailer connection information.
2994**
2995**	Returns:
2996**		none.
2997**
2998**	Side Effects:
2999**		closes the connection if there is no reply to RSET.
3000*/
3001
3002int
3003smtpprobe(mci)
3004	register MCI *mci;
3005{
3006	int r;
3007	MAILER *m = mci->mci_mailer;
3008	ENVELOPE *e;
3009	extern ENVELOPE BlankEnvelope;
3010
3011	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
3012	if (CurHostName == NULL)
3013		CurHostName = MyHostName;
3014
3015	e = &BlankEnvelope;
3016	SmtpPhase = "client probe";
3017	smtpmessage("RSET", m, mci);
3018	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL, XS_DEFAULT);
3019	if (REPLYTYPE(r) != 2)
3020		smtpquit(m, mci, e);
3021	return r;
3022}
3023/*
3024**  REPLY -- read arpanet reply
3025**
3026**	Parameters:
3027**		m -- the mailer we are reading the reply from.
3028**		mci -- the mailer connection info structure.
3029**		e -- the current envelope.
3030**		timeout -- the timeout for reads.
3031**		pfunc -- processing function called on each line of response.
3032**			If null, no special processing is done.
3033**		enhstat -- optional, returns enhanced error code string (if set)
3034**		rtype -- type of SmtpMsgBuffer: does it contains secret data?
3035**
3036**	Returns:
3037**		reply code it reads.
3038**
3039**	Side Effects:
3040**		flushes the mail file.
3041*/
3042
3043int
3044reply(m, mci, e, timeout, pfunc, enhstat, rtype)
3045	MAILER *m;
3046	MCI *mci;
3047	ENVELOPE *e;
3048	time_t timeout;
3049	void (*pfunc) __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
3050	char **enhstat;
3051	int rtype;
3052{
3053	register char *bufp;
3054	register int r;
3055	bool firstline = true;
3056	char junkbuf[MAXLINE];
3057	static char enhstatcode[ENHSCLEN];
3058	int save_errno;
3059
3060	/*
3061	**  Flush the output before reading response.
3062	**
3063	**	For SMTP pipelining, it would be better if we didn't do
3064	**	this if there was already data waiting to be read.  But
3065	**	to do it properly means pushing it to the I/O library,
3066	**	since it really needs to be done below the buffer layer.
3067	*/
3068
3069	if (mci->mci_out != NULL)
3070		(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3071
3072	if (tTd(18, 1))
3073		sm_dprintf("reply\n");
3074
3075	/*
3076	**  Read the input line, being careful not to hang.
3077	*/
3078
3079	bufp = SmtpReplyBuffer;
3080	set_tls_rd_tmo(timeout);
3081	for (;;)
3082	{
3083		register char *p;
3084
3085		/* actually do the read */
3086		if (e->e_xfp != NULL)	/* for debugging */
3087			(void) sm_io_flush(e->e_xfp, SM_TIME_DEFAULT);
3088
3089		/* if we are in the process of closing just give the code */
3090		if (mci->mci_state == MCIS_CLOSED)
3091			return SMTPCLOSING;
3092
3093		/* don't try to read from a non-existent fd */
3094		if (mci->mci_in == NULL)
3095		{
3096			if (mci->mci_errno == 0)
3097				mci->mci_errno = EBADF;
3098
3099			/* errors on QUIT should be ignored */
3100			if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3101			{
3102				errno = mci->mci_errno;
3103				mci_close(mci, "reply:1");
3104				return -1;
3105			}
3106			mci->mci_state = MCIS_ERROR;
3107			smtpquit(m, mci, e);
3108			errno = mci->mci_errno;
3109			return -1;
3110		}
3111
3112		if (mci->mci_out != NULL)
3113			(void) sm_io_flush(mci->mci_out, SM_TIME_DEFAULT);
3114
3115		/* get the line from the other side */
3116		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
3117		save_errno = errno;
3118		mci->mci_lastuse = curtime();
3119
3120		if (p == NULL)
3121		{
3122			bool oldholderrs;
3123			extern char MsgBuf[];
3124
3125			/* errors on QUIT should be ignored */
3126			if (strncmp(SmtpMsgBuffer, "QUIT", 4) == 0)
3127			{
3128				mci_close(mci, "reply:2");
3129				return -1;
3130			}
3131
3132			/* if the remote end closed early, fake an error */
3133			errno = save_errno;
3134			if (errno == 0)
3135			{
3136				(void) sm_snprintf(SmtpReplyBuffer,
3137						   sizeof(SmtpReplyBuffer),
3138						   "421 4.4.1 Connection reset by %s",
3139						   CURHOSTNAME);
3140#ifdef ECONNRESET
3141				errno = ECONNRESET;
3142#else /* ECONNRESET */
3143				errno = EPIPE;
3144#endif /* ECONNRESET */
3145			}
3146
3147			mci->mci_errno = errno;
3148			oldholderrs = HoldErrs;
3149			HoldErrs = true;
3150			usrerr("451 4.4.1 reply: read error from %s",
3151			       CURHOSTNAME);
3152			mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
3153
3154			/* if debugging, pause so we can see state */
3155			if (tTd(18, 100))
3156				(void) pause();
3157			mci->mci_state = MCIS_ERROR;
3158			smtpquit(m, mci, e);
3159#if XDEBUG
3160			{
3161				char wbuf[MAXLINE];
3162
3163				p = wbuf;
3164				if (e->e_to != NULL)
3165				{
3166					(void) sm_snprintf(p,
3167							   SPACELEFT(wbuf, p),
3168							   "%s... ",
3169							   shortenstring(e->e_to, MAXSHORTSTR));
3170					p += strlen(p);
3171				}
3172				(void) sm_snprintf(p, SPACELEFT(wbuf, p),
3173						   "reply(%.100s) during %s",
3174						   CURHOSTNAME, SmtpPhase);
3175				checkfd012(wbuf);
3176			}
3177#endif /* XDEBUG */
3178			HoldErrs = oldholderrs;
3179			errno = save_errno;
3180			return -1;
3181		}
3182		fixcrlf(bufp, true);
3183
3184		/* EHLO failure is not a real error */
3185		if (e->e_xfp != NULL && (bufp[0] == '4' ||
3186		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
3187		{
3188			/* serious error -- log the previous command */
3189			if (SmtpNeedIntro)
3190			{
3191				/* inform user who we are chatting with */
3192				(void) sm_io_fprintf(CurEnv->e_xfp,
3193						     SM_TIME_DEFAULT,
3194						     "... while talking to %s:\n",
3195						     CURHOSTNAME);
3196				SmtpNeedIntro = false;
3197			}
3198			if (SmtpMsgBuffer[0] != '\0')
3199			{
3200				(void) sm_io_fprintf(e->e_xfp,
3201					SM_TIME_DEFAULT,
3202					">>> %s\n",
3203					(rtype == XS_STARTTLS)
3204					? "STARTTLS dialogue"
3205					: ((rtype == XS_AUTH)
3206					   ? "AUTH dialogue"
3207					   : SmtpMsgBuffer));
3208				SmtpMsgBuffer[0] = '\0';
3209			}
3210
3211			/* now log the message as from the other side */
3212			(void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT,
3213					     "<<< %s\n", bufp);
3214		}
3215
3216		/* display the input for verbose mode */
3217		if (Verbose)
3218			nmessage("050 %s", bufp);
3219
3220		/* ignore improperly formatted input */
3221		if (!ISSMTPREPLY(bufp))
3222			continue;
3223
3224		if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
3225		    enhstat != NULL &&
3226		    extenhsc(bufp + 4, ' ', enhstatcode) > 0)
3227			*enhstat = enhstatcode;
3228
3229		/* process the line */
3230		if (pfunc != NULL)
3231			(*pfunc)(bufp, firstline, m, mci, e);
3232
3233		firstline = false;
3234
3235		/* decode the reply code */
3236		r = atoi(bufp);
3237
3238		/* extra semantics: 0xx codes are "informational" */
3239		if (r < 100)
3240			continue;
3241
3242		/* if no continuation lines, return this line */
3243		if (bufp[3] != '-')
3244			break;
3245
3246		/* first line of real reply -- ignore rest */
3247		bufp = junkbuf;
3248	}
3249
3250	/*
3251	**  Now look at SmtpReplyBuffer -- only care about the first
3252	**  line of the response from here on out.
3253	*/
3254
3255	/* save temporary failure messages for posterity */
3256	if (SmtpReplyBuffer[0] == '4')
3257		(void) sm_strlcpy(SmtpError, SmtpReplyBuffer, sizeof(SmtpError));
3258
3259	/* reply code 421 is "Service Shutting Down" */
3260	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD &&
3261	    mci->mci_state != MCIS_QUITING)
3262	{
3263		/* send the quit protocol */
3264		mci->mci_state = MCIS_SSD;
3265		smtpquit(m, mci, e);
3266	}
3267
3268	return r;
3269}
3270/*
3271**  SMTPMESSAGE -- send message to server
3272**
3273**	Parameters:
3274**		f -- format
3275**		m -- the mailer to control formatting.
3276**		a, b, c -- parameters
3277**
3278**	Returns:
3279**		none.
3280**
3281**	Side Effects:
3282**		writes message to mci->mci_out.
3283*/
3284
3285/*VARARGS1*/
3286void
3287#ifdef __STDC__
3288smtpmessage(char *f, MAILER *m, MCI *mci, ...)
3289#else /* __STDC__ */
3290smtpmessage(f, m, mci, va_alist)
3291	char *f;
3292	MAILER *m;
3293	MCI *mci;
3294	va_dcl
3295#endif /* __STDC__ */
3296{
3297	SM_VA_LOCAL_DECL
3298
3299	SM_VA_START(ap, mci);
3300	(void) sm_vsnprintf(SmtpMsgBuffer, sizeof(SmtpMsgBuffer), f, ap);
3301	SM_VA_END(ap);
3302
3303	if (tTd(18, 1) || Verbose)
3304		nmessage(">>> %s", SmtpMsgBuffer);
3305	if (TrafficLogFile != NULL)
3306		(void) sm_io_fprintf(TrafficLogFile, SM_TIME_DEFAULT,
3307				     "%05d >>> %s\n", (int) CurrentPid,
3308				     SmtpMsgBuffer);
3309	if (mci->mci_out != NULL)
3310	{
3311		(void) sm_io_fprintf(mci->mci_out, SM_TIME_DEFAULT, "%s%s",
3312				     SmtpMsgBuffer, m == NULL ? "\r\n"
3313							      : m->m_eol);
3314	}
3315	else if (tTd(18, 1))
3316	{
3317		sm_dprintf("smtpmessage: NULL mci_out\n");
3318	}
3319}
3320