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