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