xref: /freebsd/contrib/sendmail/src/usersmtp.c (revision 13058a916175518dfbac6ce66b9b8e22ecf43155)
1 /*
2  * Copyright (c) 1998-2001 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 #ifndef lint
17 # if SMTP
18 static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.34 2001/06/26 21:55:23 gshapiro Exp $ (with SMTP)";
19 # else /* SMTP */
20 static char id[] = "@(#)$Id: usersmtp.c,v 8.245.4.34 2001/06/26 21:55:23 gshapiro Exp $ (without SMTP)";
21 # endif /* SMTP */
22 #endif /* ! lint */
23 
24 #include <sysexits.h>
25 
26 #if SMTP
27 
28 
29 static void	datatimeout __P((void));
30 static void	esmtp_check __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
31 static void	helo_options __P((char *, bool, MAILER *, MCI *, ENVELOPE *));
32 
33 /*
34 **  USERSMTP -- run SMTP protocol from the user end.
35 **
36 **	This protocol is described in RFC821.
37 */
38 
39 # define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
40 # define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
41 # define SMTPCLOSING	421			/* "Service Shutting Down" */
42 
43 #define ENHSCN(e, d)	(e) == NULL ? (d) : newstr(e)
44 
45 static char	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
46 static char	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
47 static bool	SmtpNeedIntro;		/* need "while talking" in transcript */
48 /*
49 **  SMTPINIT -- initialize SMTP.
50 **
51 **	Opens the connection and sends the initial protocol.
52 **
53 **	Parameters:
54 **		m -- mailer to create connection to.
55 **		mci -- the mailer connection info.
56 **		e -- the envelope.
57 **		onlyhelo -- send only helo command?
58 **
59 **	Returns:
60 **		none.
61 **
62 **	Side Effects:
63 **		creates connection and sends initial protocol.
64 */
65 
66 void
67 smtpinit(m, mci, e, onlyhelo)
68 	MAILER *m;
69 	register MCI *mci;
70 	ENVELOPE *e;
71 	bool onlyhelo;
72 {
73 	register int r;
74 	register char *p;
75 	register char *hn;
76 	char *enhsc;
77 
78 	enhsc = NULL;
79 	if (tTd(18, 1))
80 	{
81 		dprintf("smtpinit ");
82 		mci_dump(mci, FALSE);
83 	}
84 
85 	/*
86 	**  Open the connection to the mailer.
87 	*/
88 
89 	SmtpError[0] = '\0';
90 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
91 	if (CurHostName == NULL)
92 		CurHostName = MyHostName;
93 	SmtpNeedIntro = TRUE;
94 	switch (mci->mci_state)
95 	{
96 	  case MCIS_ACTIVE:
97 		/* need to clear old information */
98 		smtprset(m, mci, e);
99 		/* FALLTHROUGH */
100 
101 	  case MCIS_OPEN:
102 		if (!onlyhelo)
103 			return;
104 		break;
105 
106 	  case MCIS_ERROR:
107 	  case MCIS_QUITING:
108 	  case MCIS_SSD:
109 		/* shouldn't happen */
110 		smtpquit(m, mci, e);
111 		/* FALLTHROUGH */
112 
113 	  case MCIS_CLOSED:
114 		syserr("451 4.4.0 smtpinit: state CLOSED");
115 		return;
116 
117 	  case MCIS_OPENING:
118 		break;
119 	}
120 	if (onlyhelo)
121 		goto helo;
122 
123 	mci->mci_state = MCIS_OPENING;
124 
125 	/*
126 	**  Get the greeting message.
127 	**	This should appear spontaneously.  Give it five minutes to
128 	**	happen.
129 	*/
130 
131 	SmtpPhase = mci->mci_phase = "client greeting";
132 	sm_setproctitle(TRUE, e, "%s %s: %s",
133 			qid_printname(e), CurHostName, mci->mci_phase);
134 	r = reply(m, mci, e, TimeOuts.to_initial, esmtp_check, NULL);
135 	if (r < 0)
136 		goto tempfail1;
137 	if (REPLYTYPE(r) == 4)
138 		goto tempfail2;
139 	if (REPLYTYPE(r) != 2)
140 		goto unavailable;
141 
142 	/*
143 	**  Send the HELO command.
144 	**	My mother taught me to always introduce myself.
145 	*/
146 
147 helo:
148 	if (bitnset(M_ESMTP, m->m_flags) || bitnset(M_LMTP, m->m_flags))
149 		mci->mci_flags |= MCIF_ESMTP;
150 	hn = mci->mci_heloname ? mci->mci_heloname : MyHostName;
151 
152 tryhelo:
153 	if (bitnset(M_LMTP, m->m_flags))
154 	{
155 		smtpmessage("LHLO %s", m, mci, hn);
156 		SmtpPhase = mci->mci_phase = "client LHLO";
157 	}
158 	else if (bitset(MCIF_ESMTP, mci->mci_flags))
159 	{
160 		smtpmessage("EHLO %s", m, mci, hn);
161 		SmtpPhase = mci->mci_phase = "client EHLO";
162 	}
163 	else
164 	{
165 		smtpmessage("HELO %s", m, mci, hn);
166 		SmtpPhase = mci->mci_phase = "client HELO";
167 	}
168 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
169 			CurHostName, mci->mci_phase);
170 	r = reply(m, mci, e, TimeOuts.to_helo, helo_options, NULL);
171 	if (r < 0)
172 		goto tempfail1;
173 	else if (REPLYTYPE(r) == 5)
174 	{
175 		if (bitset(MCIF_ESMTP, mci->mci_flags) &&
176 		    !bitnset(M_LMTP, m->m_flags))
177 		{
178 			/* try old SMTP instead */
179 			mci->mci_flags &= ~MCIF_ESMTP;
180 			goto tryhelo;
181 		}
182 		goto unavailable;
183 	}
184 	else if (REPLYTYPE(r) != 2)
185 		goto tempfail2;
186 
187 	/*
188 	**  Check to see if we actually ended up talking to ourself.
189 	**  This means we didn't know about an alias or MX, or we managed
190 	**  to connect to an echo server.
191 	*/
192 
193 	p = strchr(&SmtpReplyBuffer[4], ' ');
194 	if (p != NULL)
195 		*p = '\0';
196 	if (!bitnset(M_NOLOOPCHECK, m->m_flags) &&
197 	    !bitnset(M_LMTP, m->m_flags) &&
198 	    strcasecmp(&SmtpReplyBuffer[4], MyHostName) == 0)
199 	{
200 		syserr("553 5.3.5 %s config error: mail loops back to me (MX problem?)",
201 			CurHostName);
202 		mci_setstat(mci, EX_CONFIG, "5.3.5",
203 			    "553 5.3.5 system config error");
204 		mci->mci_errno = 0;
205 		smtpquit(m, mci, e);
206 		return;
207 	}
208 
209 	/*
210 	**  If this is expected to be another sendmail, send some internal
211 	**  commands.
212 	*/
213 
214 	if (bitnset(M_INTERNAL, m->m_flags))
215 	{
216 		/* tell it to be verbose */
217 		smtpmessage("VERB", m, mci);
218 		r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, &enhsc);
219 		if (r < 0)
220 			goto tempfail1;
221 	}
222 
223 	if (mci->mci_state != MCIS_CLOSED)
224 	{
225 		mci->mci_state = MCIS_OPEN;
226 		return;
227 	}
228 
229 	/* got a 421 error code during startup */
230 
231   tempfail1:
232 	if (mci->mci_errno == 0)
233 		mci->mci_errno = errno;
234 	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.4.2"), NULL);
235 	if (mci->mci_state != MCIS_CLOSED)
236 		smtpquit(m, mci, e);
237 	return;
238 
239   tempfail2:
240 	if (mci->mci_errno == 0)
241 		mci->mci_errno = errno;
242 	/* XXX should use code from other end iff ENHANCEDSTATUSCODES */
243 	mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
244 		    SmtpReplyBuffer);
245 	if (mci->mci_state != MCIS_CLOSED)
246 		smtpquit(m, mci, e);
247 	return;
248 
249   unavailable:
250 	mci->mci_errno = errno;
251 	mci_setstat(mci, EX_UNAVAILABLE, "5.5.0", SmtpReplyBuffer);
252 	smtpquit(m, mci, e);
253 	return;
254 }
255 /*
256 **  ESMTP_CHECK -- check to see if this implementation likes ESMTP protocol
257 **
258 **	Parameters:
259 **		line -- the response line.
260 **		firstline -- set if this is the first line of the reply.
261 **		m -- the mailer.
262 **		mci -- the mailer connection info.
263 **		e -- the envelope.
264 **
265 **	Returns:
266 **		none.
267 */
268 
269 static void
270 esmtp_check(line, firstline, m, mci, e)
271 	char *line;
272 	bool firstline;
273 	MAILER *m;
274 	register MCI *mci;
275 	ENVELOPE *e;
276 {
277 	if (strstr(line, "ESMTP") != NULL)
278 		mci->mci_flags |= MCIF_ESMTP;
279 	if (strstr(line, "8BIT-OK") != NULL)
280 		mci->mci_flags |= MCIF_8BITOK;
281 }
282 # if SASL
283 /*
284 **  STR_UNION -- create the union of two lists
285 **
286 **	Parameters:
287 **		s1, s2 -- lists of items (separated by single blanks).
288 **
289 **	Returns:
290 **		the union of both lists.
291 */
292 
293 static char *
294 str_union(s1, s2)
295 	char *s1, *s2;
296 {
297 	char *hr, *h1, *h, *res;
298 	int l1, l2, rl;
299 
300 	if (s1 == NULL || *s1 == '\0')
301 		return s2;
302 	if (s2 == NULL || *s2 == '\0')
303 		return s1;
304 	l1 = strlen(s1);
305 	l2 = strlen(s2);
306 	rl = l1 + l2;
307 	res = (char *)xalloc(rl + 2);
308 	(void) strlcpy(res, s1, rl);
309 	hr = res + l1;
310 	h1 = s2;
311 	h = s2;
312 
313 	/* walk through s2 */
314 	while (h != NULL && *h1 != '\0')
315 	{
316 		/* is there something after the current word? */
317 		if ((h = strchr(h1, ' ')) != NULL)
318 			*h = '\0';
319 		l1 = strlen(h1);
320 
321 		/* does the current word appear in s1 ? */
322 		if (iteminlist(h1, s1, " ") == NULL)
323 		{
324 			/* add space as delimiter */
325 			*hr++ = ' ';
326 
327 			/* copy the item */
328 			memcpy(hr, h1, l1);
329 
330 			/* advance pointer in result list */
331 			hr += l1;
332 			*hr = '\0';
333 		}
334 		if (h != NULL)
335 		{
336 			/* there are more items */
337 			*h = ' ';
338 			h1 = h + 1;
339 		}
340 	}
341 	return res;
342 }
343 # endif /* SASL */
344 /*
345 **  HELO_OPTIONS -- process the options on a HELO line.
346 **
347 **	Parameters:
348 **		line -- the response line.
349 **		firstline -- set if this is the first line of the reply.
350 **		m -- the mailer.
351 **		mci -- the mailer connection info.
352 **		e -- the envelope.
353 **
354 **	Returns:
355 **		none.
356 */
357 
358 static void
359 helo_options(line, firstline, m, mci, e)
360 	char *line;
361 	bool firstline;
362 	MAILER *m;
363 	register MCI *mci;
364 	ENVELOPE *e;
365 {
366 	register char *p;
367 
368 	if (firstline)
369 	{
370 # if SASL
371 		if (mci->mci_saslcap != NULL)
372 			sm_free(mci->mci_saslcap);
373 		mci->mci_saslcap = NULL;
374 # endif /* SASL */
375 		return;
376 	}
377 
378 	if (strlen(line) < (SIZE_T) 5)
379 		return;
380 	line += 4;
381 	p = strpbrk(line, " =");
382 	if (p != NULL)
383 		*p++ = '\0';
384 	if (strcasecmp(line, "size") == 0)
385 	{
386 		mci->mci_flags |= MCIF_SIZE;
387 		if (p != NULL)
388 			mci->mci_maxsize = atol(p);
389 	}
390 	else if (strcasecmp(line, "8bitmime") == 0)
391 	{
392 		mci->mci_flags |= MCIF_8BITMIME;
393 		mci->mci_flags &= ~MCIF_7BIT;
394 	}
395 	else if (strcasecmp(line, "expn") == 0)
396 		mci->mci_flags |= MCIF_EXPN;
397 	else if (strcasecmp(line, "dsn") == 0)
398 		mci->mci_flags |= MCIF_DSN;
399 	else if (strcasecmp(line, "enhancedstatuscodes") == 0)
400 		mci->mci_flags |= MCIF_ENHSTAT;
401 # if STARTTLS
402 	else if (strcasecmp(line, "starttls") == 0)
403 		mci->mci_flags |= MCIF_TLS;
404 # endif /* STARTTLS */
405 # if SASL
406 	else if (strcasecmp(line, "auth") == 0)
407 	{
408 		if (p != NULL && *p != '\0')
409 		{
410 			if (mci->mci_saslcap != NULL)
411 			{
412 				char *h;
413 
414 				/*
415 				**  create the union with previous auth
416 				**  offerings because we recognize "auth "
417 				**  and "auth=" (old format).
418 				*/
419 				h = mci->mci_saslcap;
420 				mci->mci_saslcap = str_union(h, p);
421 				if (h != mci->mci_saslcap)
422 					sm_free(h);
423 				mci->mci_flags |= MCIF_AUTH;
424 			}
425 			else
426 			{
427 				int l;
428 
429 				l = strlen(p) + 1;
430 				mci->mci_saslcap = (char *)xalloc(l);
431 				(void) strlcpy(mci->mci_saslcap, p, l);
432 				mci->mci_flags |= MCIF_AUTH;
433 			}
434 		}
435 	}
436 # endif /* SASL */
437 }
438 # if SASL
439 
440 /*
441 **  GETSASLDATA -- process the challenges from the SASL protocol
442 **
443 **	This gets the relevant sasl response data out of the reply
444 **	from the server
445 **
446 **	Parameters:
447 **		line -- the response line.
448 **		firstline -- set if this is the first line of the reply.
449 **		m -- the mailer.
450 **		mci -- the mailer connection info.
451 **		e -- the envelope.
452 **
453 **	Returns:
454 **		none.
455 */
456 
457 void
458 getsasldata(line, firstline, m, mci, e)
459 	char *line;
460 	bool firstline;
461 	MAILER *m;
462 	register MCI *mci;
463 	ENVELOPE *e;
464 {
465 	int len;
466 	char *out;
467 	int result;
468 
469 	/* if not a continue we don't care about it */
470 	if ((strlen(line) <= 4) ||
471 	    (line[0] != '3') ||
472 	    (line[1] != '3') ||
473 	    (line[2] != '4'))
474 	{
475 		mci->mci_sasl_string = NULL;
476 		return;
477 	}
478 
479 	/* forget about "334 " */
480 	line += 4;
481 	len = strlen(line);
482 
483 	out = xalloc(len + 1);
484 	result = sasl_decode64(line, len, out, (u_int *)&len);
485 	if (result != SASL_OK)
486 	{
487 		len = 0;
488 		*out = '\0';
489 	}
490 	if (mci->mci_sasl_string != NULL)
491 	{
492 		if (mci->mci_sasl_string_len <= len)
493 		{
494 			sm_free(mci->mci_sasl_string);
495 			mci->mci_sasl_string = xalloc(len + 1);
496 		}
497 	}
498 	else
499 		mci->mci_sasl_string = xalloc(len + 1);
500 	/* XXX this is probably leaked */
501 	memcpy(mci->mci_sasl_string, out, len);
502 	mci->mci_sasl_string[len] = '\0';
503 	mci->mci_sasl_string_len = len;
504 	sm_free(out);
505 	return;
506 }
507 
508 /*
509 **  READAUTH -- read auth value from a file
510 **
511 **	Parameters:
512 **		l -- line to define.
513 **		filename -- name of file to read.
514 **		safe -- if set, this is a safe read.
515 **
516 **	Returns:
517 **		line from file
518 **
519 **	Side Effects:
520 **		overwrites local static buffer. The caller should copy
521 **		the result.
522 **
523 */
524 
525 /* lines in authinfo file */
526 # define SASL_USER	1
527 # define SASL_AUTHID	2
528 # define SASL_PASSWORD	3
529 # define SASL_DEFREALM	4
530 # define SASL_MECH	5
531 
532 static char *sasl_info_name[] =
533 {
534 	"",
535 	"user id",
536 	"authorization id",
537 	"password",
538 	"realm",
539 	"mechanism"
540 };
541 
542 static char *
543 readauth(l, filename, safe)
544 	int l;
545 	char *filename;
546 	bool safe;
547 {
548 	FILE *f;
549 	long sff;
550 	pid_t pid;
551 	int lc;
552 	static char buf[MAXLINE];
553 
554 	if (filename == NULL || filename[0] == '\0')
555 		return "";
556 #if !_FFR_ALLOW_SASLINFO
557 	/*
558 	**  make sure we don't use a program that is not
559 	**  accesible to the user who specified a different authinfo file.
560 	**  However, currently we don't pass this info (authinfo file
561 	**  specified by user) around, so we just turn off program access.
562 	*/
563 	if (filename[0] == '|')
564 	{
565 		auto int fd;
566 		int i;
567 		char *p;
568 		char *argv[MAXPV + 1];
569 
570 		i = 0;
571 		for (p = strtok(&filename[1], " \t"); p != NULL;
572 		     p = strtok(NULL, " \t"))
573 		{
574 			if (i >= MAXPV)
575 				break;
576 			argv[i++] = p;
577 		}
578 		argv[i] = NULL;
579 		pid = prog_open(argv, &fd, CurEnv);
580 		if (pid < 0)
581 			f = NULL;
582 		else
583 			f = fdopen(fd, "r");
584 	}
585 	else
586 #endif /* !_FFR_ALLOW_SASLINFO */
587 	{
588 		pid = -1;
589 		sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
590 		      | SFF_NOGWFILES | SFF_NOWWFILES | SFF_NORFILES;
591 		if (DontLockReadFiles)
592 			sff |= SFF_NOLOCK;
593 #if _FFR_ALLOW_SASLINFO
594 		/*
595 		**  XXX: make sure we don't read or open files that are not
596 		**  accesible to the user who specified a different authinfo
597 		**  file.
598 		*/
599 		sff |= SFF_MUSTOWN;
600 #else /* _FFR_ALLOW_SASLINFO */
601 		if (safe)
602 			sff |= SFF_OPENASROOT;
603 #endif /* _FFR_ALLOW_SASLINFO */
604 
605 		f = safefopen(filename, O_RDONLY, 0, sff);
606 	}
607 	if (f == NULL)
608 	{
609 		syserr("readauth: cannot open %s", filename);
610 		return "";
611 	}
612 
613 	lc = 0;
614 	while (lc < l && fgets(buf, sizeof buf, f) != NULL)
615 	{
616 		if (buf[0] != '#')
617 			lc++;
618 	}
619 
620 	(void) fclose(f);
621 	if (pid > 0)
622 		(void) waitfor(pid);
623 	if (lc < l)
624 	{
625 		if (LogLevel >= 9)
626 			sm_syslog(LOG_WARNING, NOQID, "SASL: error: can't read %s from %s",
627 			  sasl_info_name[l], filename);
628 		return "";
629 	}
630 	lc = strlen(buf) - 1;
631 	if (lc >= 0)
632 		buf[lc] = '\0';
633 	if (tTd(95, 6))
634 		dprintf("readauth(%s, %d) = '%s'\n", filename, l, buf);
635 	return buf;
636 }
637 
638 #  ifndef __attribute__
639 #   define __attribute__(x)
640 #  endif /* ! __attribute__ */
641 
642 static int getsimple	__P((void *, int, const char **, unsigned *));
643 static int getsecret	__P((sasl_conn_t *, void *, int, sasl_secret_t **));
644 static int saslgetrealm	__P((void *, int, const char **, const char **));
645 
646 static sasl_callback_t callbacks[] =
647 {
648 	{	SASL_CB_GETREALM,	&saslgetrealm,	NULL	},
649 # define CB_GETREALM_IDX	0
650 	{	SASL_CB_PASS,		&getsecret,	NULL	},
651 # define CB_PASS_IDX	1
652 	{	SASL_CB_USER,		&getsimple,	NULL	},
653 # define CB_USER_IDX	2
654 	{	SASL_CB_AUTHNAME,	&getsimple,	NULL	},
655 # define CB_AUTHNAME_IDX	3
656 	{	SASL_CB_VERIFYFILE,	&safesaslfile,	NULL	},
657 	{	SASL_CB_LIST_END,	NULL,		NULL	}
658 };
659 
660 /*
661 **  GETSIMPLE -- callback to get userid or authid
662 **
663 **	Parameters:
664 **		context -- unused
665 **		id -- what to do
666 **		result -- (pointer to) result
667 **		len -- (pointer to) length of result
668 **
669 **	Returns:
670 **		OK/failure values
671 */
672 
673 static int
674 getsimple(context, id, result, len)
675 	void *context __attribute__((unused));
676 	int id;
677 	const char **result;
678 	unsigned *len;
679 {
680 	char *h;
681 #  if SASL > 10509
682 	int addrealm;
683 	static int addedrealm = FALSE;
684 #  endif /* SASL > 10509 */
685 	static char *user = NULL;
686 	static char *authid = NULL;
687 
688 	if (result == NULL)
689 		return SASL_BADPARAM;
690 
691 	switch (id)
692 	{
693 	  case SASL_CB_USER:
694 		if (user == NULL)
695 		{
696 			h = readauth(SASL_USER, SASLInfo, TRUE);
697 			user = newstr(h);
698 		}
699 		*result = user;
700 		if (tTd(95, 5))
701 			dprintf("AUTH username '%s'\n", *result);
702 		if (len != NULL)
703 			*len = user ? strlen(user) : 0;
704 		break;
705 
706 	  case SASL_CB_AUTHNAME:
707 #  if SASL > 10509
708 		/* XXX maybe other mechanisms too?! */
709 		addrealm = context != NULL &&
710 			   strcasecmp(context, "CRAM-MD5") == 0;
711 		if (addedrealm != addrealm && authid != NULL)
712 		{
713 #  if SASL > 10522
714 			/*
715 			**  digest-md5 prior to 1.5.23 doesn't copy the
716 			**  value it gets from the callback, but free()s
717 			**  it later on
718 			**  workaround: don't free() it here
719 			**  this can cause a memory leak!
720 			*/
721 
722 			sm_free(authid);
723 #  endif /* SASL > 10522 */
724 			authid = NULL;
725 			addedrealm = addrealm;
726 		}
727 #  endif /* SASL > 10509 */
728 		if (authid == NULL)
729 		{
730 			h = readauth(SASL_AUTHID, SASLInfo, TRUE);
731 #  if SASL > 10509
732 			if (addrealm && strchr(h, '@') == NULL)
733 			{
734 				size_t l;
735 				char *realm;
736 
737 				realm = callbacks[CB_GETREALM_IDX].context;
738 				l = strlen(h) + strlen(realm) + 2;
739 				authid = xalloc(l);
740 				snprintf(authid, l, "%s@%s", h, realm);
741 			}
742 			else
743 #  endif /* SASL > 10509 */
744 				authid = newstr(h);
745 		}
746 		*result = authid;
747 		if (tTd(95, 5))
748 			dprintf("AUTH authid '%s'\n", *result);
749 		if (len != NULL)
750 			*len = authid ? strlen(authid) : 0;
751 		break;
752 
753 	  case SASL_CB_LANGUAGE:
754 		*result = NULL;
755 		if (len != NULL)
756 			*len = 0;
757 		break;
758 
759 	  default:
760 		return SASL_BADPARAM;
761 	}
762 	return SASL_OK;
763 }
764 
765 /*
766 **  GETSECRET -- callback to get password
767 **
768 **	Parameters:
769 **		conn -- connection information
770 **		context -- unused
771 **		id -- what to do
772 **		psecret -- (pointer to) result
773 **
774 **	Returns:
775 **		OK/failure values
776 */
777 
778 static int
779 getsecret(conn, context, id, psecret)
780 	sasl_conn_t *conn;
781 	void *context __attribute__((unused));
782 	int id;
783 	sasl_secret_t **psecret;
784 {
785 	char *h;
786 	int len;
787 	static char *authpass = NULL;
788 
789 	if (conn == NULL || psecret == NULL || id != SASL_CB_PASS)
790 		return SASL_BADPARAM;
791 
792 	if (authpass == NULL)
793 	{
794 		h = readauth(SASL_PASSWORD, SASLInfo, TRUE);
795 		authpass = newstr(h);
796 	}
797 	len = strlen(authpass);
798 	*psecret = (sasl_secret_t *) xalloc(sizeof(sasl_secret_t) + len + 1);
799 	(void) strlcpy((*psecret)->data, authpass, len + 1);
800 	(*psecret)->len = len;
801 	return SASL_OK;
802 }
803 
804 /*
805 **  SAFESASLFILE -- callback for sasl: is file safe?
806 **
807 **	Parameters:
808 **		context -- pointer to context between invocations (unused)
809 **		file -- name of file to check
810 **		type -- type of file to check
811 **
812 **	Returns:
813 **		SASL_OK: file can be used
814 **		SASL_CONTINUE: don't use file
815 **		SASL_FAIL: failure (not used here)
816 **
817 */
818 int
819 # if SASL > 10515
820 safesaslfile(context, file, type)
821 # else /* SASL > 10515 */
822 safesaslfile(context, file)
823 # endif /* SASL > 10515 */
824 	void *context;
825 	char *file;
826 # if SASL > 10515
827 	int type;
828 # endif /* SASL > 10515 */
829 {
830 	long sff;
831 	int r;
832 	char *p;
833 
834 	if (file == NULL || *file == '\0')
835 		return SASL_OK;
836 
837 	sff = SFF_SAFEDIRPATH|SFF_NOWLINK|SFF_NOGWFILES|SFF_NOWWFILES|SFF_ROOTOK;
838 	if ((p = strrchr(file, '/')) == NULL)
839 		p = file;
840 	else
841 		++p;
842 
843 # if SASL <= 10515
844 	/* everything beside libs and .conf files must not be readable */
845 	r = strlen(p);
846 	if ((r <= 3 || strncmp(p, "lib", 3) != 0) &&
847 	    (r <= 5 || strncmp(p + r - 5, ".conf", 5) != 0)
848 #  if _FFR_UNSAFE_SASL
849 	    && !bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail)
850 #  endif /* _FFR_UNSAFE_SASL */
851 	   )
852 		sff |= SFF_NORFILES;
853 # else /* SASL > 10515 */
854 	/* files containing passwords should be not readable */
855 	if (type == SASL_VRFY_PASSWD)
856 	{
857 #  if _FFR_UNSAFE_SASL
858 		if (bitnset(DBS_GROUPREADABLESASLFILE, DontBlameSendmail))
859 			sff |= SFF_NOWRFILES;
860 		else
861 #  endif /* _FFR_UNSAFE_SASL */
862 			sff |= SFF_NORFILES;
863 	}
864 # endif /* SASL <= 10515 */
865 
866 	p = file;
867 	if ((r = safefile(p, RunAsUid, RunAsGid, RunAsUserName, sff,
868 			  S_IRUSR, NULL)) == 0)
869 		return SASL_OK;
870 	if (LogLevel >= 11 || (r != ENOENT && LogLevel >= 9))
871 		sm_syslog(LOG_WARNING, NOQID, "error: safesasl(%s) failed: %s",
872 			  p, errstring(r));
873 	return SASL_CONTINUE;
874 }
875 
876 /*
877 **  SASLGETREALM -- return the realm for SASL
878 **
879 **	return the realm for the client
880 **
881 **	Parameters:
882 **		context -- context shared between invocations
883 **			here: realm to return
884 **		availrealms -- list of available realms
885 **			{realm, realm, ...}
886 **		result -- pointer to result
887 **
888 **	Returns:
889 **		failure/success
890 */
891 static int
892 saslgetrealm(context, id, availrealms, result)
893 	void *context;
894 	int id;
895 	const char **availrealms;
896 	const char **result;
897 {
898 	if (LogLevel > 12)
899 		sm_syslog(LOG_INFO, NOQID, "saslgetrealm: realm %s available realms %s",
900 			  context == NULL ? "<No Context>" : (char *) context,
901 			  (availrealms == NULL || *availrealms == NULL) ? "<No Realms>" : *availrealms);
902 	if (context == NULL)
903 		return SASL_FAIL;
904 
905 	/* check whether context is in list? */
906 	if (availrealms != NULL && *availrealms != NULL)
907 	{
908 		if (iteminlist(context, (char *)(*availrealms + 1), " ,}") ==
909 		    NULL)
910 		{
911 			if (LogLevel > 8)
912 				sm_syslog(LOG_ERR, NOQID,
913 					  "saslgetrealm: realm %s not in list %s",
914 					  context, *availrealms);
915 			return SASL_FAIL;
916 		}
917 	}
918 	*result = (char *)context;
919 	return SASL_OK;
920 }
921 /*
922 **  ITEMINLIST -- does item appear in list?
923 **
924 **	Check whether item appears in list (which must be separated by a
925 **	character in delim) as a "word", i.e. it must appear at the begin
926 **	of the list or after a space, and it must end with a space or the
927 **	end of the list.
928 **
929 **	Parameters:
930 **		item -- item to search.
931 **		list -- list of items.
932 **		delim -- list of delimiters.
933 **
934 **	Returns:
935 **		pointer to occurrence (NULL if not found).
936 */
937 
938 char *
939 iteminlist(item, list, delim)
940 	char *item;
941 	char *list;
942 	char *delim;
943 {
944 	char *s;
945 	int len;
946 
947 	if (list == NULL || *list == '\0')
948 		return NULL;
949 	if (item == NULL || *item == '\0')
950 		return NULL;
951 	s = list;
952 	len = strlen(item);
953 	while (s != NULL && *s != '\0')
954 	{
955 		if (strncasecmp(s, item, len) == 0 &&
956 		    (s[len] == '\0' || strchr(delim, s[len]) != NULL))
957 			return s;
958 		s = strpbrk(s, delim);
959 		if (s != NULL)
960 			while (*++s == ' ')
961 				continue;
962 	}
963 	return NULL;
964 }
965 /*
966 **  REMOVEMECH -- remove item [rem] from list [list]
967 **
968 **	Parameters:
969 **		rem -- item to remove
970 **		list -- list of items
971 **
972 **	Returns:
973 **		pointer to new list (NULL in case of error).
974 */
975 
976 char *
977 removemech(rem, list)
978 	char *rem;
979 	char *list;
980 {
981 	char *ret;
982 	char *needle;
983 	int len;
984 
985 	if (list == NULL)
986 		return NULL;
987 	if (rem == NULL || *rem == '\0')
988 	{
989 		/* take out what? */
990 		return NULL;
991 	}
992 
993 	/* find the item in the list */
994 	if ((needle = iteminlist(rem, list, " ")) == NULL)
995 	{
996 		/* not in there: return original */
997 		return list;
998 	}
999 
1000 	/* length of string without rem */
1001 	len = strlen(list) - strlen(rem);
1002 	if (len == 0)
1003 	{
1004 		ret = xalloc(1);  /* XXX leaked */
1005 		*ret = '\0';
1006 		return ret;
1007 	}
1008 	ret = xalloc(len);  /* XXX leaked */
1009 	memset(ret, '\0', len);
1010 
1011 	/* copy from start to removed item */
1012 	memcpy(ret, list, needle - list);
1013 
1014 	/* length of rest of string past removed item */
1015 	len = strlen(needle) - strlen(rem) - 1;
1016 	if (len > 0)
1017 	{
1018 		/* not last item -- copy into string */
1019 		memcpy(ret + (needle - list),
1020 		       list + (needle - list) + strlen(rem) + 1,
1021 		       len);
1022 	}
1023 	else
1024 		ret[(needle - list) - 1] = '\0';
1025 	return ret;
1026 }
1027 /*
1028 **  INTERSECT -- create the intersection between two lists
1029 **
1030 **	Parameters:
1031 **		s1, s2 -- lists of items (separated by single blanks).
1032 **
1033 **	Returns:
1034 **		the intersection of both lists.
1035 */
1036 
1037 char *
1038 intersect(s1, s2)
1039 	char *s1, *s2;
1040 {
1041 	char *hr, *h1, *h, *res;
1042 	int l1, l2, rl;
1043 
1044 	if (s1 == NULL || s2 == NULL)	/* NULL string(s) -> NULL result */
1045 		return NULL;
1046 	l1 = strlen(s1);
1047 	l2 = strlen(s2);
1048 	rl = min(l1, l2);
1049 	res = (char *)xalloc(rl + 1);
1050 	*res = '\0';
1051 	if (rl == 0)	/* at least one string empty? */
1052 		return res;
1053 	hr = res;
1054 	h1 = s1;
1055 	h = s1;
1056 
1057 	/* walk through s1 */
1058 	while (h != NULL && *h1 != '\0')
1059 	{
1060 		/* is there something after the current word? */
1061 		if ((h = strchr(h1, ' ')) != NULL)
1062 			*h = '\0';
1063 		l1 = strlen(h1);
1064 
1065 		/* does the current word appear in s2 ? */
1066 		if (iteminlist(h1, s2, " ") != NULL)
1067 		{
1068 			/* add a blank if not first item */
1069 			if (hr != res)
1070 				*hr++ = ' ';
1071 
1072 			/* copy the item */
1073 			memcpy(hr, h1, l1);
1074 
1075 			/* advance pointer in result list */
1076 			hr += l1;
1077 			*hr = '\0';
1078 		}
1079 		if (h != NULL)
1080 		{
1081 			/* there are more items */
1082 			*h = ' ';
1083 			h1 = h + 1;
1084 		}
1085 	}
1086 	return res;
1087 }
1088 /*
1089 **  ATTEMPTAUTH -- try to AUTHenticate using one mechanism
1090 **
1091 **	Parameters:
1092 **		m -- the mailer.
1093 **		mci -- the mailer connection structure.
1094 **		e -- the envelope (including the sender to specify).
1095 **		mechused - filled in with mechanism used
1096 **
1097 **	Returns:
1098 **		EX_OK/EX_TEMPFAIL
1099 */
1100 
1101 int
1102 attemptauth(m, mci, e, mechused)
1103 	MAILER *m;
1104 	MCI *mci;
1105 	ENVELOPE *e;
1106 	char **mechused;
1107 {
1108 	int saslresult, smtpresult;
1109 	sasl_external_properties_t ssf;
1110 	sasl_interact_t *client_interact = NULL;
1111 	char *out;
1112 	unsigned int outlen;
1113 	static char *mechusing;
1114 	sasl_security_properties_t ssp;
1115 	char in64[MAXOUTLEN];
1116 # if NETINET
1117 	extern SOCKADDR CurHostAddr;
1118 # endif /* NETINET */
1119 
1120 	*mechused = NULL;
1121 	if (mci->mci_conn != NULL)
1122 	{
1123 		sasl_dispose(&(mci->mci_conn));
1124 
1125 		/* just in case, sasl_dispose() should take care of it */
1126 		mci->mci_conn = NULL;
1127 	}
1128 
1129 	/* make a new client sasl connection */
1130 	saslresult = sasl_client_new(bitnset(M_LMTP, m->m_flags) ? "lmtp"
1131 								 : "smtp",
1132 				     CurHostName, NULL, 0, &mci->mci_conn);
1133 
1134 	/* set properties */
1135 	(void) memset(&ssp, '\0', sizeof ssp);
1136 #  if SFIO
1137 	/* XXX should these be options settable via .cf ? */
1138 	/* ssp.min_ssf = 0; is default due to memset() */
1139 	{
1140 		ssp.max_ssf = INT_MAX;
1141 		ssp.maxbufsize = MAXOUTLEN;
1142 #   if 0
1143 		ssp.security_flags = SASL_SEC_NOPLAINTEXT;
1144 #   endif /* 0 */
1145 	}
1146 #  endif /* SFIO */
1147 	saslresult = sasl_setprop(mci->mci_conn, SASL_SEC_PROPS, &ssp);
1148 	if (saslresult != SASL_OK)
1149 		return EX_TEMPFAIL;
1150 
1151 	/* external security strength factor, authentication id */
1152 	ssf.ssf = 0;
1153 	ssf.auth_id = NULL;
1154 # if _FFR_EXT_MECH
1155 	out = macvalue(macid("{cert_subject}", NULL), e);
1156 	if (out != NULL && *out != '\0')
1157 		ssf.auth_id = out;
1158 	out = macvalue(macid("{cipher_bits}", NULL), e);
1159 	if (out != NULL && *out != '\0')
1160 		ssf.ssf = atoi(out);
1161 # endif /* _FFR_EXT_MECH */
1162 	saslresult = sasl_setprop(mci->mci_conn, SASL_SSF_EXTERNAL, &ssf);
1163 	if (saslresult != SASL_OK)
1164 		return EX_TEMPFAIL;
1165 
1166 # if NETINET
1167 	/* set local/remote ipv4 addresses */
1168 	if (mci->mci_out != NULL && CurHostAddr.sa.sa_family == AF_INET)
1169 	{
1170 		SOCKADDR_LEN_T addrsize;
1171 		struct sockaddr_in saddr_l;
1172 
1173 		if (sasl_setprop(mci->mci_conn, SASL_IP_REMOTE,
1174 				 (struct sockaddr_in *) &CurHostAddr)
1175 		    != SASL_OK)
1176 			return EX_TEMPFAIL;
1177 		addrsize = sizeof(struct sockaddr_in);
1178 		if (getsockname(fileno(mci->mci_out),
1179 				(struct sockaddr *) &saddr_l, &addrsize) == 0)
1180 		{
1181 			if (sasl_setprop(mci->mci_conn, SASL_IP_LOCAL,
1182 					 &saddr_l) != SASL_OK)
1183 				return EX_TEMPFAIL;
1184 		}
1185 	}
1186 # endif /* NETINET */
1187 
1188 	/* start client side of sasl */
1189 	saslresult = sasl_client_start(mci->mci_conn, mci->mci_saslcap,
1190 				       NULL, &client_interact,
1191 				       &out, &outlen,
1192 				       (const char **)&mechusing);
1193 	callbacks[CB_AUTHNAME_IDX].context = mechusing;
1194 
1195 	if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1196 	{
1197 #  if SFIO
1198 		if (saslresult == SASL_NOMECH && LogLevel > 8)
1199 		{
1200 			sm_syslog(LOG_NOTICE, e->e_id,
1201 				  "available AUTH mechanisms do not fulfill requirements");
1202 		}
1203 #  endif /* SFIO */
1204 		return EX_TEMPFAIL;
1205 	}
1206 
1207 	*mechused = mechusing;
1208 
1209 	/* send the info across the wire */
1210 	if (outlen > 0)
1211 	{
1212 		saslresult = sasl_encode64(out, outlen, in64, MAXOUTLEN, NULL);
1213 		if (saslresult != SASL_OK) /* internal error */
1214 		{
1215 			if (LogLevel > 8)
1216 				sm_syslog(LOG_ERR, e->e_id,
1217 					"encode64 for AUTH failed");
1218 			return EX_TEMPFAIL;
1219 		}
1220 		smtpmessage("AUTH %s %s", m, mci, mechusing, in64);
1221 	}
1222 	else
1223 	{
1224 		smtpmessage("AUTH %s", m, mci, mechusing);
1225 	}
1226 
1227 	/* get the reply */
1228 	smtpresult = reply(m, mci, e, TimeOuts.to_datafinal, getsasldata, NULL);
1229 	/* which timeout? XXX */
1230 
1231 	for (;;)
1232 	{
1233 		/* check return code from server */
1234 		if (smtpresult == 235)
1235 		{
1236 			define(macid("{auth_type}", NULL),
1237 			       newstr(mechusing), e);
1238 #  if !SFIO
1239 			if (LogLevel > 9)
1240 				sm_syslog(LOG_INFO, NOQID,
1241 					  "SASL: outgoing connection to %.64s: mech=%.16s",
1242 					  mci->mci_host, mechusing);
1243 #  endif /* !SFIO */
1244 			return EX_OK;
1245 		}
1246 		if (smtpresult == -1)
1247 			return EX_IOERR;
1248 		if (smtpresult != 334)
1249 			return EX_TEMPFAIL;
1250 
1251 		saslresult = sasl_client_step(mci->mci_conn,
1252 					      mci->mci_sasl_string,
1253 					      mci->mci_sasl_string_len,
1254 					      &client_interact,
1255 					      &out, &outlen);
1256 
1257 		if (saslresult != SASL_OK && saslresult != SASL_CONTINUE)
1258 		{
1259 			if (tTd(95, 5))
1260 				dprintf("AUTH FAIL: %s (%d)\n",
1261 					sasl_errstring(saslresult, NULL, NULL),
1262 					saslresult);
1263 
1264 			/* fail deliberately, see RFC 2254 4. */
1265 			smtpmessage("*", m, mci);
1266 
1267 			/*
1268 			**  but we should only fail for this authentication
1269 			**  mechanism; how to do that?
1270 			*/
1271 
1272 			smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
1273 					   getsasldata, NULL);
1274 			return EX_TEMPFAIL;
1275 		}
1276 
1277 		if (outlen > 0)
1278 		{
1279 			saslresult = sasl_encode64(out, outlen, in64,
1280 						   MAXOUTLEN, NULL);
1281 			if (saslresult != SASL_OK)
1282 			{
1283 				/* give an error reply to the other side! */
1284 				smtpmessage("*", m, mci);
1285 				return EX_TEMPFAIL;
1286 			}
1287 		}
1288 		else
1289 			in64[0] = '\0';
1290 		smtpmessage("%s", m, mci, in64);
1291 		smtpresult = reply(m, mci, e, TimeOuts.to_datafinal,
1292 				   getsasldata, NULL);
1293 		/* which timeout? XXX */
1294 	}
1295 	/* NOTREACHED */
1296 }
1297 
1298 /*
1299 **  SMTPAUTH -- try to AUTHenticate
1300 **
1301 **	This will try mechanisms in the order the sasl library decided until:
1302 **	- there are no more mechanisms
1303 **	- a mechanism succeeds
1304 **	- the sasl library fails initializing
1305 **
1306 **	Parameters:
1307 **		m -- the mailer.
1308 **		mci -- the mailer connection info.
1309 **		e -- the envelope.
1310 **
1311 **	Returns:
1312 **		EX_OK/EX_TEMPFAIL
1313 */
1314 
1315 int
1316 smtpauth(m, mci, e)
1317 	MAILER *m;
1318 	MCI *mci;
1319 	ENVELOPE *e;
1320 {
1321 	int result;
1322 	char *mechused;
1323 	char *h;
1324 	static char *defrealm = NULL;
1325 	static char *mechs = NULL;
1326 
1327 	mci->mci_sasl_auth = FALSE;
1328 	if (defrealm == NULL)
1329 	{
1330 		h = readauth(SASL_DEFREALM, SASLInfo, TRUE);
1331 		if (h != NULL && *h != '\0')
1332 			defrealm = newstr(h);
1333 	}
1334 	if (defrealm == NULL || *defrealm == '\0')
1335 		defrealm = newstr(macvalue('j', CurEnv));
1336 	callbacks[CB_GETREALM_IDX].context = defrealm;
1337 
1338 # if _FFR_DEFAUTHINFO_MECHS
1339 	if (mechs == NULL)
1340 	{
1341 		h = readauth(SASL_MECH, SASLInfo, TRUE);
1342 		if (h != NULL && *h != '\0')
1343 			mechs = newstr(h);
1344 	}
1345 # endif /* _FFR_DEFAUTHINFO_MECHS */
1346 	if (mechs == NULL || *mechs == '\0')
1347 		mechs = AuthMechanisms;
1348 	mci->mci_saslcap = intersect(mechs, mci->mci_saslcap);
1349 
1350 	/* initialize sasl client library */
1351 	result = sasl_client_init(callbacks);
1352 	if (result != SASL_OK)
1353 		return EX_TEMPFAIL;
1354 	do
1355 	{
1356 		result = attemptauth(m, mci, e, &mechused);
1357 		if (result == EX_OK)
1358 			mci->mci_sasl_auth = TRUE;
1359 		else if (result == EX_TEMPFAIL)
1360 		{
1361 			mci->mci_saslcap = removemech(mechused,
1362 						      mci->mci_saslcap);
1363 			if (mci->mci_saslcap == NULL ||
1364 			    *(mci->mci_saslcap) == '\0')
1365 				return EX_TEMPFAIL;
1366 		}
1367 		else	/* all others for now */
1368 			return EX_TEMPFAIL;
1369 	} while (result != EX_OK);
1370 	return result;
1371 }
1372 # endif /* SASL */
1373 
1374 /*
1375 **  SMTPMAILFROM -- send MAIL command
1376 **
1377 **	Parameters:
1378 **		m -- the mailer.
1379 **		mci -- the mailer connection structure.
1380 **		e -- the envelope (including the sender to specify).
1381 */
1382 
1383 int
1384 smtpmailfrom(m, mci, e)
1385 	MAILER *m;
1386 	MCI *mci;
1387 	ENVELOPE *e;
1388 {
1389 	int r;
1390 	char *bufp;
1391 	char *bodytype;
1392 	char buf[MAXNAME + 1];
1393 	char optbuf[MAXLINE];
1394 	char *enhsc;
1395 
1396 	if (tTd(18, 2))
1397 		dprintf("smtpmailfrom: CurHost=%s\n", CurHostName);
1398 	enhsc = NULL;
1399 
1400 	/* set up appropriate options to include */
1401 	if (bitset(MCIF_SIZE, mci->mci_flags) && e->e_msgsize > 0)
1402 	{
1403 		snprintf(optbuf, sizeof optbuf, " SIZE=%ld", e->e_msgsize);
1404 		bufp = &optbuf[strlen(optbuf)];
1405 	}
1406 	else
1407 	{
1408 		optbuf[0] = '\0';
1409 		bufp = optbuf;
1410 	}
1411 
1412 	bodytype = e->e_bodytype;
1413 	if (bitset(MCIF_8BITMIME, mci->mci_flags))
1414 	{
1415 		if (bodytype == NULL &&
1416 		    bitset(MM_MIME8BIT, MimeMode) &&
1417 		    bitset(EF_HAS8BIT, e->e_flags) &&
1418 		    !bitset(EF_DONT_MIME, e->e_flags) &&
1419 		    !bitnset(M_8BITS, m->m_flags))
1420 			bodytype = "8BITMIME";
1421 		if (bodytype != NULL &&
1422 		    SPACELEFT(optbuf, bufp) > strlen(bodytype) + 7)
1423 		{
1424 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1425 				 " BODY=%s", bodytype);
1426 			bufp += strlen(bufp);
1427 		}
1428 	}
1429 	else if (bitnset(M_8BITS, m->m_flags) ||
1430 		 !bitset(EF_HAS8BIT, e->e_flags) ||
1431 		 bitset(MCIF_8BITOK, mci->mci_flags))
1432 	{
1433 		/* EMPTY */
1434 		/* just pass it through */
1435 	}
1436 # if MIME8TO7
1437 	else if (bitset(MM_CVTMIME, MimeMode) &&
1438 		 !bitset(EF_DONT_MIME, e->e_flags) &&
1439 		 (!bitset(MM_PASS8BIT, MimeMode) ||
1440 		  bitset(EF_IS_MIME, e->e_flags)))
1441 	{
1442 		/* must convert from 8bit MIME format to 7bit encoded */
1443 		mci->mci_flags |= MCIF_CVT8TO7;
1444 	}
1445 # endif /* MIME8TO7 */
1446 	else if (!bitset(MM_PASS8BIT, MimeMode))
1447 	{
1448 		/* cannot just send a 8-bit version */
1449 		extern char MsgBuf[];
1450 
1451 		usrerrenh("5.6.3", "%s does not support 8BITMIME", CurHostName);
1452 		mci_setstat(mci, EX_NOTSTICKY, "5.6.3", MsgBuf);
1453 		return EX_DATAERR;
1454 	}
1455 
1456 	if (bitset(MCIF_DSN, mci->mci_flags))
1457 	{
1458 		if (e->e_envid != NULL &&
1459 		    SPACELEFT(optbuf, bufp) > strlen(e->e_envid) + 7)
1460 		{
1461 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1462 				 " ENVID=%s", e->e_envid);
1463 			bufp += strlen(bufp);
1464 		}
1465 
1466 		/* RET= parameter */
1467 		if (bitset(EF_RET_PARAM, e->e_flags) &&
1468 		    SPACELEFT(optbuf, bufp) > 9)
1469 		{
1470 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1471 				 " RET=%s",
1472 				 bitset(EF_NO_BODY_RETN, e->e_flags) ?
1473 					"HDRS" : "FULL");
1474 			bufp += strlen(bufp);
1475 		}
1476 	}
1477 
1478 	if (bitset(MCIF_AUTH, mci->mci_flags) && e->e_auth_param != NULL &&
1479 	    SPACELEFT(optbuf, bufp) > strlen(e->e_auth_param) + 7
1480 # if SASL
1481 	     && (!bitset(SASL_AUTH_AUTH, SASLOpts) || mci->mci_sasl_auth)
1482 # endif /* SASL */
1483 	    )
1484 	{
1485 		snprintf(bufp, SPACELEFT(optbuf, bufp),
1486 			 " AUTH=%s", e->e_auth_param);
1487 		bufp += strlen(bufp);
1488 	}
1489 
1490 	/*
1491 	**  Send the MAIL command.
1492 	**	Designates the sender.
1493 	*/
1494 
1495 	mci->mci_state = MCIS_ACTIVE;
1496 
1497 	if (bitset(EF_RESPONSE, e->e_flags) &&
1498 	    !bitnset(M_NO_NULL_FROM, m->m_flags))
1499 		buf[0] = '\0';
1500 	else
1501 		expand("\201g", buf, sizeof buf, e);
1502 	if (buf[0] == '<')
1503 	{
1504 		/* strip off <angle brackets> (put back on below) */
1505 		bufp = &buf[strlen(buf) - 1];
1506 		if (*bufp == '>')
1507 			*bufp = '\0';
1508 		bufp = &buf[1];
1509 	}
1510 	else
1511 		bufp = buf;
1512 	if (bitnset(M_LOCALMAILER, e->e_from.q_mailer->m_flags) ||
1513 	    !bitnset(M_FROMPATH, m->m_flags))
1514 	{
1515 		smtpmessage("MAIL From:<%s>%s", m, mci, bufp, optbuf);
1516 	}
1517 	else
1518 	{
1519 		smtpmessage("MAIL From:<@%s%c%s>%s", m, mci, MyHostName,
1520 			    *bufp == '@' ? ',' : ':', bufp, optbuf);
1521 	}
1522 	SmtpPhase = mci->mci_phase = "client MAIL";
1523 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1524 			CurHostName, mci->mci_phase);
1525 	r = reply(m, mci, e, TimeOuts.to_mail, NULL, &enhsc);
1526 	if (r < 0)
1527 	{
1528 		/* communications failure */
1529 		mci->mci_errno = errno;
1530 		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
1531 		smtpquit(m, mci, e);
1532 		return EX_TEMPFAIL;
1533 	}
1534 	else if (r == SMTPCLOSING)
1535 	{
1536 		/* service shutting down */
1537 		mci_setstat(mci, EX_TEMPFAIL, ENHSCN(enhsc, "4.5.0"),
1538 			    SmtpReplyBuffer);
1539 		smtpquit(m, mci, e);
1540 		return EX_TEMPFAIL;
1541 	}
1542 	else if (REPLYTYPE(r) == 4)
1543 	{
1544 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, smtptodsn(r)),
1545 			    SmtpReplyBuffer);
1546 		return EX_TEMPFAIL;
1547 	}
1548 	else if (REPLYTYPE(r) == 2)
1549 	{
1550 		return EX_OK;
1551 	}
1552 	else if (r == 501)
1553 	{
1554 		/* syntax error in arguments */
1555 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.5.2"),
1556 			    SmtpReplyBuffer);
1557 		return EX_DATAERR;
1558 	}
1559 	else if (r == 553)
1560 	{
1561 		/* mailbox name not allowed */
1562 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.1.3"),
1563 			    SmtpReplyBuffer);
1564 		return EX_DATAERR;
1565 	}
1566 	else if (r == 552)
1567 	{
1568 		/* exceeded storage allocation */
1569 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.3.4"),
1570 			    SmtpReplyBuffer);
1571 		if (bitset(MCIF_SIZE, mci->mci_flags))
1572 			e->e_flags |= EF_NO_BODY_RETN;
1573 		return EX_UNAVAILABLE;
1574 	}
1575 	else if (REPLYTYPE(r) == 5)
1576 	{
1577 		/* unknown error */
1578 		mci_setstat(mci, EX_NOTSTICKY, ENHSCN(enhsc, "5.0.0"),
1579 			    SmtpReplyBuffer);
1580 		return EX_UNAVAILABLE;
1581 	}
1582 
1583 	if (LogLevel > 1)
1584 	{
1585 		sm_syslog(LOG_CRIT, e->e_id,
1586 			  "%.100s: SMTP MAIL protocol error: %s",
1587 			  CurHostName,
1588 			  shortenstring(SmtpReplyBuffer, 403));
1589 	}
1590 
1591 	/* protocol error -- close up */
1592 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1593 		    SmtpReplyBuffer);
1594 	smtpquit(m, mci, e);
1595 	return EX_PROTOCOL;
1596 }
1597 /*
1598 **  SMTPRCPT -- designate recipient.
1599 **
1600 **	Parameters:
1601 **		to -- address of recipient.
1602 **		m -- the mailer we are sending to.
1603 **		mci -- the connection info for this transaction.
1604 **		e -- the envelope for this transaction.
1605 **
1606 **	Returns:
1607 **		exit status corresponding to recipient status.
1608 **
1609 **	Side Effects:
1610 **		Sends the mail via SMTP.
1611 */
1612 
1613 int
1614 smtprcpt(to, m, mci, e)
1615 	ADDRESS *to;
1616 	register MAILER *m;
1617 	MCI *mci;
1618 	ENVELOPE *e;
1619 {
1620 	register int r;
1621 	char *bufp;
1622 	char optbuf[MAXLINE];
1623 	char *enhsc;
1624 
1625 	enhsc = NULL;
1626 	optbuf[0] = '\0';
1627 	bufp = optbuf;
1628 
1629 	/*
1630 	**  warning: in the following it is assumed that the free space
1631 	**  in bufp is sizeof optbuf
1632 	*/
1633 	if (bitset(MCIF_DSN, mci->mci_flags))
1634 	{
1635 		/* NOTIFY= parameter */
1636 		if (bitset(QHASNOTIFY, to->q_flags) &&
1637 		    bitset(QPRIMARY, to->q_flags) &&
1638 		    !bitnset(M_LOCALMAILER, m->m_flags))
1639 		{
1640 			bool firstone = TRUE;
1641 
1642 			(void) strlcat(bufp, " NOTIFY=", sizeof optbuf);
1643 			if (bitset(QPINGONSUCCESS, to->q_flags))
1644 			{
1645 				(void) strlcat(bufp, "SUCCESS", sizeof optbuf);
1646 				firstone = FALSE;
1647 			}
1648 			if (bitset(QPINGONFAILURE, to->q_flags))
1649 			{
1650 				if (!firstone)
1651 					(void) strlcat(bufp, ",",
1652 						       sizeof optbuf);
1653 				(void) strlcat(bufp, "FAILURE", sizeof optbuf);
1654 				firstone = FALSE;
1655 			}
1656 			if (bitset(QPINGONDELAY, to->q_flags))
1657 			{
1658 				if (!firstone)
1659 					(void) strlcat(bufp, ",",
1660 						       sizeof optbuf);
1661 				(void) strlcat(bufp, "DELAY", sizeof optbuf);
1662 				firstone = FALSE;
1663 			}
1664 			if (firstone)
1665 				(void) strlcat(bufp, "NEVER", sizeof optbuf);
1666 			bufp += strlen(bufp);
1667 		}
1668 
1669 		/* ORCPT= parameter */
1670 		if (to->q_orcpt != NULL &&
1671 		    SPACELEFT(optbuf, bufp) > strlen(to->q_orcpt) + 7)
1672 		{
1673 			snprintf(bufp, SPACELEFT(optbuf, bufp),
1674 				 " ORCPT=%s", to->q_orcpt);
1675 			bufp += strlen(bufp);
1676 		}
1677 	}
1678 
1679 	smtpmessage("RCPT To:<%s>%s", m, mci, to->q_user, optbuf);
1680 
1681 	SmtpPhase = mci->mci_phase = "client RCPT";
1682 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1683 			CurHostName, mci->mci_phase);
1684 	r = reply(m, mci, e, TimeOuts.to_rcpt, NULL, &enhsc);
1685 	to->q_rstatus = newstr(SmtpReplyBuffer);
1686 	to->q_status = ENHSCN(enhsc, smtptodsn(r));
1687 	if (!bitnset(M_LMTP, m->m_flags))
1688 		to->q_statmta = mci->mci_host;
1689 	if (r < 0 || REPLYTYPE(r) == 4)
1690 		return EX_TEMPFAIL;
1691 	else if (REPLYTYPE(r) == 2)
1692 		return EX_OK;
1693 	else if (r == 550)
1694 	{
1695 		to->q_status = ENHSCN(enhsc, "5.1.1");
1696 		return EX_NOUSER;
1697 	}
1698 	else if (r == 551)
1699 	{
1700 		to->q_status = ENHSCN(enhsc, "5.1.6");
1701 		return EX_NOUSER;
1702 	}
1703 	else if (r == 553)
1704 	{
1705 		to->q_status = ENHSCN(enhsc, "5.1.3");
1706 		return EX_NOUSER;
1707 	}
1708 	else if (REPLYTYPE(r) == 5)
1709 	{
1710 		return EX_UNAVAILABLE;
1711 	}
1712 
1713 	if (LogLevel > 1)
1714 	{
1715 		sm_syslog(LOG_CRIT, e->e_id,
1716 			  "%.100s: SMTP RCPT protocol error: %s",
1717 			  CurHostName,
1718 			  shortenstring(SmtpReplyBuffer, 403));
1719 	}
1720 
1721 	mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1722 		    SmtpReplyBuffer);
1723 	return EX_PROTOCOL;
1724 }
1725 /*
1726 **  SMTPDATA -- send the data and clean up the transaction.
1727 **
1728 **	Parameters:
1729 **		m -- mailer being sent to.
1730 **		mci -- the mailer connection information.
1731 **		e -- the envelope for this message.
1732 **
1733 **	Returns:
1734 **		exit status corresponding to DATA command.
1735 **
1736 **	Side Effects:
1737 **		none.
1738 */
1739 
1740 static jmp_buf	CtxDataTimeout;
1741 static EVENT	*volatile DataTimeout = NULL;
1742 
1743 int
1744 smtpdata(m, mci, e)
1745 	MAILER *m;
1746 	register MCI *mci;
1747 	register ENVELOPE *e;
1748 {
1749 	register int r;
1750 	int rstat;
1751 	int xstat;
1752 	time_t timeout;
1753 	char *enhsc;
1754 
1755 	enhsc = NULL;
1756 
1757 	/*
1758 	**  Send the data.
1759 	**	First send the command and check that it is ok.
1760 	**	Then send the data.
1761 	**	Follow it up with a dot to terminate.
1762 	**	Finally get the results of the transaction.
1763 	*/
1764 
1765 	/* send the command and check ok to proceed */
1766 	smtpmessage("DATA", m, mci);
1767 	SmtpPhase = mci->mci_phase = "client DATA 354";
1768 	sm_setproctitle(TRUE, e, "%s %s: %s",
1769 			qid_printname(e), CurHostName, mci->mci_phase);
1770 	r = reply(m, mci, e, TimeOuts.to_datainit, NULL, &enhsc);
1771 	if (r < 0 || REPLYTYPE(r) == 4)
1772 	{
1773 		smtpquit(m, mci, e);
1774 		return EX_TEMPFAIL;
1775 	}
1776 	else if (REPLYTYPE(r) == 5)
1777 	{
1778 		smtprset(m, mci, e);
1779 		return EX_UNAVAILABLE;
1780 	}
1781 	else if (REPLYTYPE(r) != 3)
1782 	{
1783 		if (LogLevel > 1)
1784 		{
1785 			sm_syslog(LOG_CRIT, e->e_id,
1786 				  "%.100s: SMTP DATA-1 protocol error: %s",
1787 				  CurHostName,
1788 				  shortenstring(SmtpReplyBuffer, 403));
1789 		}
1790 		smtprset(m, mci, e);
1791 		mci_setstat(mci, EX_PROTOCOL, ENHSCN(enhsc, "5.5.1"),
1792 			    SmtpReplyBuffer);
1793 		return EX_PROTOCOL;
1794 	}
1795 
1796 	/*
1797 	**  Set timeout around data writes.  Make it at least large
1798 	**  enough for DNS timeouts on all recipients plus some fudge
1799 	**  factor.  The main thing is that it should not be infinite.
1800 	*/
1801 
1802 	if (setjmp(CtxDataTimeout) != 0)
1803 	{
1804 		mci->mci_errno = errno;
1805 		mci->mci_state = MCIS_ERROR;
1806 		mci_setstat(mci, EX_TEMPFAIL, "4.4.2", NULL);
1807 
1808 		/*
1809 		**  If putbody() couldn't finish due to a timeout,
1810 		**  rewind it here in the timeout handler.  See
1811 		**  comments at the end of putbody() for reasoning.
1812 		*/
1813 
1814 		if (e->e_dfp != NULL)
1815 			(void) bfrewind(e->e_dfp);
1816 
1817 		errno = mci->mci_errno;
1818 		syserr("451 4.4.1 timeout writing message to %s", CurHostName);
1819 		smtpquit(m, mci, e);
1820 		return EX_TEMPFAIL;
1821 	}
1822 
1823 	if (tTd(18, 101))
1824 	{
1825 		/* simulate a DATA timeout */
1826 		timeout = 1;
1827 	}
1828 	else
1829 		timeout = DATA_PROGRESS_TIMEOUT;
1830 
1831 	DataTimeout = setevent(timeout, datatimeout, 0);
1832 
1833 
1834 	/*
1835 	**  Output the actual message.
1836 	*/
1837 
1838 	(*e->e_puthdr)(mci, e->e_header, e, M87F_OUTER);
1839 
1840 	if (tTd(18, 101))
1841 	{
1842 		/* simulate a DATA timeout */
1843 		(void) sleep(2);
1844 	}
1845 
1846 	(*e->e_putbody)(mci, e, NULL);
1847 
1848 	/*
1849 	**  Cleanup after sending message.
1850 	*/
1851 
1852 	if (DataTimeout != NULL)
1853 		clrevent(DataTimeout);
1854 
1855 # if _FFR_CATCH_BROKEN_MTAS
1856 	{
1857 		fd_set readfds;
1858 		struct timeval timeout;
1859 
1860 		FD_ZERO(&readfds);
1861 		FD_SET(fileno(mci->mci_in), &readfds);
1862 		timeout.tv_sec = 0;
1863 		timeout.tv_usec = 0;
1864 		if (select(fileno(mci->mci_in) + 1, FDSET_CAST &readfds,
1865 			   NULL, NULL, &timeout) > 0 &&
1866 		    FD_ISSET(fileno(mci->mci_in), &readfds))
1867 		{
1868 			/* terminate the message */
1869 			fprintf(mci->mci_out, ".%s", m->m_eol);
1870 			if (TrafficLogFile != NULL)
1871 				fprintf(TrafficLogFile, "%05d >>> .\n",
1872 					(int) getpid());
1873 			if (Verbose)
1874 				nmessage(">>> .");
1875 
1876 			sm_syslog(LOG_CRIT, e->e_id,
1877 				  "%.100s: SMTP DATA-1 protocol error: remote server returned response before final dot",
1878 				  CurHostName);
1879 			mci->mci_errno = EIO;
1880 			mci->mci_state = MCIS_ERROR;
1881 			mci_setstat(mci, EX_PROTOCOL, "5.5.0", NULL);
1882 			smtpquit(m, mci, e);
1883 			return EX_PROTOCOL;
1884 		}
1885 	}
1886 # endif /* _FFR_CATCH_BROKEN_MTAS */
1887 
1888 	if (ferror(mci->mci_out))
1889 	{
1890 		/* error during processing -- don't send the dot */
1891 		mci->mci_errno = EIO;
1892 		mci->mci_state = MCIS_ERROR;
1893 		mci_setstat(mci, EX_IOERR, "4.4.2", NULL);
1894 		smtpquit(m, mci, e);
1895 		return EX_IOERR;
1896 	}
1897 
1898 	/* terminate the message */
1899 	fprintf(mci->mci_out, ".%s", m->m_eol);
1900 	if (TrafficLogFile != NULL)
1901 		fprintf(TrafficLogFile, "%05d >>> .\n", (int) getpid());
1902 	if (Verbose)
1903 		nmessage(">>> .");
1904 
1905 	/* check for the results of the transaction */
1906 	SmtpPhase = mci->mci_phase = "client DATA status";
1907 	sm_setproctitle(TRUE, e, "%s %s: %s", qid_printname(e),
1908 			CurHostName, mci->mci_phase);
1909 	if (bitnset(M_LMTP, m->m_flags))
1910 		return EX_OK;
1911 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
1912 	if (r < 0)
1913 	{
1914 		smtpquit(m, mci, e);
1915 		return EX_TEMPFAIL;
1916 	}
1917 	mci->mci_state = MCIS_OPEN;
1918 	xstat = EX_NOTSTICKY;
1919 	if (r == 452)
1920 		rstat = EX_TEMPFAIL;
1921 	else if (REPLYTYPE(r) == 4)
1922 		rstat = xstat = EX_TEMPFAIL;
1923 	else if (REPLYCLASS(r) != 5)
1924 		rstat = xstat = EX_PROTOCOL;
1925 	else if (REPLYTYPE(r) == 2)
1926 		rstat = xstat = EX_OK;
1927 	else if (REPLYTYPE(r) == 5)
1928 		rstat = EX_UNAVAILABLE;
1929 	else
1930 		rstat = EX_PROTOCOL;
1931 	mci_setstat(mci, xstat, ENHSCN(enhsc, smtptodsn(r)),
1932 		    SmtpReplyBuffer);
1933 	if (e->e_statmsg != NULL)
1934 		sm_free(e->e_statmsg);
1935 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
1936 	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
1937 		r += 5;
1938 	else
1939 		r = 4;
1940 	e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
1941 	SmtpPhase = mci->mci_phase = "idle";
1942 	sm_setproctitle(TRUE, e, "%s: %s", CurHostName, mci->mci_phase);
1943 	if (rstat != EX_PROTOCOL)
1944 		return rstat;
1945 	if (LogLevel > 1)
1946 	{
1947 		sm_syslog(LOG_CRIT, e->e_id,
1948 			  "%.100s: SMTP DATA-2 protocol error: %s",
1949 			  CurHostName,
1950 			  shortenstring(SmtpReplyBuffer, 403));
1951 	}
1952 	return rstat;
1953 }
1954 
1955 
1956 static void
1957 datatimeout()
1958 {
1959 	int save_errno = errno;
1960 
1961 	/*
1962 	**  NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER.  DO NOT ADD
1963 	**	ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE
1964 	**	DOING.
1965 	*/
1966 
1967 	if (DataProgress)
1968 	{
1969 		time_t timeout;
1970 
1971 		/* check back again later */
1972 		if (tTd(18, 101))
1973 		{
1974 			/* simulate a DATA timeout */
1975 			timeout = 1;
1976 		}
1977 		else
1978 			timeout = DATA_PROGRESS_TIMEOUT;
1979 
1980 		/* reset the timeout */
1981 		DataTimeout = sigsafe_setevent(timeout, datatimeout, 0);
1982 		DataProgress = FALSE;
1983 	}
1984 	else
1985 	{
1986 		/* event is done */
1987 		DataTimeout = NULL;
1988 	}
1989 
1990 	/* if no progress was made or problem resetting event, die now */
1991 	if (DataTimeout == NULL)
1992 	{
1993 		errno = ETIMEDOUT;
1994 		longjmp(CtxDataTimeout, 1);
1995 	}
1996 
1997 	errno = save_errno;
1998 }
1999 /*
2000 **  SMTPGETSTAT -- get status code from DATA in LMTP
2001 **
2002 **	Parameters:
2003 **		m -- the mailer to which we are sending the message.
2004 **		mci -- the mailer connection structure.
2005 **		e -- the current envelope.
2006 **
2007 **	Returns:
2008 **		The exit status corresponding to the reply code.
2009 */
2010 
2011 int
2012 smtpgetstat(m, mci, e)
2013 	MAILER *m;
2014 	MCI *mci;
2015 	ENVELOPE *e;
2016 {
2017 	int r;
2018 	int status;
2019 	char *enhsc;
2020 
2021 	enhsc = NULL;
2022 	/* check for the results of the transaction */
2023 	r = reply(m, mci, e, TimeOuts.to_datafinal, NULL, &enhsc);
2024 	if (r < 0)
2025 	{
2026 		smtpquit(m, mci, e);
2027 		return EX_TEMPFAIL;
2028 	}
2029 	if (REPLYTYPE(r) == 4)
2030 		status = EX_TEMPFAIL;
2031 	else if (REPLYCLASS(r) != 5)
2032 		status = EX_PROTOCOL;
2033 	else if (REPLYTYPE(r) == 2)
2034 		status = EX_OK;
2035 	else if (REPLYTYPE(r) == 5)
2036 		status = EX_UNAVAILABLE;
2037 	else
2038 		status = EX_PROTOCOL;
2039 	if (e->e_statmsg != NULL)
2040 		sm_free(e->e_statmsg);
2041 	if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2042 	    (r = isenhsc(SmtpReplyBuffer + 4, ' ')) > 0)
2043 		r += 5;
2044 	else
2045 		r = 4;
2046 	e->e_statmsg = newstr(&SmtpReplyBuffer[r]);
2047 	mci_setstat(mci, status, ENHSCN(enhsc, smtptodsn(r)),
2048 		    SmtpReplyBuffer);
2049 	if (LogLevel > 1 && status == EX_PROTOCOL)
2050 	{
2051 		sm_syslog(LOG_CRIT, e->e_id,
2052 			  "%.100s: SMTP DATA-3 protocol error: %s",
2053 			  CurHostName,
2054 			  shortenstring(SmtpReplyBuffer, 403));
2055 	}
2056 	return status;
2057 }
2058 /*
2059 **  SMTPQUIT -- close the SMTP connection.
2060 **
2061 **	Parameters:
2062 **		m -- a pointer to the mailer.
2063 **		mci -- the mailer connection information.
2064 **		e -- the current envelope.
2065 **
2066 **	Returns:
2067 **		none.
2068 **
2069 **	Side Effects:
2070 **		sends the final protocol and closes the connection.
2071 */
2072 
2073 void
2074 smtpquit(m, mci, e)
2075 	register MAILER *m;
2076 	register MCI *mci;
2077 	ENVELOPE *e;
2078 {
2079 	bool oldSuprErrs = SuprErrs;
2080 	int rcode;
2081 
2082 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2083 	if (CurHostName == NULL)
2084 		CurHostName = MyHostName;
2085 
2086 	/*
2087 	**	Suppress errors here -- we may be processing a different
2088 	**	job when we do the quit connection, and we don't want the
2089 	**	new job to be penalized for something that isn't it's
2090 	**	problem.
2091 	*/
2092 
2093 	SuprErrs = TRUE;
2094 
2095 	/* send the quit message if we haven't gotten I/O error */
2096 	if (mci->mci_state != MCIS_ERROR &&
2097 	    mci->mci_state != MCIS_QUITING)
2098 	{
2099 		int origstate = mci->mci_state;
2100 
2101 		SmtpPhase = "client QUIT";
2102 		mci->mci_state = MCIS_QUITING;
2103 		smtpmessage("QUIT", m, mci);
2104 		(void) reply(m, mci, e, TimeOuts.to_quit, NULL, NULL);
2105 		SuprErrs = oldSuprErrs;
2106 		if (mci->mci_state == MCIS_CLOSED ||
2107 		    origstate == MCIS_CLOSED)
2108 			return;
2109 	}
2110 
2111 	/* now actually close the connection and pick up the zombie */
2112 	rcode = endmailer(mci, e, NULL);
2113 	if (rcode != EX_OK)
2114 	{
2115 		char *mailer = NULL;
2116 
2117 		if (mci->mci_mailer != NULL &&
2118 		    mci->mci_mailer->m_name != NULL)
2119 			mailer = mci->mci_mailer->m_name;
2120 
2121 		/* look for naughty mailers */
2122 		sm_syslog(LOG_ERR, e->e_id,
2123 			  "smtpquit: mailer%s%s exited with exit value %d",
2124 			  mailer == NULL ? "" : " ",
2125 			  mailer == NULL ? "" : mailer,
2126 			  rcode);
2127 	}
2128 
2129 	SuprErrs = oldSuprErrs;
2130 }
2131 /*
2132 **  SMTPRSET -- send a RSET (reset) command
2133 **
2134 **	Parameters:
2135 **		m -- a pointer to the mailer.
2136 **		mci -- the mailer connection information.
2137 **		e -- the current envelope.
2138 **
2139 **	Returns:
2140 **		none.
2141 **
2142 **	Side Effects:
2143 **		closes the connection if there is no reply to RSET.
2144 */
2145 
2146 void
2147 smtprset(m, mci, e)
2148 	register MAILER *m;
2149 	register MCI *mci;
2150 	ENVELOPE *e;
2151 {
2152 	int r;
2153 
2154 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2155 	if (CurHostName == NULL)
2156 		CurHostName = MyHostName;
2157 
2158 	SmtpPhase = "client RSET";
2159 	smtpmessage("RSET", m, mci);
2160 	r = reply(m, mci, e, TimeOuts.to_rset, NULL, NULL);
2161 	if (r < 0)
2162 		mci->mci_state = MCIS_ERROR;
2163 	else
2164 	{
2165 		/*
2166 		**  Any response is deemed to be acceptable.
2167 		**  The standard does not state the proper action
2168 		**  to take when a value other than 250 is received.
2169 		**
2170 		**  However, if 421 is returned for the RSET, leave
2171 		**  mci_state as MCIS_SSD (set in reply()).
2172 		*/
2173 
2174 		if (mci->mci_state != MCIS_SSD)
2175 			mci->mci_state = MCIS_OPEN;
2176 		return;
2177 	}
2178 	smtpquit(m, mci, e);
2179 }
2180 /*
2181 **  SMTPPROBE -- check the connection state
2182 **
2183 **	Parameters:
2184 **		mci -- the mailer connection information.
2185 **
2186 **	Returns:
2187 **		none.
2188 **
2189 **	Side Effects:
2190 **		closes the connection if there is no reply to RSET.
2191 */
2192 
2193 int
2194 smtpprobe(mci)
2195 	register MCI *mci;
2196 {
2197 	int r;
2198 	MAILER *m = mci->mci_mailer;
2199 	ENVELOPE *e;
2200 	extern ENVELOPE BlankEnvelope;
2201 
2202 	CurHostName = mci->mci_host;		/* XXX UGLY XXX */
2203 	if (CurHostName == NULL)
2204 		CurHostName = MyHostName;
2205 
2206 	e = &BlankEnvelope;
2207 	SmtpPhase = "client probe";
2208 	smtpmessage("RSET", m, mci);
2209 	r = reply(m, mci, e, TimeOuts.to_miscshort, NULL, NULL);
2210 	if (r < 0 || REPLYTYPE(r) != 2)
2211 		smtpquit(m, mci, e);
2212 	return r;
2213 }
2214 /*
2215 **  REPLY -- read arpanet reply
2216 **
2217 **	Parameters:
2218 **		m -- the mailer we are reading the reply from.
2219 **		mci -- the mailer connection info structure.
2220 **		e -- the current envelope.
2221 **		timeout -- the timeout for reads.
2222 **		pfunc -- processing function called on each line of response.
2223 **			If null, no special processing is done.
2224 **
2225 **	Returns:
2226 **		reply code it reads.
2227 **
2228 **	Side Effects:
2229 **		flushes the mail file.
2230 */
2231 
2232 int
2233 reply(m, mci, e, timeout, pfunc, enhstat)
2234 	MAILER *m;
2235 	MCI *mci;
2236 	ENVELOPE *e;
2237 	time_t timeout;
2238 	void (*pfunc)();
2239 	char **enhstat;
2240 {
2241 	register char *bufp;
2242 	register int r;
2243 	bool firstline = TRUE;
2244 	char junkbuf[MAXLINE];
2245 	static char enhstatcode[ENHSCLEN];
2246 	int save_errno;
2247 
2248 	if (mci->mci_out != NULL)
2249 		(void) fflush(mci->mci_out);
2250 
2251 	if (tTd(18, 1))
2252 		dprintf("reply\n");
2253 
2254 	/*
2255 	**  Read the input line, being careful not to hang.
2256 	*/
2257 
2258 	bufp = SmtpReplyBuffer;
2259 	for (;;)
2260 	{
2261 		register char *p;
2262 
2263 		/* actually do the read */
2264 		if (e->e_xfp != NULL)
2265 			(void) fflush(e->e_xfp);	/* for debugging */
2266 
2267 		/* if we are in the process of closing just give the code */
2268 		if (mci->mci_state == MCIS_CLOSED)
2269 			return SMTPCLOSING;
2270 
2271 		if (mci->mci_out != NULL)
2272 			(void) fflush(mci->mci_out);
2273 
2274 		/* get the line from the other side */
2275 		p = sfgets(bufp, MAXLINE, mci->mci_in, timeout, SmtpPhase);
2276 		mci->mci_lastuse = curtime();
2277 
2278 		if (p == NULL)
2279 		{
2280 			bool oldholderrs;
2281 			extern char MsgBuf[];
2282 
2283 			/* if the remote end closed early, fake an error */
2284 			if (errno == 0)
2285 # ifdef ECONNRESET
2286 				errno = ECONNRESET;
2287 # else /* ECONNRESET */
2288 				errno = EPIPE;
2289 # endif /* ECONNRESET */
2290 
2291 			mci->mci_errno = errno;
2292 			oldholderrs = HoldErrs;
2293 			HoldErrs = TRUE;
2294 			usrerr("451 4.4.1 reply: read error from %s",
2295 			       CurHostName == NULL ? "NO_HOST" : CurHostName);
2296 
2297 			/* errors on QUIT should not be persistent */
2298 			if (strncmp(SmtpMsgBuffer, "QUIT", 4) != 0)
2299 				mci_setstat(mci, EX_TEMPFAIL, "4.4.2", MsgBuf);
2300 
2301 			/* if debugging, pause so we can see state */
2302 			if (tTd(18, 100))
2303 				(void) pause();
2304 			mci->mci_state = MCIS_ERROR;
2305 			save_errno = errno;
2306 			smtpquit(m, mci, e);
2307 # if XDEBUG
2308 			{
2309 				char wbuf[MAXLINE];
2310 				int wbufleft = sizeof wbuf;
2311 
2312 				p = wbuf;
2313 				if (e->e_to != NULL)
2314 				{
2315 					int plen;
2316 
2317 					snprintf(p, wbufleft, "%s... ",
2318 						shortenstring(e->e_to, MAXSHORTSTR));
2319 					plen = strlen(p);
2320 					p += plen;
2321 					wbufleft -= plen;
2322 				}
2323 				snprintf(p, wbufleft, "reply(%.100s) during %s",
2324 					 CurHostName == NULL ? "NO_HOST" : CurHostName,
2325 					 SmtpPhase);
2326 				checkfd012(wbuf);
2327 			}
2328 # endif /* XDEBUG */
2329 			errno = save_errno;
2330 			HoldErrs = oldholderrs;
2331 			return -1;
2332 		}
2333 		fixcrlf(bufp, TRUE);
2334 
2335 		/* EHLO failure is not a real error */
2336 		if (e->e_xfp != NULL && (bufp[0] == '4' ||
2337 		    (bufp[0] == '5' && strncmp(SmtpMsgBuffer, "EHLO", 4) != 0)))
2338 		{
2339 			/* serious error -- log the previous command */
2340 			if (SmtpNeedIntro)
2341 			{
2342 				/* inform user who we are chatting with */
2343 				fprintf(CurEnv->e_xfp,
2344 					"... while talking to %s:\n",
2345 					CurHostName == NULL ? "NO_HOST" : CurHostName);
2346 				SmtpNeedIntro = FALSE;
2347 			}
2348 			if (SmtpMsgBuffer[0] != '\0')
2349 				fprintf(e->e_xfp, ">>> %s\n", SmtpMsgBuffer);
2350 			SmtpMsgBuffer[0] = '\0';
2351 
2352 			/* now log the message as from the other side */
2353 			fprintf(e->e_xfp, "<<< %s\n", bufp);
2354 		}
2355 
2356 		/* display the input for verbose mode */
2357 		if (Verbose)
2358 			nmessage("050 %s", bufp);
2359 
2360 		/* ignore improperly formatted input */
2361 		if (!ISSMTPREPLY(bufp))
2362 			continue;
2363 
2364 		if (bitset(MCIF_ENHSTAT, mci->mci_flags) &&
2365 		    enhstat != NULL &&
2366 		    extenhsc(bufp + 4, ' ', enhstatcode) > 0)
2367 			*enhstat = enhstatcode;
2368 
2369 		/* process the line */
2370 		if (pfunc != NULL)
2371 			(*pfunc)(bufp, firstline, m, mci, e);
2372 
2373 		firstline = FALSE;
2374 
2375 		/* decode the reply code */
2376 		r = atoi(bufp);
2377 
2378 		/* extra semantics: 0xx codes are "informational" */
2379 		if (r < 100)
2380 			continue;
2381 
2382 		/* if no continuation lines, return this line */
2383 		if (bufp[3] != '-')
2384 			break;
2385 
2386 		/* first line of real reply -- ignore rest */
2387 		bufp = junkbuf;
2388 	}
2389 
2390 	/*
2391 	**  Now look at SmtpReplyBuffer -- only care about the first
2392 	**  line of the response from here on out.
2393 	*/
2394 
2395 	/* save temporary failure messages for posterity */
2396 	if (SmtpReplyBuffer[0] == '4' &&
2397 	    (bitnset(M_LMTP, m->m_flags) || SmtpError[0] == '\0'))
2398 		snprintf(SmtpError, sizeof SmtpError, "%s", SmtpReplyBuffer);
2399 
2400 	/* reply code 421 is "Service Shutting Down" */
2401 	if (r == SMTPCLOSING && mci->mci_state != MCIS_SSD)
2402 	{
2403 		/* send the quit protocol */
2404 		mci->mci_state = MCIS_SSD;
2405 		smtpquit(m, mci, e);
2406 	}
2407 
2408 	return r;
2409 }
2410 /*
2411 **  SMTPMESSAGE -- send message to server
2412 **
2413 **	Parameters:
2414 **		f -- format
2415 **		m -- the mailer to control formatting.
2416 **		a, b, c -- parameters
2417 **
2418 **	Returns:
2419 **		none.
2420 **
2421 **	Side Effects:
2422 **		writes message to mci->mci_out.
2423 */
2424 
2425 /*VARARGS1*/
2426 void
2427 # ifdef __STDC__
2428 smtpmessage(char *f, MAILER *m, MCI *mci, ...)
2429 # else /* __STDC__ */
2430 smtpmessage(f, m, mci, va_alist)
2431 	char *f;
2432 	MAILER *m;
2433 	MCI *mci;
2434 	va_dcl
2435 # endif /* __STDC__ */
2436 {
2437 	VA_LOCAL_DECL
2438 
2439 	VA_START(mci);
2440 	(void) vsnprintf(SmtpMsgBuffer, sizeof SmtpMsgBuffer, f, ap);
2441 	VA_END;
2442 
2443 	if (tTd(18, 1) || Verbose)
2444 		nmessage(">>> %s", SmtpMsgBuffer);
2445 	if (TrafficLogFile != NULL)
2446 		fprintf(TrafficLogFile, "%05d >>> %s\n",
2447 			(int) getpid(), SmtpMsgBuffer);
2448 	if (mci->mci_out != NULL)
2449 	{
2450 		fprintf(mci->mci_out, "%s%s", SmtpMsgBuffer,
2451 			m == NULL ? "\r\n" : m->m_eol);
2452 	}
2453 	else if (tTd(18, 1))
2454 	{
2455 		dprintf("smtpmessage: NULL mci_out\n");
2456 	}
2457 }
2458 
2459 #endif /* SMTP */
2460