Home | History | Annotate | Download | only in src
      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
     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
    287 esmtp_check(line, firstline, m, mci, e)
    288 	char *line;
    289 	bool firstline;
    290 	MAILER *m;
    291 	register MCI *mci;
    292 	ENVELOPE *e;
    293 {
    294 	if (strstr(line, "ESMTP") != NULL)
    295 		mci->mci_flags |= MCIF_ESMTP;
    296 
    297 	/*
    298 	**  Dirty hack below. Quoting the author:
    299 	**  This was a response to people who wanted SMTP transmission to be
    300 	**  just-send-8 by default.  Essentially, you could put this tag into
    301 	**  your greeting message to behave as though the F=8 flag was set on
    302 	**  the mailer.
    303 	*/
    304 
    305 	if (strstr(line, "8BIT-OK") != NULL)
    306 		mci->mci_flags |= MCIF_8BITOK;
    307 }
    308 
    309 #if SASL
    310 /* specify prototype so compiler can check calls */
    311 static char *str_union __P((char *, char *, SM_RPOOL_T *));
    312 
    313 /*
    314 **  STR_UNION -- create the union of two lists
    315 **
    316 **	Parameters:
    317 **		s1, s2 -- lists of items (separated by single blanks).
    318 **		rpool -- resource pool from which result is allocated.
    319 **
    320 **	Returns:
    321 **		the union of both lists.
    322 */
    323 
    324 static char *
    325 str_union(s1, s2, rpool)
    326 	char *s1, *s2;
    327 	SM_RPOOL_T *rpool;
    328 {
    329 	char *hr, *h1, *h, *res;
    330 	int l1, l2, rl;
    331 
    332 	if (s1 == NULL || *s1 == '\0')
    333 		return s2;
    334 	if (s2 == NULL || *s2 == '\0')
    335 		return s1;
    336 	l1 = strlen(s1);
    337 	l2 = strlen(s2);
    338 	rl = l1 + l2;
    339 	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
    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
    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
    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
    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
    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
    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
   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
   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
   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
   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
   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
   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 *
   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 *
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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
   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__
   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