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