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