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