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