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