usersmtp.c revision 77349
1/*
2 * Copyright (c) 1998-2001 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
16#ifndef lint
17# if SMTP
18static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.33 2001/05/23 18:53:09 ca Exp $ (with SMTP)";
19# else /* SMTP */
20static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.33 2001/05/23 18:53:09 ca Exp $ (without SMTP)";
21# endif /* SMTP */
22#endif /* ! lint */
23
24#include <sysexits.h>
25
26#if SMTP
27
28
29static void	datatimeout __P((void));
30static void	esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
31static void	helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
32
33/*
34**  USERSMTP -- run SMTP protocol from the user end.
35**
36**	This protocol is described in RFC821.
37*/
38
39# define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
40# define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
41# define SMTPCLOSING	421			/* "Service Shutting Down" */
42
43#define ENHSCN(e, d)	(e) == NULL ? (d) : newstr(e)
44
45static char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
46static char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
47static bool	SmtpNeedIntro;		/* need "while talking" in transcript */
48/*
49**  SMTPINIT -- initialize SMTP.
50**
51**	Opens the connection and sends the initial protocol.
52**
53**	Parameters:
54**		m -- mailer to create connection to.
55**		mci -- the mailer connection info.
56**		e -- the envelope.
57**		onlyhelo -- send only helo command?
58**
59**	Returns:
60**		none.
61**
62**	Side Effects:
63**		creates connection and sends initial protocol.
64*/
65
66void
67smtpinit(m, mci, e, onlyhelo)
68	MAILER *m;
69	register MCI *mci;
70	ENVELOPE *e;
71	bool onlyhelo;
72{
73	register int r;
74	register char *p;
75	register char *hn;
76	char *enhsc;
77
78	enhsc = NULL;
79	if (tTd(18, 1))
80	{
81		dprintf("smtpinit ");
82		mci_dump(mci, FALSE);
83	}
84
85	/*
86	**  Open the connection to the mailer.
87	*/
88
89	SmtpError[0] = '\0';
90	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
91	if (CurHostName == NULL)
92		CurHostName = MyHostName;
93	SmtpNeedIntro = TRUE;
94	switch (mci->mci_state)
95	{
96	  case MCIS_ACTIVE:
97		/* need to clear old information */
98		smtprset(m, mci, e);
99		/* FALLTHROUGH */
100
101	  case MCIS_OPEN:
102		if (!onlyhelo)
103			return;
104		break;
105
106	  case MCIS_ERROR:
107	  case MCIS_QUITING:
108	  case MCIS_SSD:
109		/* shouldn't happen */
110		smtpquit(m, mci, e);
111		/* FALLTHROUGH */
112
113	  case MCIS_CLOSED:
114		syserr("451 4.4.0 smtpinit: state CLOSED");
115		return;
116
117	  case MCIS_OPENING:
118		break;
119	}
120	if (onlyhelo)
121		goto helo;
122
123	mci->mci_state = MCIS_OPENING;
124
125	/*
126	**  Get the greeting message.
127	**	This should appear spontaneously.  Give it five minutes to
128	**	happen.
129	*/
130
131	SmtpPhase = mci->mci_phase = "client greeting";
132	sm_setproctitle(TRUE, e, "%s %s: %s",
133			qid_printname(e), CurHostName, mci->mci_phase);
134	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL);
135	if (r < 0)
136		goto tempfail1;
137	if (REPLYTYPE(r) == 4)
138		goto tempfail2;
139	if (REPLYTYPE(r) != 2)
140		goto unavailable;
141
142	/*
143	**  Send the HELO command.
144	**	My mother taught me to always introduce myself.
145	*/
146
147helo:
148	if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
149		mci->mci_flags |= MCIF_ESMTP;
150	hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
151
152tryhelo:
153	if (bitnset(M_LMTP, m->m_flags))
154	{
155		smtpmessage("LHLO %s", m, mci, hn);
156		SmtpPhase = mci->mci_phase = "client LHLO";
157	}
158	else if (bitset(MCIF_ESMTP, mci->mci_flags))
159	{
160		smtpmessage("EHLO %s", m, mci, hn);
161		SmtpPhase = mci->mci_phase = "client EHLO";
162	}
163	else
164	{
165		smtpmessage("HELO %s", m, mci, hn);
166		SmtpPhase = mci->mci_phase = "client HELO";
167	}
168	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
169			CurHostName, mci->mci_phase);
170	r = reply(m, mci, e, TimeOuts.to_helo, helo_options, NULL);
171	if (r < 0)
172		goto tempfail1;
173	else if (REPLYTYPE(r) == 5)
174	{
175		if (bitset(MCIF_ESMTP, mci->mci_flags) &&
176		    !bitnset(M_LMTP, m->m_flags))
177		{
178			/* try old SMTP instead */
179			mci->mci_flags &= ~MCIF_ESMTP;
180			goto tryhelo;
181		}
182		goto unavailable;
183	}
184	else if (REPLYTYPE(r) != 2)
185		goto tempfail2;
186
187	/*
188	**  Check to see if we actually ended up talking to ourself.
189	**  This means we didn't know about an alias or MX, or we managed
190	**  to connect to an echo server.
191	*/
192
193	p = strchr(&SmtpReplyBuffer[4], ' ');
194	if (p != NULL)
195		*p = '\0';
196	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
197	    !bitnset(M_LMTP, m->m_flags) &&
198	    strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
199	{
200		syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
201			CurHostName);
202		mci_setstat(mci, EX_CONFIG, "5.3.5",
203			    "553 5.3.5 system config error");
204		mci->mci_errno = 0;
205		smtpquit(m, mci, e);
206		return;
207	}
208
209	/*
210	**  If this is expected to be another sendmail, send some internal
211	**  commands.
212	*/
213
214	if (bitnset(M_INTERNAL, m->m_flags))
215	{
216		/* tell it to be verbose */
217		smtpmessage("VERB", m, mci);
218		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc);
219		if (r < 0)
220			goto tempfail1;
221	}
222
223	if (mci->mci_state != MCIS_CLOSED)
224	{
225		mci->mci_state = MCIS_OPEN;
226		return;
227	}
228
229	/* got a 421 error code during startup */
230
231  tempfail1:
232	if (mci->mci_errno == 0)
233		mci->mci_errno = errno;
234	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
235	if (mci->mci_state != MCIS_CLOSED)
236		smtpquit(m, mci, e);
237	return;
238
239  tempfail2:
240	if (mci->mci_errno == 0)
241		mci->mci_errno = errno;
242	/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
243	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
244		    SmtpReplyBuffer);
245	if (mci->mci_state != MCIS_CLOSED)
246		smtpquit(m, mci, e);
247	return;
248
249  unavailable:
250	mci->mci_errno = errno;
251	mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
252	smtpquit(m, mci, e);
253	return;
254}
255/*
256**  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
257**
258**	Parameters:
259**		line -- the response line.
260**		firstline -- set if this is the first line of the reply.
261**		m -- the mailer.
262**		mci -- the mailer connection info.
263**		e -- the envelope.
264**
265**	Returns:
266**		none.
267*/
268
269static void
270esmtp_check(line, firstline, m, mci, e)
271	char *line;
272	bool firstline;
273	MAILER *m;
274	register MCI *mci;
275	ENVELOPE *e;
276{
277	if (strstr(line, "ESMTP") != NULL)
278		mci->mci_flags |= MCIF_ESMTP;
279	if (strstr(line, "8BIT-OK") != NULL)
280		mci->mci_flags |= MCIF_8BITOK;
281}
282# if SASL
283/*
284**  STR_UNION -- create the union of two lists
285**
286**	Parameters:
287**		s1, s2 -- lists of items (separated by single blanks).
288**
289**	Returns:
290**		the union of both lists.
291*/
292
293static char *
294str_union(s1, s2)
295	char *s1, *s2;
296{
297	char *hr, *h1, *h, *res;
298	int l1, l2, rl;
299
300	if (s1 == NULL || *s1 == '\0')
301		return s2;
302	if (s2 == NULL || *s2 == '\0')
303		return s1;
304	l1 = strlen(s1);
305	l2 = strlen(s2);
306	rl = l1 + l2;
307	res = (char *)xalloc(rl + 2);
308	(void) strlcpy(res, s1, rl);
309	hr = res + l1;
310	h1 = s2;
311	h = s2;
312
313	/* walk through s2 */
314	while (h != NULL && *h1 != '\0')
315	{
316		/* is there something after the current word? */
317		if ((h = strchr(h1, ' ')) != NULL)
318			*h = '\0';
319		l1 = strlen(h1);
320
321		/* does the current word appear in s1 ? */
322		if (iteminlist(h1, s1, " ") == NULL)
323		{
324			/* add space as delimiter */
325			*hr++ = ' ';
326
327			/* copy the item */
328			memcpy(hr, h1, l1);
329
330			/* advance pointer in result list */
331			hr += l1;
332			*hr = '\0';
333		}
334		if (h != NULL)
335		{
336			/* there are more items */
337			*h = ' ';
338			h1 = h + 1;
339		}
340	}
341	return res;
342}
343# endif /* SASL */
344/*
345**  HELO_OPTIONS -- process the options on a HELO line.
346**
347**	Parameters:
348**		line -- the response line.
349**		firstline -- set if this is the first line of the reply.
350**		m -- the mailer.
351**		mci -- the mailer connection info.
352**		e -- the envelope.
353**
354**	Returns:
355**		none.
356*/
357
358static void
359helo_options(line, firstline, m, mci, e)
360	char *line;
361	bool firstline;
362	MAILER *m;
363	register MCI *mci;
364	ENVELOPE *e;
365{
366	register char *p;
367
368	if (firstline)
369	{
370# if SASL
371		if (mci->mci_saslcap != NULL)
372			sm_free(mci->mci_saslcap);
373		mci->mci_saslcap = NULL;
374# endif /* SASL */
375		return;
376	}
377
378	if (strlen(line) < (SIZE_T) 5)
379		return;
380	line += 4;
381	p = strpbrk(line, " =");
382	if (p != NULL)
383		*p++ = '\0';
384	if (strcasecmp(line, "size") == 0)
385	{
386		mci->mci_flags |= MCIF_SIZE;
387		if (p != NULL)
388			mci->mci_maxsize = atol(p);
389	}
390	else if (strcasecmp(line, "8bitmime") == 0)
391	{
392		mci->mci_flags |= MCIF_8BITMIME;
393		mci->mci_flags &= ~MCIF_7BIT;
394	}
395	else if (strcasecmp(line, "expn") == 0)
396		mci->mci_flags |= MCIF_EXPN;
397	else if (strcasecmp(line, "dsn") == 0)
398		mci->mci_flags |= MCIF_DSN;
399	else if (strcasecmp(line, "enhancedstatuscodes") == 0)
400		mci->mci_flags |= MCIF_ENHSTAT;
401# if STARTTLS
402	else if (strcasecmp(line, "starttls") == 0)
403		mci->mci_flags |= MCIF_TLS;
404# endif /* STARTTLS */
405# if SASL
406	else if (strcasecmp(line, "auth") == 0)
407	{
408		if (p != NULL && *p != '\0')
409		{
410			if (mci->mci_saslcap != NULL)
411			{
412				char *h;
413
414				/*
415				**  create the union with previous auth
416				**  offerings because we recognize "auth "
417				**  and "auth=" (old format).
418				*/
419				h = mci->mci_saslcap;
420				mci->mci_saslcap = str_union(h, p);
421				if (h != mci->mci_saslcap)
422					sm_free(h);
423				mci->mci_flags |= MCIF_AUTH;
424			}
425			else
426			{
427				int l;
428
429				l = strlen(p) + 1;
430				mci->mci_saslcap = (char *)xalloc(l);
431				(void) strlcpy(mci->mci_saslcap, p, l);
432				mci->mci_flags |= MCIF_AUTH;
433			}
434		}
435	}
436# endif /* SASL */
437}
438# if SASL
439
440/*
441**  GETSASLDATA -- process the challenges from the SASL protocol
442**
443**	This gets the relevant sasl response data out of the reply
444**	from the server
445**
446**	Parameters:
447**		line -- the response line.
448**		firstline -- set if this is the first line of the reply.
449**		m -- the mailer.
450**		mci -- the mailer connection info.
451**		e -- the envelope.
452**
453**	Returns:
454**		none.
455*/
456
457void
458getsasldata(line, firstline, m, mci, e)
459	char *line;
460	bool firstline;
461	MAILER *m;
462	register MCI *mci;
463	ENVELOPE *e;
464{
465	int len;
466	char *out;
467	int result;
468
469	/* if not a continue we don't care about it */
470	if ((strlen(line) <= 4) ||
471	    (line[0] != '3') ||
472	    (line[1] != '3') ||
473	    (line[2] != '4'))
474	{
475		mci->mci_sasl_string = NULL;
476		return;
477	}
478
479	/* forget about "334 " */
480	line += 4;
481	len = strlen(line);
482
483	out = xalloc(len + 1);
484	result = sasl_decode64(line, len, out, (u_int *)&len);
485	if (result != SASL_OK)
486	{
487		len = 0;
488		*out = '\0';
489	}
490	if (mci->mci_sasl_string != NULL)
491	{
492		if (mci->mci_sasl_string_len <= len)
493		{
494			sm_free(mci->mci_sasl_string);
495			mci->mci_sasl_string = xalloc(len + 1);
496		}
497	}
498	else
499		mci->mci_sasl_string = xalloc(len + 1);
500	/* XXX this is probably leaked */
501	memcpy(mci->mci_sasl_string, out, len);
502	mci->mci_sasl_string[len] = '\0';
503	mci->mci_sasl_string_len = len;
504	sm_free(out);
505	return;
506}
507
508/*
509**  READAUTH -- read auth value from a file
510**
511**	Parameters:
512**		l -- line to define.
513**		filename -- name of file to read.
514**		safe -- if set, this is a safe read.
515**
516**	Returns:
517**		line from file
518**
519**	Side Effects:
520**		overwrites local static buffer. The caller should copy
521**		the result.
522**
523*/
524
525/* lines in authinfo file */
526# define SASL_USER	1
527# define SASL_AUTHID	2
528# define SASL_PASSWORD	3
529# define SASL_DEFREALM	4
530# define SASL_MECH	5
531
532static char *sasl_info_name[] =
533{
534	"",
535	"user id",
536	"authorization id",
537	"password",
538	"realm",
539	"mechanism"
540};
541
542static char *
543readauth(l, filename, safe)
544	int l;
545	char *filename;
546	bool safe;
547{
548	FILE *f;
549	long sff;
550	pid_t pid;
551	int lc;
552	static char buf[MAXLINE];
553
554	if (filename == NULL || filename[0] == '\0')
555		return "";
556#if !_FFR_ALLOW_SASLINFO
557	/*
558	**  make sure we don't use a program that is not
559	**  accesible to the user who specified a different authinfo file.
560	**  However, currently we don't pass this info (authinfo file
561	**  specified by user) around, so we just turn off program access.
562	*/
563	if (filename[0] == '|')
564	{
565		auto int fd;
566		int i;
567		char *p;
568		char *argv[MAXPV + 1];
569
570		i = 0;
571		for (p = strtok(&filename[1], " \t"); p != NULL;
572		     p = strtok(NULL, " \t"))
573		{
574			if (i >= MAXPV)
575				break;
576			argv[i++] = p;
577		}
578		argv[i] = NULL;
579		pid = prog_open(argv, &fd, CurEnv);
580		if (pid < 0)
581			f = NULL;
582		else
583			f = fdopen(fd, "r");
584	}
585	else
586#endif /* !_FFR_ALLOW_SASLINFO */
587	{
588		pid = -1;
589		sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
590		      | SFF_NOGWFILES | SFF_NOWWFILES | SFF_NORFILES;
591		if (DontLockReadFiles)
592			sff |= SFF_NOLOCK;
593#if _FFR_ALLOW_SASLINFO
594		/*
595		**  XXX: make sure we don't read or open files that are not
596		**  accesible to the user who specified a different authinfo
597		**  file.
598		*/
599		sff |= SFF_MUSTOWN;
600#else /* _FFR_ALLOW_SASLINFO */
601		if (safe)
602			sff |= SFF_OPENASROOT;
603#endif /* _FFR_ALLOW_SASLINFO */
604
605		f = safefopen(filename, O_RDONLY, 0, sff);
606	}
607	if (f == NULL)
608	{
609		syserr("readauth: cannot open %s", filename);
610		return "";
611	}
612
613	lc = 0;
614	while (lc < l && fgets(buf, sizeof buf, f) != NULL)
615	{
616		if (buf[0] != '#')
617			lc++;
618	}
619
620	(void) fclose(f);
621	if (pid > 0)
622		(void) waitfor(pid);
623	if (lc < l)
624	{
625		if (LogLevel >= 9)
626			sm_syslog(LOG_WARNING, NOQID, "SASL: error: can't read %s from %s",
627			  sasl_info_name[l], filename);
628		return "";
629	}
630	lc = strlen(buf) - 1;
631	if (lc >= 0)
632		buf[lc] = '\0';
633	if (tTd(95, 6))
634		dprintf("readauth(%s, %d) = '%s'\n", filename, l, buf);
635	return buf;
636}
637
638#  ifndef __attribute__
639#   define __attribute__(x)
640#  endif /* ! __attribute__ */
641
642static int getsimple	__P((void *, int, const char **, unsigned *));
643static int getsecret	__P((sasl_conn_t *, void *, int, sasl_secret_t **));
644static int saslgetrealm	__P((void *, int, const char **, const char **));
645
646static sasl_callback_t callbacks[] =
647{
648	{	SASL_CB_GETREALM,	&saslgetrealm,	NULL	},
649# define CB_GETREALM_IDX	0
650	{	SASL_CB_PASS,		&getsecret,	NULL	},
651# define CB_PASS_IDX	1
652	{	SASL_CB_USER,		&getsimple,	NULL	},
653# define CB_USER_IDX	2
654	{	SASL_CB_AUTHNAME,	&getsimple,	NULL	},
655# define CB_AUTHNAME_IDX	3
656	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
657	{	SASL_CB_LIST_END,	NULL,		NULL	}
658};
659
660/*
661**  GETSIMPLE -- callback to get userid or authid
662**
663**	Parameters:
664**		context -- unused
665**		id -- what to do
666**		result -- (pointer to) result
667**		len -- (pointer to) length of result
668**
669**	Returns:
670**		OK/failure values
671*/
672
673static int
674getsimple(context, id, result, len)
675	void *context __attribute__((unused));
676	int id;
677	const char **result;
678	unsigned *len;
679{
680	char *h;
681#  if SASL > 10509
682	int addrealm;
683	static int addedrealm = FALSE;
684#  endif /* SASL > 10509 */
685	static char *user = NULL;
686	static char *authid = NULL;
687
688	if (result == NULL)
689		return SASL_BADPARAM;
690
691	switch (id)
692	{
693	  case SASL_CB_USER:
694		if (user == NULL)
695		{
696			h = readauth(SASL_USER, SASLInfo, TRUE);
697			user = newstr(h);
698		}
699		*result = user;
700		if (tTd(95, 5))
701			dprintf("AUTH username '%s'\n", *result);
702		if (len != NULL)
703			*len = user ? strlen(user) : 0;
704		break;
705
706	  case SASL_CB_AUTHNAME:
707#  if SASL > 10509
708		/* XXX maybe other mechanisms too?! */
709		addrealm = context != NULL &&
710			   strcasecmp(context, "CRAM-MD5") == 0;
711		if (addedrealm != addrealm && authid != NULL)
712		{
713#  if SASL > 10522
714			/*
715			**  digest-md5 prior to 1.5.23 doesn't copy the
716			**  value it gets from the callback, but free()s
717			**  it later on
718			**  workaround: don't free() it here
719			**  this can cause a memory leak!
720			*/
721
722			sm_free(authid);
723#  endif /* SASL > 10522 */
724			authid = NULL;
725			addedrealm = addrealm;
726		}
727#  endif /* SASL > 10509 */
728		if (authid == NULL)
729		{
730			h = readauth(SASL_AUTHID, SASLInfo, TRUE);
731#  if SASL > 10509
732			if (addrealm && strchr(h, '@') == NULL)
733			{
734				size_t l;
735				char *realm;
736
737				realm = callbacks[CB_GETREALM_IDX].context;
738				l = strlen(h) + strlen(realm) + 2;
739				authid = xalloc(l);
740				snprintf(authid, l, "%s@%s", h, realm);
741			}
742			else
743#  endif /* SASL > 10509 */
744				authid = newstr(h);
745		}
746		*result = authid;
747		if (tTd(95, 5))
748			dprintf("AUTH authid '%s'\n", *result);
749		if (len != NULL)
750			*len = authid ? strlen(authid) : 0;
751		break;
752
753	  case SASL_CB_LANGUAGE:
754		*result = NULL;
755		if (len != NULL)
756			*len = 0;
757		break;
758
759	  default:
760		return SASL_BADPARAM;
761	}
762	return SASL_OK;
763}
764
765/*
766**  GETSECRET -- callback to get password
767**
768**	Parameters:
769**		conn -- connection information
770**		context -- unused
771**		id -- what to do
772**		psecret -- (pointer to) result
773**
774**	Returns:
775**		OK/failure values
776*/
777
778static int
779getsecret(conn, context, id, psecret)
780	sasl_conn_t *conn;
781	void *context __attribute__((unused));
782	int id;
783	sasl_secret_t **psecret;
784{
785	char *h;
786	int len;
787	static char *authpass = NULL;
788
789	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
790		return SASL_BADPARAM;
791
792	if (authpass == NULL)
793	{
794		h = readauth(SASL_PASSWORD, SASLInfo, TRUE);
795		authpass = newstr(h);
796	}
797	len = strlen(authpass);
798	*psecret = (sasl_secret_t *) xalloc(sizeof(sasl_secret_t) + len + 1);
799	(void) strlcpy((*psecret)->data, authpass, len + 1);
800	(*psecret)->len = len;
801	return SASL_OK;
802}
803
804/*
805**  SAFESASLFILE -- callback for sasl: is file safe?
806**
807**	Parameters:
808**		context -- pointer to context between invocations (unused)
809**		file -- name of file to check
810**		type -- type of file to check
811**
812**	Returns:
813**		SASL_OK: file can be used
814**		SASL_CONTINUE: don't use file
815**		SASL_FAIL: failure (not used here)
816**
817*/
818int
819# if SASL > 10515
820safesaslfile(context, file, type)
821# else /* SASL > 10515 */
822safesaslfile(context, file)
823# endif /* SASL > 10515 */
824	void *context;
825	char *file;
826# if SASL > 10515
827	int type;
828# endif /* SASL > 10515 */
829{
830	long sff;
831	int r;
832	char *p;
833
834	if (file == NULL || *file == '\0')
835		return SASL_OK;
836
837	sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK;
838	if ((p = strrchr(file, '/')) == NULL)
839		p = file;
840	else
841		++p;
842
843# if SASL <= 10515
844	/* everything beside libs and .conf files must not be readable */
845	r = strlen(p);
846	if ((r <= 3 || strncmp(p, "lib", 3) != 0) &&
847	    (r <= 5 || strncmp(p + r - 5, ".conf", 5) != 0)
848#  if _FFR_UNSAFE_SASL
849	    && !bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail)
850#  endif /* _FFR_UNSAFE_SASL */
851	   )
852		sff |= SFF_NORFILES;
853# else /* SASL > 10515 */
854	/* files containing passwords should be not readable */
855	if (type == SASL_VRFY_PASSWD)
856	{
857#  if _FFR_UNSAFE_SASL
858		if (bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail))
859			sff |= SFF_NOWRFILES;
860		else
861#  endif /* _FFR_UNSAFE_SASL */
862			sff |= SFF_NORFILES;
863	}
864# endif /* SASL <= 10515 */
865
866	p = file;
867	if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
868			  S_IRUSR, NULL)) == 0)
869		return SASL_OK;
870	if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9))
871		sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
872			  p, errstring(r));
873	return SASL_CONTINUE;
874}
875
876/*
877**  SASLGETREALM -- return the realm for SASL
878**
879**	return the realm for the client
880**
881**	Parameters:
882**		context -- context shared between invocations
883**			here: realm to return
884**		availrealms -- list of available realms
885**			{realm, realm, ...}
886**		result -- pointer to result
887**
888**	Returns:
889**		failure/success
890*/
891static int
892saslgetrealm(context, id, availrealms, result)
893	void *context;
894	int id;
895	const char **availrealms;
896	const char **result;
897{
898	if (LogLevel > 12)
899		sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s",
900			  context == NULL ? "<No Context>" : (char *) context,
901			  (availrealms == NULL || *availrealms == NULL) ? "<No Realms>" : *availrealms);
902	if (context == NULL)
903		return SASL_FAIL;
904
905	/* check whether context is in list? */
906	if (availrealms != NULL && *availrealms != NULL)
907	{
908		if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
909		    NULL)
910		{
911			if (LogLevel > 8)
912				sm_syslog(LOG_ERR, NOQID,
913					  "saslgetrealm: realm %s not in list %s",
914					  context, *availrealms);
915			return SASL_FAIL;
916		}
917	}
918	*result = (char *)context;
919	return SASL_OK;
920}
921/*
922**  ITEMINLIST -- does item appear in list?
923**
924**	Check whether item appears in list (which must be separated by a
925**	character in delim) as a "word", i.e. it must appear at the begin
926**	of the list or after a space, and it must end with a space or the
927**	end of the list.
928**
929**	Parameters:
930**		item -- item to search.
931**		list -- list of items.
932**		delim -- list of delimiters.
933**
934**	Returns:
935**		pointer to occurrence (NULL if not found).
936*/
937
938char *
939iteminlist(item, list, delim)
940	char *item;
941	char *list;
942	char *delim;
943{
944	char *s;
945	int len;
946
947	if (list == NULL || *list == '\0')
948		return NULL;
949	if (item == NULL || *item == '\0')
950		return NULL;
951	s = list;
952	len = strlen(item);
953	while (s != NULL && *s != '\0')
954	{
955		if (strncasecmp(s, item, len) == 0 &&
956		    (s[len] == '\0' || strchr(delim, s[len]) != NULL))
957			return s;
958		s = strpbrk(s, delim);
959		if (s != NULL)
960			while (*++s == ' ')
961				continue;
962	}
963	return NULL;
964}
965/*
966**  REMOVEMECH -- remove item [rem] from list [list]
967**
968**	Parameters:
969**		rem -- item to remove
970**		list -- list of items
971**
972**	Returns:
973**		pointer to new list (NULL in case of error).
974*/
975
976char *
977removemech(rem, list)
978	char *rem;
979	char *list;
980{
981	char *ret;
982	char *needle;
983	int len;
984
985	if (list == NULL)
986		return NULL;
987	if (rem == NULL || *rem == '\0')
988	{
989		/* take out what? */
990		return NULL;
991	}
992
993	/* find the item in the list */
994	if ((needle = iteminlist(rem, list, " ")) == NULL)
995	{
996		/* not in there: return original */
997		return list;
998	}
999
1000	/* length of string without rem */
1001	len = strlen(list) - strlen(rem);
1002	if (len == 0)
1003	{
1004		ret = xalloc(1);  /* XXX leaked */
1005		*ret = '\0';
1006		return ret;
1007	}
1008	ret = xalloc(len);  /* XXX leaked */
1009	memset(ret, '\0', len);
1010
1011	/* copy from start to removed item */
1012	memcpy(ret, list, needle - list);
1013
1014	/* length of rest of string past removed item */
1015	len = strlen(needle) - strlen(rem) - 1;
1016	if (len > 0)
1017	{
1018		/* not last item -- copy into string */
1019		memcpy(ret + (needle - list),
1020		       list + (needle - list) + strlen(rem) + 1,
1021		       len);
1022	}
1023	else
1024		ret[(needle - list) - 1] = '\0';
1025	return ret;
1026}
1027/*
1028**  INTERSECT -- create the intersection between two lists
1029**
1030**	Parameters:
1031**		s1, s2 -- lists of items (separated by single blanks).
1032**
1033**	Returns:
1034**		the intersection of both lists.
1035*/
1036
1037char *
1038intersect(s1, s2)
1039	char *s1, *s2;
1040{
1041	char *hr, *h1, *h, *res;
1042	int l1, l2, rl;
1043
1044	if (s1 == NULL || s2 == NULL)	/* NULL string(s) -> NULL result */
1045		return NULL;
1046	l1 = strlen(s1);
1047	l2 = strlen(s2);
1048	rl = min(l1, l2);
1049	res = (char *)xalloc(rl + 1);
1050	*res = '\0';
1051	if (rl == 0)	/* at least one string empty? */
1052		return res;
1053	hr = res;
1054	h1 = s1;
1055	h = s1;
1056
1057	/* walk through s1 */
1058	while (h != NULL && *h1 != '\0')
1059	{
1060		/* is there something after the current word? */
1061		if ((h = strchr(h1, ' ')) != NULL)
1062			*h = '\0';
1063		l1 = strlen(h1);
1064
1065		/* does the current word appear in s2 ? */
1066		if (iteminlist(h1, s2, " ") != NULL)
1067		{
1068			/* add a blank if not first item */
1069			if (hr != res)
1070				*hr++ = ' ';
1071
1072			/* copy the item */
1073			memcpy(hr, h1, l1);
1074
1075			/* advance pointer in result list */
1076			hr += l1;
1077			*hr = '\0';
1078		}
1079		if (h != NULL)
1080		{
1081			/* there are more items */
1082			*h = ' ';
1083			h1 = h + 1;
1084		}
1085	}
1086	return res;
1087}
1088/*
1089**  ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1090**
1091**	Parameters:
1092**		m -- the mailer.
1093**		mci -- the mailer connection structure.
1094**		e -- the envelope (including the sender to specify).
1095**		mechused - filled in with mechanism used
1096**
1097**	Returns:
1098**		EX_OK/EX_TEMPFAIL
1099*/
1100
1101int
1102attemptauth(m, mci, e, mechused)
1103	MAILER *m;
1104	MCI *mci;
1105	ENVELOPE *e;
1106	char **mechused;
1107{
1108	int saslresult, smtpresult;
1109	sasl_external_properties_t ssf;
1110	sasl_interact_t *client_interact = NULL;
1111	char *out;
1112	unsigned int outlen;
1113	static char *mechusing;
1114	sasl_security_properties_t ssp;
1115	char in64[MAXOUTLEN];
1116# if NETINET
1117	extern SOCKADDR CurHostAddr;
1118# endif /* NETINET */
1119
1120	*mechused = NULL;
1121	if (mci->mci_conn != NULL)
1122	{
1123		sasl_dispose(&(mci->mci_conn));
1124
1125		/* just in case, sasl_dispose() should take care of it */
1126		mci->mci_conn = NULL;
1127	}
1128
1129	/* make a new client sasl connection */
1130	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1131								 : "smtp",
1132				     CurHostName, NULL, 0, &mci->mci_conn);
1133
1134	/* set properties */
1135	(void) memset(&ssp, '\0', sizeof ssp);
1136#  if SFIO
1137	/* XXX should these be options settable via .cf ? */
1138	/* ssp.min_ssf = 0; is default due to memset() */
1139	{
1140		ssp.max_ssf = INT_MAX;
1141		ssp.maxbufsize = MAXOUTLEN;
1142#   if 0
1143		ssp.security_flags = SASL_SEC_NOPLAINTEXT;
1144#   endif /* 0 */
1145	}
1146#  endif /* SFIO */
1147	saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
1148	if (saslresult != SASL_OK)
1149		return EX_TEMPFAIL;
1150
1151	/* external security strength factor, authentication id */
1152	ssf.ssf = 0;
1153	ssf.auth_id = NULL;
1154# if _FFR_EXT_MECH
1155	out = macvalue(macid("{cert_subject}", NULL), e);
1156	if (out != NULL && *out != '\0')
1157		ssf.auth_id = out;
1158	out = macvalue(macid("{cipher_bits}", NULL), e);
1159	if (out != NULL && *out != '\0')
1160		ssf.ssf = atoi(out);
1161# endif /* _FFR_EXT_MECH */
1162	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1163	if (saslresult != SASL_OK)
1164		return EX_TEMPFAIL;
1165
1166# if NETINET
1167	/* set local/remote ipv4 addresses */
1168	if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
1169	{
1170		SOCKADDR_LEN_T addrsize;
1171		struct sockaddr_in saddr_l;
1172
1173		if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
1174				 (struct sockaddr_in *) &CurHostAddr)
1175		    != SASL_OK)
1176			return EX_TEMPFAIL;
1177		addrsize = sizeof(struct sockaddr_in);
1178		if (getsockname(fileno(mci->mci_out),
1179				(struct sockaddr *) &saddr_l, &addrsize) == 0)
1180		{
1181			if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
1182					 &saddr_l) != SASL_OK)
1183				return EX_TEMPFAIL;
1184		}
1185	}
1186# endif /* NETINET */
1187
1188	/* start client side of sasl */
1189	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1190				       NULL, &client_interact,
1191				       &out, &outlen,
1192				       (const char **)&mechusing);
1193	callbacks[CB_AUTHNAME_IDX].context = mechusing;
1194
1195	if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1196	{
1197#  if SFIO
1198		if (saslresult == SASL_NOMECH && LogLevel > 8)
1199		{
1200			sm_syslog(LOG_NOTICE, e->e_id,
1201				  "available AUTH mechanisms do not fulfill requirements");
1202		}
1203#  endif /* SFIO */
1204		return EX_TEMPFAIL;
1205	}
1206
1207	*mechused = mechusing;
1208
1209	/* send the info across the wire */
1210	if (outlen > 0)
1211	{
1212		saslresult = sasl_encode64(out, outlen, in64, MAXOUTLEN, NULL);
1213		if (saslresult != SASL_OK) /* internal error */
1214		{
1215			if (LogLevel > 8)
1216				sm_syslog(LOG_ERR, e->e_id,
1217					"encode64 for AUTH failed");
1218			return EX_TEMPFAIL;
1219		}
1220		smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
1221	}
1222	else
1223	{
1224		smtpmessage("AUTH %s", m, mci, mechusing);
1225	}
1226
1227	/* get the reply */
1228	smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, getsasldata, NULL);
1229	/* which timeout? XXX */
1230
1231	for (;;)
1232	{
1233		/* check return code from server */
1234		if (smtpresult == 235)
1235		{
1236			define(macid("{auth_type}", NULL),
1237			       newstr(mechusing), e);
1238#  if !SFIO
1239			if (LogLevel > 9)
1240				sm_syslog(LOG_INFO, NOQID,
1241					  "SASL: outgoing connection to %.64s: mech=%.16s",
1242					  mci->mci_host, mechusing);
1243#  endif /* !SFIO */
1244			return EX_OK;
1245		}
1246		if (smtpresult == -1)
1247			return EX_IOERR;
1248		if (smtpresult != 334)
1249			return EX_TEMPFAIL;
1250
1251		saslresult = sasl_client_step(mci->mci_conn,
1252					      mci->mci_sasl_string,
1253					      mci->mci_sasl_string_len,
1254					      &client_interact,
1255					      &out, &outlen);
1256
1257		if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1258		{
1259			if (tTd(95, 5))
1260				dprintf("AUTH FAIL: %s (%d)\n",
1261					sasl_errstring(saslresult, NULL, NULL),
1262					saslresult);
1263
1264			/* fail deliberately, see RFC 2254 4. */
1265			smtpmessage("*", m, mci);
1266
1267			/*
1268			**  but we should only fail for this authentication
1269			**  mechanism; how to do that?
1270			*/
1271
1272			smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
1273					   getsasldata, NULL);
1274			return EX_TEMPFAIL;
1275		}
1276
1277		if (outlen > 0)
1278		{
1279			saslresult = sasl_encode64(out, outlen, in64,
1280						   MAXOUTLEN, NULL);
1281			if (saslresult != SASL_OK)
1282			{
1283				/* give an error reply to the other side! */
1284				smtpmessage("*", m, mci);
1285				return EX_TEMPFAIL;
1286			}
1287		}
1288		else
1289			in64[0] = '\0';
1290		smtpmessage("%s", m, mci, in64);
1291		smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
1292				   getsasldata, NULL);
1293		/* which timeout? XXX */
1294	}
1295	/* NOTREACHED */
1296}
1297
1298/*
1299**  SMTPAUTH -- try to AUTHenticate
1300**
1301**	This will try mechanisms in the order the sasl library decided until:
1302**	- there are no more mechanisms
1303**	- a mechanism succeeds
1304**	- the sasl library fails initializing
1305**
1306**	Parameters:
1307**		m -- the mailer.
1308**		mci -- the mailer connection info.
1309**		e -- the envelope.
1310**
1311**	Returns:
1312**		EX_OK/EX_TEMPFAIL
1313*/
1314
1315int
1316smtpauth(m, mci, e)
1317	MAILER *m;
1318	MCI *mci;
1319	ENVELOPE *e;
1320{
1321	int result;
1322	char *mechused;
1323	char *h;
1324	static char *defrealm = NULL;
1325	static char *mechs = NULL;
1326
1327	mci->mci_sasl_auth = FALSE;
1328	if (defrealm == NULL)
1329	{
1330		h = readauth(SASL_DEFREALM, SASLInfo, TRUE);
1331		if (h != NULL && *h != '\0')
1332			defrealm = newstr(h);
1333	}
1334	if (defrealm == NULL || *defrealm == '\0')
1335		defrealm = newstr(macvalue('j', CurEnv));
1336	callbacks[CB_GETREALM_IDX].context = defrealm;
1337
1338# if _FFR_DEFAUTHINFO_MECHS
1339	if (mechs == NULL)
1340	{
1341		h = readauth(SASL_MECH, SASLInfo, TRUE);
1342		if (h != NULL && *h != '\0')
1343			mechs = newstr(h);
1344	}
1345# endif /* _FFR_DEFAUTHINFO_MECHS */
1346	if (mechs == NULL || *mechs == '\0')
1347		mechs = AuthMechanisms;
1348	mci->mci_saslcap = intersect(mechs, mci->mci_saslcap);
1349
1350	/* initialize sasl client library */
1351	result = sasl_client_init(callbacks);
1352	if (result != SASL_OK)
1353		return EX_TEMPFAIL;
1354	do
1355	{
1356		result = attemptauth(m, mci, e, &mechused);
1357		if (result == EX_OK)
1358			mci->mci_sasl_auth = TRUE;
1359		else if (result == EX_TEMPFAIL)
1360		{
1361			mci->mci_saslcap = removemech(mechused,
1362						      mci->mci_saslcap);
1363			if (mci->mci_saslcap == NULL ||
1364			    *(mci->mci_saslcap) == '\0')
1365				return EX_TEMPFAIL;
1366		}
1367		else	/* all others for now */
1368			return EX_TEMPFAIL;
1369	} while (result != EX_OK);
1370	return result;
1371}
1372# endif /* SASL */
1373
1374/*
1375**  SMTPMAILFROM -- send MAIL command
1376**
1377**	Parameters:
1378**		m -- the mailer.
1379**		mci -- the mailer connection structure.
1380**		e -- the envelope (including the sender to specify).
1381*/
1382
1383int
1384smtpmailfrom(m, mci, e)
1385	MAILER *m;
1386	MCI *mci;
1387	ENVELOPE *e;
1388{
1389	int r;
1390	char *bufp;
1391	char *bodytype;
1392	char buf[MAXNAME + 1];
1393	char optbuf[MAXLINE];
1394	char *enhsc;
1395
1396	if (tTd(18, 2))
1397		dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
1398	enhsc = NULL;
1399
1400	/* set up appropriate options to include */
1401	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
1402	{
1403		snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize);
1404		bufp = &optbuf[strlen(optbuf)];
1405	}
1406	else
1407	{
1408		optbuf[0] = '\0';
1409		bufp = optbuf;
1410	}
1411
1412	bodytype = e->e_bodytype;
1413	if (bitset(MCIF_8BITMIME, mci->mci_flags))
1414	{
1415		if (bodytype == NULL &&
1416		    bitset(MM_MIME8BIT, MimeMode) &&
1417		    bitset(EF_HAS8BIT, e->e_flags) &&
1418		    !bitset(EF_DONT_MIME, e->e_flags) &&
1419		    !bitnset(M_8BITS, m->m_flags))
1420			bodytype = "8BITMIME";
1421		if (bodytype != NULL &&
1422		    SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
1423		{
1424			snprintf(bufp, SPACELEFT(optbuf, bufp),
1425				 " BODY=%s", bodytype);
1426			bufp += strlen(bufp);
1427		}
1428	}
1429	else if (bitnset(M_8BITS, m->m_flags) ||
1430		 !bitset(EF_HAS8BIT, e->e_flags) ||
1431		 bitset(MCIF_8BITOK, mci->mci_flags))
1432	{
1433		/* EMPTY */
1434		/* just pass it through */
1435	}
1436# if MIME8TO7
1437	else if (bitset(MM_CVTMIME, MimeMode) &&
1438		 !bitset(EF_DONT_MIME, e->e_flags) &&
1439		 (!bitset(MM_PASS8BIT, MimeMode) ||
1440		  bitset(EF_IS_MIME, e->e_flags)))
1441	{
1442		/* must convert from 8bit MIME format to 7bit encoded */
1443		mci->mci_flags |= MCIF_CVT8TO7;
1444	}
1445# endif /* MIME8TO7 */
1446	else if (!bitset(MM_PASS8BIT, MimeMode))
1447	{
1448		/* cannot just send a 8-bit version */
1449		extern char MsgBuf[];
1450
1451		usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
1452		mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
1453		return EX_DATAERR;
1454	}
1455
1456	if (bitset(MCIF_DSN, mci->mci_flags))
1457	{
1458		if (e->e_envid != NULL &&
1459		    SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
1460		{
1461			snprintf(bufp, SPACELEFT(optbuf, bufp),
1462				 " ENVID=%s", e->e_envid);
1463			bufp += strlen(bufp);
1464		}
1465
1466		/* RET= parameter */
1467		if (bitset(EF_RET_PARAM, e->e_flags) &&
1468		    SPACELEFT(optbuf, bufp) > 9)
1469		{
1470			snprintf(bufp, SPACELEFT(optbuf, bufp),
1471				 " RET=%s",
1472				 bitset(EF_NO_BODY_RETN, e->e_flags) ?
1473					"HDRS" : "FULL");
1474			bufp += strlen(bufp);
1475		}
1476	}
1477
1478	if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
1479	    SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
1480# if SASL
1481	     && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
1482# endif /* SASL */
1483	    )
1484	{
1485		snprintf(bufp, SPACELEFT(optbuf, bufp),
1486			 " AUTH=%s", e->e_auth_param);
1487		bufp += strlen(bufp);
1488	}
1489
1490	/*
1491	**  Send the MAIL command.
1492	**	Designates the sender.
1493	*/
1494
1495	mci->mci_state = MCIS_ACTIVE;
1496
1497	if (bitset(EF_RESPONSE, e->e_flags) &&
1498	    !bitnset(M_NO_NULL_FROM, m->m_flags))
1499		buf[0] = '\0';
1500	else
1501		expand("\201g", buf, sizeof buf, e);
1502	if (buf[0] == '<')
1503	{
1504		/* strip off <angle brackets> (put back on below) */
1505		bufp = &buf[strlen(buf) - 1];
1506		if (*bufp == '>')
1507			*bufp = '\0';
1508		bufp = &buf[1];
1509	}
1510	else
1511		bufp = buf;
1512	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
1513	    !bitnset(M_FROMPATH, m->m_flags))
1514	{
1515		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
1516	}
1517	else
1518	{
1519		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
1520			    *bufp == '@' ? ',' : ':', bufp, optbuf);
1521	}
1522	SmtpPhase = mci->mci_phase = "client MAIL";
1523	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1524			CurHostName, mci->mci_phase);
1525	r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc);
1526	if (r < 0)
1527	{
1528		/* communications failure */
1529		mci->mci_errno = errno;
1530		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
1531		smtpquit(m, mci, e);
1532		return EX_TEMPFAIL;
1533	}
1534	else if (r == SMTPCLOSING)
1535	{
1536		/* service shutting down */
1537		mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
1538			    SmtpReplyBuffer);
1539		smtpquit(m, mci, e);
1540		return EX_TEMPFAIL;
1541	}
1542	else if (REPLYTYPE(r) == 4)
1543	{
1544		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
1545			    SmtpReplyBuffer);
1546		return EX_TEMPFAIL;
1547	}
1548	else if (REPLYTYPE(r) == 2)
1549	{
1550		return EX_OK;
1551	}
1552	else if (r == 501)
1553	{
1554		/* syntax error in arguments */
1555		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
1556			    SmtpReplyBuffer);
1557		return EX_DATAERR;
1558	}
1559	else if (r == 553)
1560	{
1561		/* mailbox name not allowed */
1562		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
1563			    SmtpReplyBuffer);
1564		return EX_DATAERR;
1565	}
1566	else if (r == 552)
1567	{
1568		/* exceeded storage allocation */
1569		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
1570			    SmtpReplyBuffer);
1571		if (bitset(MCIF_SIZE, mci->mci_flags))
1572			e->e_flags |= EF_NO_BODY_RETN;
1573		return EX_UNAVAILABLE;
1574	}
1575	else if (REPLYTYPE(r) == 5)
1576	{
1577		/* unknown error */
1578		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
1579			    SmtpReplyBuffer);
1580		return EX_UNAVAILABLE;
1581	}
1582
1583	if (LogLevel > 1)
1584	{
1585		sm_syslog(LOG_CRIT, e->e_id,
1586			  "%.100s: SMTP MAIL protocol error: %s",
1587			  CurHostName,
1588			  shortenstring(SmtpReplyBuffer, 403));
1589	}
1590
1591	/* protocol error -- close up */
1592	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1593		    SmtpReplyBuffer);
1594	smtpquit(m, mci, e);
1595	return EX_PROTOCOL;
1596}
1597/*
1598**  SMTPRCPT -- designate recipient.
1599**
1600**	Parameters:
1601**		to -- address of recipient.
1602**		m -- the mailer we are sending to.
1603**		mci -- the connection info for this transaction.
1604**		e -- the envelope for this transaction.
1605**
1606**	Returns:
1607**		exit status corresponding to recipient status.
1608**
1609**	Side Effects:
1610**		Sends the mail via SMTP.
1611*/
1612
1613int
1614smtprcpt(to, m, mci, e)
1615	ADDRESS *to;
1616	register MAILER *m;
1617	MCI *mci;
1618	ENVELOPE *e;
1619{
1620	register int r;
1621	char *bufp;
1622	char optbuf[MAXLINE];
1623	char *enhsc;
1624
1625	enhsc = NULL;
1626	optbuf[0] = '\0';
1627	bufp = optbuf;
1628
1629	/*
1630	**  warning: in the following it is assumed that the free space
1631	**  in bufp is sizeof optbuf
1632	*/
1633	if (bitset(MCIF_DSN, mci->mci_flags))
1634	{
1635		/* NOTIFY= parameter */
1636		if (bitset(QHASNOTIFY, to->q_flags) &&
1637		    bitset(QPRIMARY, to->q_flags) &&
1638		    !bitnset(M_LOCALMAILER, m->m_flags))
1639		{
1640			bool firstone = TRUE;
1641
1642			(void) strlcat(bufp, " NOTIFY=", sizeof optbuf);
1643			if (bitset(QPINGONSUCCESS, to->q_flags))
1644			{
1645				(void) strlcat(bufp, "SUCCESS", sizeof optbuf);
1646				firstone = FALSE;
1647			}
1648			if (bitset(QPINGONFAILURE, to->q_flags))
1649			{
1650				if (!firstone)
1651					(void) strlcat(bufp, ",",
1652						       sizeof optbuf);
1653				(void) strlcat(bufp, "FAILURE", sizeof optbuf);
1654				firstone = FALSE;
1655			}
1656			if (bitset(QPINGONDELAY, to->q_flags))
1657			{
1658				if (!firstone)
1659					(void) strlcat(bufp, ",",
1660						       sizeof optbuf);
1661				(void) strlcat(bufp, "DELAY", sizeof optbuf);
1662				firstone = FALSE;
1663			}
1664			if (firstone)
1665				(void) strlcat(bufp, "NEVER", sizeof optbuf);
1666			bufp += strlen(bufp);
1667		}
1668
1669		/* ORCPT= parameter */
1670		if (to->q_orcpt != NULL &&
1671		    SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
1672		{
1673			snprintf(bufp, SPACELEFT(optbuf, bufp),
1674				 " ORCPT=%s", to->q_orcpt);
1675			bufp += strlen(bufp);
1676		}
1677	}
1678
1679	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
1680
1681	SmtpPhase = mci->mci_phase = "client RCPT";
1682	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1683			CurHostName, mci->mci_phase);
1684	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc);
1685	to->q_rstatus = newstr(SmtpReplyBuffer);
1686	to->q_status = ENHSCN(enhsc, smtptodsn(r));
1687	if (!bitnset(M_LMTP, m->m_flags))
1688		to->q_statmta = mci->mci_host;
1689	if (r < 0 || REPLYTYPE(r) == 4)
1690		return EX_TEMPFAIL;
1691	else if (REPLYTYPE(r) == 2)
1692		return EX_OK;
1693	else if (r == 550)
1694	{
1695		to->q_status = ENHSCN(enhsc, "5.1.1");
1696		return EX_NOUSER;
1697	}
1698	else if (r == 551)
1699	{
1700		to->q_status = ENHSCN(enhsc, "5.1.6");
1701		return EX_NOUSER;
1702	}
1703	else if (r == 553)
1704	{
1705		to->q_status = ENHSCN(enhsc, "5.1.3");
1706		return EX_NOUSER;
1707	}
1708	else if (REPLYTYPE(r) == 5)
1709	{
1710		return EX_UNAVAILABLE;
1711	}
1712
1713	if (LogLevel > 1)
1714	{
1715		sm_syslog(LOG_CRIT, e->e_id,
1716			  "%.100s: SMTP RCPT protocol error: %s",
1717			  CurHostName,
1718			  shortenstring(SmtpReplyBuffer, 403));
1719	}
1720
1721	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1722		    SmtpReplyBuffer);
1723	return EX_PROTOCOL;
1724}
1725/*
1726**  SMTPDATA -- send the data and clean up the transaction.
1727**
1728**	Parameters:
1729**		m -- mailer being sent to.
1730**		mci -- the mailer connection information.
1731**		e -- the envelope for this message.
1732**
1733**	Returns:
1734**		exit status corresponding to DATA command.
1735**
1736**	Side Effects:
1737**		none.
1738*/
1739
1740static jmp_buf	CtxDataTimeout;
1741static EVENT	*volatile DataTimeout = NULL;
1742
1743int
1744smtpdata(m, mci, e)
1745	MAILER *m;
1746	register MCI *mci;
1747	register ENVELOPE *e;
1748{
1749	register int r;
1750	int rstat;
1751	int xstat;
1752	time_t timeout;
1753	char *enhsc;
1754
1755	enhsc = NULL;
1756
1757	/*
1758	**  Send the data.
1759	**	First send the command and check that it is ok.
1760	**	Then send the data.
1761	**	Follow it up with a dot to terminate.
1762	**	Finally get the results of the transaction.
1763	*/
1764
1765	/* send the command and check ok to proceed */
1766	smtpmessage("DATA", m, mci);
1767	SmtpPhase = mci->mci_phase = "client DATA 354";
1768	sm_setproctitle(TRUE, e, "%s %s: %s",
1769			qid_printname(e), CurHostName, mci->mci_phase);
1770	r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc);
1771	if (r < 0 || REPLYTYPE(r) == 4)
1772	{
1773		smtpquit(m, mci, e);
1774		return EX_TEMPFAIL;
1775	}
1776	else if (REPLYTYPE(r) == 5)
1777	{
1778		smtprset(m, mci, e);
1779		return EX_UNAVAILABLE;
1780	}
1781	else if (REPLYTYPE(r) != 3)
1782	{
1783		if (LogLevel > 1)
1784		{
1785			sm_syslog(LOG_CRIT, e->e_id,
1786				  "%.100s: SMTP DATA-1 protocol error: %s",
1787				  CurHostName,
1788				  shortenstring(SmtpReplyBuffer, 403));
1789		}
1790		smtprset(m, mci, e);
1791		mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1792			    SmtpReplyBuffer);
1793		return EX_PROTOCOL;
1794	}
1795
1796	/*
1797	**  Set timeout around data writes.  Make it at least large
1798	**  enough for DNS timeouts on all recipients plus some fudge
1799	**  factor.  The main thing is that it should not be infinite.
1800	*/
1801
1802	if (setjmp(CtxDataTimeout) != 0)
1803	{
1804		mci->mci_errno = errno;
1805		mci->mci_state = MCIS_ERROR;
1806		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
1807
1808		/*
1809		**  If putbody() couldn't finish due to a timeout,
1810		**  rewind it here in the timeout handler.  See
1811		**  comments at the end of putbody() for reasoning.
1812		*/
1813
1814		if (e->e_dfp != NULL)
1815			(void) bfrewind(e->e_dfp);
1816
1817		errno = mci->mci_errno;
1818		syserr("451 4.4.1 timeout writing message to %s", CurHostName);
1819		smtpquit(m, mci, e);
1820		return EX_TEMPFAIL;
1821	}
1822
1823	if (tTd(18, 101))
1824	{
1825		/* simulate a DATA timeout */
1826		timeout = 1;
1827	}
1828	else
1829		timeout = DATA_PROGRESS_TIMEOUT;
1830
1831	DataTimeout = setevent(timeout, datatimeout, 0);
1832
1833
1834	/*
1835	**  Output the actual message.
1836	*/
1837
1838	(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
1839
1840	if (tTd(18, 101))
1841	{
1842		/* simulate a DATA timeout */
1843		(void) sleep(2);
1844	}
1845
1846	(*e->e_putbody)(mci, e, NULL);
1847
1848	/*
1849	**  Cleanup after sending message.
1850	*/
1851
1852	if (DataTimeout != NULL)
1853		clrevent(DataTimeout);
1854
1855# if _FFR_CATCH_BROKEN_MTAS
1856	{
1857		fd_set readfds;
1858		struct timeval timeout;
1859
1860		FD_ZERO(&readfds);
1861		FD_SET(fileno(mci->mci_in), &readfds);
1862		timeout.tv_sec = 0;
1863		timeout.tv_usec = 0;
1864		if (select(fileno(mci->mci_in) + 1, FDSET_CAST &readfds,
1865			   NULL, NULL, &timeout) > 0 &&
1866		    FD_ISSET(fileno(mci->mci_in), &readfds))
1867		{
1868			/* terminate the message */
1869			fprintf(mci->mci_out, ".%s", m->m_eol);
1870			if (TrafficLogFile != NULL)
1871				fprintf(TrafficLogFile, "%05d >>> .\n",
1872					(int) getpid());
1873			if (Verbose)
1874				nmessage(">>> .");
1875
1876			mci->mci_errno = EIO;
1877			mci->mci_state = MCIS_ERROR;
1878			mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
1879			smtpquit(m, mci, e);
1880			return EX_PROTOCOL;
1881		}
1882	}
1883# endif /* _FFR_CATCH_BROKEN_MTAS */
1884
1885	if (ferror(mci->mci_out))
1886	{
1887		/* error during processing -- don't send the dot */
1888		mci->mci_errno = EIO;
1889		mci->mci_state = MCIS_ERROR;
1890		mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
1891		smtpquit(m, mci, e);
1892		return EX_IOERR;
1893	}
1894
1895	/* terminate the message */
1896	fprintf(mci->mci_out, ".%s", m->m_eol);
1897	if (TrafficLogFile != NULL)
1898		fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid());
1899	if (Verbose)
1900		nmessage(">>> .");
1901
1902	/* check for the results of the transaction */
1903	SmtpPhase = mci->mci_phase = "client DATA status";
1904	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1905			CurHostName, mci->mci_phase);
1906	if (bitnset(M_LMTP, m->m_flags))
1907		return EX_OK;
1908	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
1909	if (r < 0)
1910	{
1911		smtpquit(m, mci, e);
1912		return EX_TEMPFAIL;
1913	}
1914	mci->mci_state = MCIS_OPEN;
1915	xstat = EX_NOTSTICKY;
1916	if (r == 452)
1917		rstat = EX_TEMPFAIL;
1918	else if (REPLYTYPE(r) == 4)
1919		rstat = xstat = EX_TEMPFAIL;
1920	else if (REPLYCLASS(r) != 5)
1921		rstat = xstat = EX_PROTOCOL;
1922	else if (REPLYTYPE(r) == 2)
1923		rstat = xstat = EX_OK;
1924	else if (REPLYTYPE(r) == 5)
1925		rstat = EX_UNAVAILABLE;
1926	else
1927		rstat = EX_PROTOCOL;
1928	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
1929		    SmtpReplyBuffer);
1930	if (e->e_statmsg != NULL)
1931		sm_free(e->e_statmsg);
1932	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
1933	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
1934		r += 5;
1935	else
1936		r = 4;
1937	e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
1938	SmtpPhase = mci->mci_phase = "idle";
1939	sm_setproctitle(TRUE, e, "%s: %s", CurHostName, mci->mci_phase);
1940	if (rstat != EX_PROTOCOL)
1941		return rstat;
1942	if (LogLevel > 1)
1943	{
1944		sm_syslog(LOG_CRIT, e->e_id,
1945			  "%.100s: SMTP DATA-2 protocol error: %s",
1946			  CurHostName,
1947			  shortenstring(SmtpReplyBuffer, 403));
1948	}
1949	return rstat;
1950}
1951
1952
1953static void
1954datatimeout()
1955{
1956	int save_errno = errno;
1957
1958	/*
1959	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
1960	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
1961	**	DOING.
1962	*/
1963
1964	if (DataProgress)
1965	{
1966		time_t timeout;
1967
1968		/* check back again later */
1969		if (tTd(18, 101))
1970		{
1971			/* simulate a DATA timeout */
1972			timeout = 1;
1973		}
1974		else
1975			timeout = DATA_PROGRESS_TIMEOUT;
1976
1977		/* reset the timeout */
1978		DataTimeout = sigsafe_setevent(timeout, datatimeout, 0);
1979		DataProgress = FALSE;
1980	}
1981	else
1982	{
1983		/* event is done */
1984		DataTimeout = NULL;
1985	}
1986
1987	/* if no progress was made or problem resetting event, die now */
1988	if (DataTimeout == NULL)
1989	{
1990		errno = ETIMEDOUT;
1991		longjmp(CtxDataTimeout, 1);
1992	}
1993
1994	errno = save_errno;
1995}
1996/*
1997**  SMTPGETSTAT -- get status code from DATA in LMTP
1998**
1999**	Parameters:
2000**		m -- the mailer to which we are sending the message.
2001**		mci -- the mailer connection structure.
2002**		e -- the current envelope.
2003**
2004**	Returns:
2005**		The exit status corresponding to the reply code.
2006*/
2007
2008int
2009smtpgetstat(m, mci, e)
2010	MAILER *m;
2011	MCI *mci;
2012	ENVELOPE *e;
2013{
2014	int r;
2015	int status;
2016	char *enhsc;
2017
2018	enhsc = NULL;
2019	/* check for the results of the transaction */
2020	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
2021	if (r < 0)
2022	{
2023		smtpquit(m, mci, e);
2024		return EX_TEMPFAIL;
2025	}
2026	if (REPLYTYPE(r) == 4)
2027		status = EX_TEMPFAIL;
2028	else if (REPLYCLASS(r) != 5)
2029		status = EX_PROTOCOL;
2030	else if (REPLYTYPE(r) == 2)
2031		status = EX_OK;
2032	else if (REPLYTYPE(r) == 5)
2033		status = EX_UNAVAILABLE;
2034	else
2035		status = EX_PROTOCOL;
2036	if (e->e_statmsg != NULL)
2037		sm_free(e->e_statmsg);
2038	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2039	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2040		r += 5;
2041	else
2042		r = 4;
2043	e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
2044	mci_setstat(mci, status, ENHSCN(enhsc, smtptodsn(r)),
2045		    SmtpReplyBuffer);
2046	if (LogLevel > 1 && status == EX_PROTOCOL)
2047	{
2048		sm_syslog(LOG_CRIT, e->e_id,
2049			  "%.100s: SMTP DATA-3 protocol error: %s",
2050			  CurHostName,
2051			  shortenstring(SmtpReplyBuffer, 403));
2052	}
2053	return status;
2054}
2055/*
2056**  SMTPQUIT -- close the SMTP connection.
2057**
2058**	Parameters:
2059**		m -- a pointer to the mailer.
2060**		mci -- the mailer connection information.
2061**		e -- the current envelope.
2062**
2063**	Returns:
2064**		none.
2065**
2066**	Side Effects:
2067**		sends the final protocol and closes the connection.
2068*/
2069
2070void
2071smtpquit(m, mci, e)
2072	register MAILER *m;
2073	register MCI *mci;
2074	ENVELOPE *e;
2075{
2076	bool oldSuprErrs = SuprErrs;
2077	int rcode;
2078
2079	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2080	if (CurHostName == NULL)
2081		CurHostName = MyHostName;
2082
2083	/*
2084	**	Suppress errors here -- we may be processing a different
2085	**	job when we do the quit connection, and we don't want the
2086	**	new job to be penalized for something that isn't it's
2087	**	problem.
2088	*/
2089
2090	SuprErrs = TRUE;
2091
2092	/* send the quit message if we haven't gotten I/O error */
2093	if (mci->mci_state != MCIS_ERROR &&
2094	    mci->mci_state != MCIS_QUITING)
2095	{
2096		int origstate = mci->mci_state;
2097
2098		SmtpPhase = "client QUIT";
2099		mci->mci_state = MCIS_QUITING;
2100		smtpmessage("QUIT", m, mci);
2101		(void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL);
2102		SuprErrs = oldSuprErrs;
2103		if (mci->mci_state == MCIS_CLOSED ||
2104		    origstate == MCIS_CLOSED)
2105			return;
2106	}
2107
2108	/* now actually close the connection and pick up the zombie */
2109	rcode = endmailer(mci, e, NULL);
2110	if (rcode != EX_OK)
2111	{
2112		char *mailer = NULL;
2113
2114		if (mci->mci_mailer != NULL &&
2115		    mci->mci_mailer->m_name != NULL)
2116			mailer = mci->mci_mailer->m_name;
2117
2118		/* look for naughty mailers */
2119		sm_syslog(LOG_ERR, e->e_id,
2120			  "smtpquit: mailer%s%s exited with exit value %d",
2121			  mailer == NULL ? "" : " ",
2122			  mailer == NULL ? "" : mailer,
2123			  rcode);
2124	}
2125
2126	SuprErrs = oldSuprErrs;
2127}
2128/*
2129**  SMTPRSET -- send a RSET (reset) command
2130**
2131**	Parameters:
2132**		m -- a pointer to the mailer.
2133**		mci -- the mailer connection information.
2134**		e -- the current envelope.
2135**
2136**	Returns:
2137**		none.
2138**
2139**	Side Effects:
2140**		closes the connection if there is no reply to RSET.
2141*/
2142
2143void
2144smtprset(m, mci, e)
2145	register MAILER *m;
2146	register MCI *mci;
2147	ENVELOPE *e;
2148{
2149	int r;
2150
2151	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2152	if (CurHostName == NULL)
2153		CurHostName = MyHostName;
2154
2155	SmtpPhase = "client RSET";
2156	smtpmessage("RSET", m, mci);
2157	r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL);
2158	if (r < 0)
2159		mci->mci_state = MCIS_ERROR;
2160	else
2161	{
2162		/*
2163		**  Any response is deemed to be acceptable.
2164		**  The standard does not state the proper action
2165		**  to take when a value other than 250 is received.
2166		**
2167		**  However, if 421 is returned for the RSET, leave
2168		**  mci_state as MCIS_SSD (set in reply()).
2169		*/
2170
2171		if (mci->mci_state != MCIS_SSD)
2172			mci->mci_state = MCIS_OPEN;
2173		return;
2174	}
2175	smtpquit(m, mci, e);
2176}
2177/*
2178**  SMTPPROBE -- check the connection state
2179**
2180**	Parameters:
2181**		mci -- the mailer connection information.
2182**
2183**	Returns:
2184**		none.
2185**
2186**	Side Effects:
2187**		closes the connection if there is no reply to RSET.
2188*/
2189
2190int
2191smtpprobe(mci)
2192	register MCI *mci;
2193{
2194	int r;
2195	MAILER *m = mci->mci_mailer;
2196	ENVELOPE *e;
2197	extern ENVELOPE BlankEnvelope;
2198
2199	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2200	if (CurHostName == NULL)
2201		CurHostName = MyHostName;
2202
2203	e = &BlankEnvelope;
2204	SmtpPhase = "client probe";
2205	smtpmessage("RSET", m, mci);
2206	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL);
2207	if (r < 0 || REPLYTYPE(r) != 2)
2208		smtpquit(m, mci, e);
2209	return r;
2210}
2211/*
2212**  REPLY -- read arpanet reply
2213**
2214**	Parameters:
2215**		m -- the mailer we are reading the reply from.
2216**		mci -- the mailer connection info structure.
2217**		e -- the current envelope.
2218**		timeout -- the timeout for reads.
2219**		pfunc -- processing function called on each line of response.
2220**			If null, no special processing is done.
2221**
2222**	Returns:
2223**		reply code it reads.
2224**
2225**	Side Effects:
2226**		flushes the mail file.
2227*/
2228
2229int
2230reply(m, mci, e, timeout, pfunc, enhstat)
2231	MAILER *m;
2232	MCI *mci;
2233	ENVELOPE *e;
2234	time_t timeout;
2235	void (*pfunc)();
2236	char **enhstat;
2237{
2238	register char *bufp;
2239	register int r;
2240	bool firstline = TRUE;
2241	char junkbuf[MAXLINE];
2242	static char enhstatcode[ENHSCLEN];
2243	int save_errno;
2244
2245	if (mci->mci_out != NULL)
2246		(void) fflush(mci->mci_out);
2247
2248	if (tTd(18, 1))
2249		dprintf("reply\n");
2250
2251	/*
2252	**  Read the input line, being careful not to hang.
2253	*/
2254
2255	bufp = SmtpReplyBuffer;
2256	for (;;)
2257	{
2258		register char *p;
2259
2260		/* actually do the read */
2261		if (e->e_xfp != NULL)
2262			(void) fflush(e->e_xfp);	/* for debugging */
2263
2264		/* if we are in the process of closing just give the code */
2265		if (mci->mci_state == MCIS_CLOSED)
2266			return SMTPCLOSING;
2267
2268		if (mci->mci_out != NULL)
2269			(void) fflush(mci->mci_out);
2270
2271		/* get the line from the other side */
2272		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
2273		mci->mci_lastuse = curtime();
2274
2275		if (p == NULL)
2276		{
2277			bool oldholderrs;
2278			extern char MsgBuf[];
2279
2280			/* if the remote end closed early, fake an error */
2281			if (errno == 0)
2282# ifdef ECONNRESET
2283				errno = ECONNRESET;
2284# else /* ECONNRESET */
2285				errno = EPIPE;
2286# endif /* ECONNRESET */
2287
2288			mci->mci_errno = errno;
2289			oldholderrs = HoldErrs;
2290			HoldErrs = TRUE;
2291			usrerr("451 4.4.1 reply: read error from %s",
2292			       CurHostName == NULL ? "NO_HOST" : CurHostName);
2293
2294			/* errors on QUIT should not be persistent */
2295			if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0)
2296				mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
2297
2298			/* if debugging, pause so we can see state */
2299			if (tTd(18, 100))
2300				(void) pause();
2301			mci->mci_state = MCIS_ERROR;
2302			save_errno = errno;
2303			smtpquit(m, mci, e);
2304# if XDEBUG
2305			{
2306				char wbuf[MAXLINE];
2307				int wbufleft = sizeof wbuf;
2308
2309				p = wbuf;
2310				if (e->e_to != NULL)
2311				{
2312					int plen;
2313
2314					snprintf(p, wbufleft, "%s... ",
2315						shortenstring(e->e_to, MAXSHORTSTR));
2316					plen = strlen(p);
2317					p += plen;
2318					wbufleft -= plen;
2319				}
2320				snprintf(p, wbufleft, "reply(%.100s) during %s",
2321					 CurHostName == NULL ? "NO_HOST" : CurHostName,
2322					 SmtpPhase);
2323				checkfd012(wbuf);
2324			}
2325# endif /* XDEBUG */
2326			errno = save_errno;
2327			HoldErrs = oldholderrs;
2328			return -1;
2329		}
2330		fixcrlf(bufp, TRUE);
2331
2332		/* EHLO failure is not a real error */
2333		if (e->e_xfp != NULL && (bufp[0] == '4' ||
2334		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
2335		{
2336			/* serious error -- log the previous command */
2337			if (SmtpNeedIntro)
2338			{
2339				/* inform user who we are chatting with */
2340				fprintf(CurEnv->e_xfp,
2341					"... while talking to %s:\n",
2342					CurHostName == NULL ? "NO_HOST" : CurHostName);
2343				SmtpNeedIntro = FALSE;
2344			}
2345			if (SmtpMsgBuffer[0] != '\0')
2346				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
2347			SmtpMsgBuffer[0] = '\0';
2348
2349			/* now log the message as from the other side */
2350			fprintf(e->e_xfp, "<<< %s\n", bufp);
2351		}
2352
2353		/* display the input for verbose mode */
2354		if (Verbose)
2355			nmessage("050 %s", bufp);
2356
2357		/* ignore improperly formatted input */
2358		if (!ISSMTPREPLY(bufp))
2359			continue;
2360
2361		if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2362		    enhstat != NULL &&
2363		    extenhsc(bufp + 4, ' ', enhstatcode) > 0)
2364			*enhstat = enhstatcode;
2365
2366		/* process the line */
2367		if (pfunc != NULL)
2368			(*pfunc)(bufp, firstline, m, mci, e);
2369
2370		firstline = FALSE;
2371
2372		/* decode the reply code */
2373		r = atoi(bufp);
2374
2375		/* extra semantics: 0xx codes are "informational" */
2376		if (r < 100)
2377			continue;
2378
2379		/* if no continuation lines, return this line */
2380		if (bufp[3] != '-')
2381			break;
2382
2383		/* first line of real reply -- ignore rest */
2384		bufp = junkbuf;
2385	}
2386
2387	/*
2388	**  Now look at SmtpReplyBuffer -- only care about the first
2389	**  line of the response from here on out.
2390	*/
2391
2392	/* save temporary failure messages for posterity */
2393	if (SmtpReplyBuffer[0] == '4' &&
2394	    (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0'))
2395		snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer);
2396
2397	/* reply code 421 is "Service Shutting Down" */
2398	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
2399	{
2400		/* send the quit protocol */
2401		mci->mci_state = MCIS_SSD;
2402		smtpquit(m, mci, e);
2403	}
2404
2405	return r;
2406}
2407/*
2408**  SMTPMESSAGE -- send message to server
2409**
2410**	Parameters:
2411**		f -- format
2412**		m -- the mailer to control formatting.
2413**		a, b, c -- parameters
2414**
2415**	Returns:
2416**		none.
2417**
2418**	Side Effects:
2419**		writes message to mci->mci_out.
2420*/
2421
2422/*VARARGS1*/
2423void
2424# ifdef __STDC__
2425smtpmessage(char *f, MAILER *m, MCI *mci, ...)
2426# else /* __STDC__ */
2427smtpmessage(f, m, mci, va_alist)
2428	char *f;
2429	MAILER *m;
2430	MCI *mci;
2431	va_dcl
2432# endif /* __STDC__ */
2433{
2434	VA_LOCAL_DECL
2435
2436	VA_START(mci);
2437	(void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
2438	VA_END;
2439
2440	if (tTd(18, 1) || Verbose)
2441		nmessage(">>> %s", SmtpMsgBuffer);
2442	if (TrafficLogFile != NULL)
2443		fprintf(TrafficLogFile, "%05d >>> %s\n",
2444			(int) getpid(), SmtpMsgBuffer);
2445	if (mci->mci_out != NULL)
2446	{
2447		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
2448			m == NULL ? "\r\n" : m->m_eol);
2449	}
2450	else if (tTd(18, 1))
2451	{
2452		dprintf("smtpmessage: NULL mci_out\n");
2453	}
2454}
2455
2456#endif /* SMTP */
2457