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