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