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