srvrsmtp.c revision 66494
138032Speter/*
264562Sgshapiro * Copyright (c) 1998-2000 Sendmail, Inc. and its suppliers.
364562Sgshapiro *	All rights reserved.
438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
538032Speter * Copyright (c) 1988, 1993
638032Speter *	The Regents of the University of California.  All rights reserved.
738032Speter *
838032Speter * By using this file, you agree to the terms and conditions set
938032Speter * forth in the LICENSE file which can be found at the top level of
1038032Speter * the sendmail distribution.
1138032Speter *
1238032Speter */
1338032Speter
1438032Speter
1564562Sgshapiro#include <sendmail.h>
1664562Sgshapiro
1738032Speter#ifndef lint
1864562Sgshapiro# if SMTP
1966494Sgshapirostatic char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.58 2000/09/21 21:52:18 ca Exp $ (with SMTP)";
2064562Sgshapiro# else /* SMTP */
2166494Sgshapirostatic char id[] = "@(#)$Id: srvrsmtp.c,v 8.471.2.2.2.58 2000/09/21 21:52:18 ca Exp $ (without SMTP)";
2264562Sgshapiro# endif /* SMTP */
2364562Sgshapiro#endif /* ! lint */
2464562Sgshapiro
2538032Speter#if SMTP
2666494Sgshapiro# if SASL || STARTTLS
2766494Sgshapiro#  include "sfsasl.h"
2866494Sgshapiro# endif /* SASL || STARTTLS */
2964562Sgshapiro# if SASL
3064562Sgshapiro#  define ENC64LEN(l)	(((l) + 2) * 4 / 3 + 1)
3164562Sgshapirostatic int saslmechs __P((sasl_conn_t *, char **));
3264562Sgshapiro# endif /* SASL */
3364562Sgshapiro# if STARTTLS
3464562Sgshapiro#  include <sysexits.h>
3564562Sgshapiro#   include <openssl/err.h>
3664562Sgshapiro#   include <openssl/bio.h>
3764562Sgshapiro#   include <openssl/pem.h>
3864562Sgshapiro#   ifndef HASURANDOMDEV
3964562Sgshapiro#    include <openssl/rand.h>
4064562Sgshapiro#   endif /* !HASURANDOMDEV */
4138032Speter
4264562Sgshapirostatic SSL	*srv_ssl = NULL;
4364562Sgshapirostatic SSL_CTX	*srv_ctx = NULL;
4464562Sgshapiro#  if !TLS_NO_RSA
4564562Sgshapirostatic RSA	*rsa = NULL;
4664562Sgshapiro#  endif /* !TLS_NO_RSA */
4764562Sgshapirostatic bool	tls_ok = FALSE;
4864562Sgshapirostatic int	tls_verify_cb __P((X509_STORE_CTX *));
4964562Sgshapiro#  if !TLS_NO_RSA
5064562Sgshapiro#   define RSA_KEYLENGTH	512
5164562Sgshapiro#  endif /* !TLS_NO_RSA */
5264562Sgshapiro# endif /* STARTTLS */
5338032Speter
5464562Sgshapirostatic time_t	checksmtpattack __P((volatile int *, int, bool,
5564562Sgshapiro				     char *, ENVELOPE *));
5664562Sgshapirostatic void	mail_esmtp_args __P((char *, char *, ENVELOPE *));
5764562Sgshapirostatic void	printvrfyaddr __P((ADDRESS *, bool, bool));
5864562Sgshapirostatic void	rcpt_esmtp_args __P((ADDRESS *, char *, char *, ENVELOPE *));
5964562Sgshapirostatic int	runinchild __P((char *, ENVELOPE *));
6064562Sgshapirostatic char	*skipword __P((char *volatile, char *));
6164562Sgshapiroextern ENVELOPE	BlankEnvelope;
6238032Speter
6338032Speter/*
6438032Speter**  SMTP -- run the SMTP protocol.
6538032Speter**
6638032Speter**	Parameters:
6738032Speter**		nullserver -- if non-NULL, rejection message for
6838032Speter**			all SMTP commands.
6938032Speter**		e -- the envelope.
7038032Speter**
7138032Speter**	Returns:
7238032Speter**		never.
7338032Speter**
7438032Speter**	Side Effects:
7538032Speter**		Reads commands from the input channel and processes
7638032Speter**			them.
7738032Speter*/
7838032Speter
7938032Speterstruct cmd
8038032Speter{
8164562Sgshapiro	char	*cmd_name;	/* command name */
8264562Sgshapiro	int	cmd_code;	/* internal code, see below */
8338032Speter};
8438032Speter
8564562Sgshapiro/* values for cmd_code */
8638032Speter# define CMDERROR	0	/* bad command */
8738032Speter# define CMDMAIL	1	/* mail -- designate sender */
8838032Speter# define CMDRCPT	2	/* rcpt -- designate recipient */
8938032Speter# define CMDDATA	3	/* data -- send message text */
9038032Speter# define CMDRSET	4	/* rset -- reset state */
9138032Speter# define CMDVRFY	5	/* vrfy -- verify address */
9238032Speter# define CMDEXPN	6	/* expn -- expand address */
9338032Speter# define CMDNOOP	7	/* noop -- do nothing */
9438032Speter# define CMDQUIT	8	/* quit -- close connection and die */
9538032Speter# define CMDHELO	9	/* helo -- be polite */
9638032Speter# define CMDHELP	10	/* help -- give usage info */
9738032Speter# define CMDEHLO	11	/* ehlo -- extended helo (RFC 1425) */
9838032Speter# define CMDETRN	12	/* etrn -- flush queue */
9964562Sgshapiro# if SASL
10064562Sgshapiro#  define CMDAUTH	13	/* auth -- SASL authenticate */
10164562Sgshapiro# endif /* SASL */
10264562Sgshapiro# if STARTTLS
10364562Sgshapiro#  define CMDSTLS	14	/* STARTTLS -- start TLS session */
10464562Sgshapiro# endif /* STARTTLS */
10538032Speter/* non-standard commands */
10638032Speter# define CMDONEX	16	/* onex -- sending one transaction only */
10738032Speter# define CMDVERB	17	/* verb -- go into verbose mode */
10838032Speter# define CMDXUSR	18	/* xusr -- initial (user) submission */
10964562Sgshapiro/* unimplemented commands from RFC 821 */
11064562Sgshapiro# define CMDUNIMPL	19	/* unimplemented rfc821 commands */
11138032Speter/* use this to catch and log "door handle" attempts on your system */
11238032Speter# define CMDLOGBOGUS	23	/* bogus command that should be logged */
11338032Speter/* debugging-only commands, only enabled if SMTPDEBUG is defined */
11438032Speter# define CMDDBGQSHOW	24	/* showq -- show send queue */
11538032Speter# define CMDDBGDEBUG	25	/* debug -- set debug mode */
11638032Speter
11766494Sgshapiro/*
11866494Sgshapiro**  Note: If you change this list,
11966494Sgshapiro**        remember to update 'helpfile'
12066494Sgshapiro*/
12166494Sgshapiro
12238032Speterstatic struct cmd	CmdTab[] =
12338032Speter{
12438032Speter	{ "mail",	CMDMAIL		},
12538032Speter	{ "rcpt",	CMDRCPT		},
12638032Speter	{ "data",	CMDDATA		},
12738032Speter	{ "rset",	CMDRSET		},
12838032Speter	{ "vrfy",	CMDVRFY		},
12938032Speter	{ "expn",	CMDEXPN		},
13038032Speter	{ "help",	CMDHELP		},
13138032Speter	{ "noop",	CMDNOOP		},
13238032Speter	{ "quit",	CMDQUIT		},
13338032Speter	{ "helo",	CMDHELO		},
13438032Speter	{ "ehlo",	CMDEHLO		},
13538032Speter	{ "etrn",	CMDETRN		},
13638032Speter	{ "verb",	CMDVERB		},
13738032Speter	{ "onex",	CMDONEX		},
13838032Speter	{ "xusr",	CMDXUSR		},
13964562Sgshapiro	{ "send",	CMDUNIMPL	},
14064562Sgshapiro	{ "saml",	CMDUNIMPL	},
14164562Sgshapiro	{ "soml",	CMDUNIMPL	},
14264562Sgshapiro	{ "turn",	CMDUNIMPL	},
14364562Sgshapiro# if SASL
14464562Sgshapiro	{ "auth",	CMDAUTH,	},
14564562Sgshapiro# endif /* SASL */
14664562Sgshapiro# if STARTTLS
14764562Sgshapiro	{ "starttls",	CMDSTLS,	},
14864562Sgshapiro# endif /* STARTTLS */
14938032Speter    /* remaining commands are here only to trap and log attempts to use them */
15038032Speter	{ "showq",	CMDDBGQSHOW	},
15138032Speter	{ "debug",	CMDDBGDEBUG	},
15238032Speter	{ "wiz",	CMDLOGBOGUS	},
15338032Speter
15438032Speter	{ NULL,		CMDERROR	}
15538032Speter};
15638032Speter
15764562Sgshapirostatic bool	OneXact = FALSE;	/* one xaction only this run */
15864562Sgshapirostatic char	*CurSmtpClient;		/* who's at the other end of channel */
15938032Speter
16064562Sgshapiro# define MAXBADCOMMANDS		25	/* maximum number of bad commands */
16164562Sgshapiro# define MAXNOOPCOMMANDS	20	/* max "noise" commands before slowdown */
16264562Sgshapiro# define MAXHELOCOMMANDS	3	/* max HELO/EHLO commands before slowdown */
16364562Sgshapiro# define MAXVRFYCOMMANDS	6	/* max VRFY/EXPN commands before slowdown */
16464562Sgshapiro# define MAXETRNCOMMANDS	8	/* max ETRN commands before slowdown */
16564562Sgshapiro# define MAXTIMEOUT	(4 * 60)	/* max timeout for bad commands */
16638032Speter
16764562Sgshapiro/* runinchild() returns */
16864562Sgshapiro# define RIC_INCHILD		0	/* in a child process */
16964562Sgshapiro# define RIC_INPARENT		1	/* still in parent process */
17064562Sgshapiro# define RIC_TEMPFAIL		2	/* temporary failure occurred */
17138032Speter
17238032Spetervoid
17364562Sgshapirosmtp(nullserver, d_flags, e)
17464562Sgshapiro	char *volatile nullserver;
17564562Sgshapiro	BITMAP256 d_flags;
17638032Speter	register ENVELOPE *volatile e;
17738032Speter{
17838032Speter	register char *volatile p;
17964562Sgshapiro	register struct cmd *volatile c = NULL;
18038032Speter	char *cmd;
18138032Speter	auto ADDRESS *vrfyqueue;
18238032Speter	ADDRESS *a;
18338032Speter	volatile bool gotmail;		/* mail command received */
18438032Speter	volatile bool gothello;		/* helo command received */
18538032Speter	bool vrfy;			/* set if this is a vrfy command */
18638032Speter	char *volatile protocol;	/* sending protocol */
18738032Speter	char *volatile sendinghost;	/* sending hostname */
18838032Speter	char *volatile peerhostname;	/* name of SMTP peer or "localhost" */
18938032Speter	auto char *delimptr;
19038032Speter	char *id;
19138032Speter	volatile int nrcpts = 0;	/* number of RCPT commands */
19264562Sgshapiro	int ric;
19338032Speter	bool doublequeue;
19442575Speter	volatile bool discard;
19538032Speter	volatile int badcommands = 0;	/* count of bad commands */
19638032Speter	volatile int nverifies = 0;	/* count of VRFY/EXPN commands */
19738032Speter	volatile int n_etrn = 0;	/* count of ETRN commands */
19838032Speter	volatile int n_noop = 0;	/* count of NOOP/VERB/ONEX etc cmds */
19938032Speter	volatile int n_helo = 0;	/* count of HELO/EHLO commands */
20064562Sgshapiro	volatile int delay = 1;		/* timeout for bad commands */
20138032Speter	bool ok;
20264562Sgshapiro	volatile bool tempfail = FALSE;
20364562Sgshapiro# if _FFR_MILTER
20464562Sgshapiro	volatile bool milterize = (nullserver == NULL);
20564562Sgshapiro# endif /* _FFR_MILTER */
20664562Sgshapiro	volatile time_t wt;		/* timeout after too many commands */
20764562Sgshapiro	volatile time_t previous;	/* time after checksmtpattack() */
20864562Sgshapiro	volatile bool lognullconnection = TRUE;
20938032Speter	register char *q;
21064562Sgshapiro	char *addr;
21164562Sgshapiro	char *greetcode = "220";
21238032Speter	QUEUE_CHAR *new;
21364562Sgshapiro	int argno;
21464562Sgshapiro	char *args[MAXSMTPARGS];
21538032Speter	char inp[MAXLINE];
21638032Speter	char cmdbuf[MAXLINE];
21764562Sgshapiro# if SASL
21864562Sgshapiro	sasl_conn_t *conn;
21964562Sgshapiro	volatile bool sasl_ok;
22064562Sgshapiro	volatile int n_auth = 0;	/* count of AUTH commands */
22164562Sgshapiro	bool ismore;
22264562Sgshapiro	int result;
22364562Sgshapiro	volatile int authenticating;
22464562Sgshapiro	char *hostname;
22564562Sgshapiro	char *user;
22664562Sgshapiro	char *in, *out, *out2;
22764562Sgshapiro	const char *errstr;
22864562Sgshapiro	int inlen, out2len;
22964562Sgshapiro	unsigned int outlen;
23064562Sgshapiro	char *volatile auth_type;
23164562Sgshapiro	char *mechlist;
23264562Sgshapiro	volatile int n_mechs;
23364562Sgshapiro	int len;
23464562Sgshapiro	sasl_security_properties_t ssp;
23564562Sgshapiro	sasl_external_properties_t ext_ssf;
23664562Sgshapiro#  if SFIO
23764562Sgshapiro	sasl_ssf_t *ssf;
23864562Sgshapiro#  endif /* SFIO */
23964562Sgshapiro# endif /* SASL */
24064562Sgshapiro# if STARTTLS
24164562Sgshapiro	int r;
24266494Sgshapiro	int rfd, wfd;
24364562Sgshapiro	volatile bool usetls = TRUE;
24464562Sgshapiro	volatile bool tls_active = FALSE;
24564562Sgshapiro	bool saveQuickAbort;
24664562Sgshapiro	bool saveSuprErrs;
24764562Sgshapiro# endif /* STARTTLS */
24838032Speter
24938032Speter	if (fileno(OutChannel) != fileno(stdout))
25038032Speter	{
25138032Speter		/* arrange for debugging output to go to remote host */
25238032Speter		(void) dup2(fileno(OutChannel), fileno(stdout));
25338032Speter	}
25464562Sgshapiro
25538032Speter	settime(e);
25664562Sgshapiro	(void)sm_getla(e);
25738032Speter	peerhostname = RealHostName;
25838032Speter	if (peerhostname == NULL)
25938032Speter		peerhostname = "localhost";
26038032Speter	CurHostName = peerhostname;
26138032Speter	CurSmtpClient = macvalue('_', e);
26238032Speter	if (CurSmtpClient == NULL)
26338032Speter		CurSmtpClient = CurHostName;
26438032Speter
26538032Speter	/* check_relay may have set discard bit, save for later */
26638032Speter	discard = bitset(EF_DISCARD, e->e_flags);
26738032Speter
26864562Sgshapiro	sm_setproctitle(TRUE, e, "server %s startup", CurSmtpClient);
26964562Sgshapiro
27064562Sgshapiro# if SASL
27164562Sgshapiro	sasl_ok = FALSE;	/* SASL can't be used (yet) */
27264562Sgshapiro	n_mechs = 0;
27364562Sgshapiro
27464562Sgshapiro	/* SASL server new connection */
27564562Sgshapiro	hostname = macvalue('j', e);
27664562Sgshapiro#  if SASL > 10505
27764562Sgshapiro	/* use empty realm: only works in SASL > 1.5.5 */
27864562Sgshapiro	result = sasl_server_new("smtp", hostname, "", NULL, 0, &conn);
27964562Sgshapiro#  else /* SASL > 10505 */
28064562Sgshapiro	/* use no realm -> realm is set to hostname by SASL lib */
28164562Sgshapiro	result = sasl_server_new("smtp", hostname, NULL, NULL, 0, &conn);
28264562Sgshapiro#  endif /* SASL > 10505 */
28364562Sgshapiro	if (result == SASL_OK)
28438032Speter	{
28564562Sgshapiro		sasl_ok = TRUE;
28664562Sgshapiro
28764562Sgshapiro		/*
28864562Sgshapiro		**  SASL set properties for sasl
28964562Sgshapiro		**  set local/remote IP
29064562Sgshapiro		**  XXX only IPv4: Cyrus SASL doesn't support anything else
29164562Sgshapiro		**
29264562Sgshapiro		**  XXX where exactly are these used/required?
29364562Sgshapiro		**  Kerberos_v4
29464562Sgshapiro		*/
29564562Sgshapiro
29664562Sgshapiro# if NETINET
29764562Sgshapiro		in = macvalue(macid("{daemon_family}", NULL), e);
29864562Sgshapiro		if (in != NULL && strcmp(in, "inet") == 0)
29964562Sgshapiro		{
30064562Sgshapiro			SOCKADDR_LEN_T addrsize;
30164562Sgshapiro			struct sockaddr_in saddr_l;
30264562Sgshapiro			struct sockaddr_in saddr_r;
30364562Sgshapiro
30464562Sgshapiro			addrsize = sizeof(struct sockaddr_in);
30564562Sgshapiro			if (getpeername(fileno(InChannel),
30664562Sgshapiro					(struct sockaddr *)&saddr_r,
30764562Sgshapiro					&addrsize) == 0)
30864562Sgshapiro			{
30964562Sgshapiro				sasl_setprop(conn, SASL_IP_REMOTE, &saddr_r);
31064562Sgshapiro				addrsize = sizeof(struct sockaddr_in);
31164562Sgshapiro				if (getsockname(fileno(InChannel),
31264562Sgshapiro						(struct sockaddr *)&saddr_l,
31364562Sgshapiro						&addrsize) == 0)
31464562Sgshapiro					sasl_setprop(conn, SASL_IP_LOCAL,
31564562Sgshapiro						     &saddr_l);
31664562Sgshapiro			}
31764562Sgshapiro		}
31864562Sgshapiro# endif /* NETINET */
31964562Sgshapiro
32064562Sgshapiro		authenticating = SASL_NOT_AUTH;
32164562Sgshapiro		auth_type = NULL;
32264562Sgshapiro		mechlist = NULL;
32364562Sgshapiro		user = NULL;
32464562Sgshapiro#  if 0
32564562Sgshapiro		define(macid("{auth_author}", NULL), NULL, &BlankEnvelope);
32664562Sgshapiro#  endif /* 0 */
32764562Sgshapiro
32864562Sgshapiro		/* set properties */
32964562Sgshapiro		(void) memset(&ssp, '\0', sizeof ssp);
33064562Sgshapiro#  if SFIO
33164562Sgshapiro		/* XXX should these be options settable via .cf ? */
33264562Sgshapiro		/* ssp.min_ssf = 0; is default due to memset() */
33364562Sgshapiro		{
33464562Sgshapiro			ssp.max_ssf = INT_MAX;
33564562Sgshapiro			ssp.maxbufsize = MAXOUTLEN;
33664562Sgshapiro		}
33764562Sgshapiro#  endif /* SFIO */
33864562Sgshapiro#  if _FFR_SASL_OPTS
33964562Sgshapiro		ssp.security_flags = SASLOpts & SASL_SEC_MASK;
34064562Sgshapiro#  endif /* _FFR_SASL_OPTS */
34164562Sgshapiro		sasl_ok = sasl_setprop(conn, SASL_SEC_PROPS, &ssp) == SASL_OK;
34264562Sgshapiro
34364562Sgshapiro		if (sasl_ok)
34464562Sgshapiro		{
34564562Sgshapiro			/*
34664562Sgshapiro			**  external security strength factor;
34764562Sgshapiro			**  we have none so zero
34864562Sgshapiro#   if STARTTLS
34964562Sgshapiro			**  we may have to change this for STARTTLS
35064562Sgshapiro			**  (dynamically)
35164562Sgshapiro#   endif
35264562Sgshapiro			*/
35364562Sgshapiro			ext_ssf.ssf = 0;
35464562Sgshapiro			ext_ssf.auth_id = NULL;
35564562Sgshapiro			sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
35664562Sgshapiro					       &ext_ssf) == SASL_OK;
35764562Sgshapiro		}
35864562Sgshapiro		if (sasl_ok)
35964562Sgshapiro		{
36064562Sgshapiro			n_mechs = saslmechs(conn, &mechlist);
36164562Sgshapiro			sasl_ok = n_mechs > 0;
36264562Sgshapiro		}
36338032Speter	}
36464562Sgshapiro	else
36564562Sgshapiro	{
36664562Sgshapiro		if (LogLevel > 9)
36764562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
36864562Sgshapiro				  "SASL error: sasl_server_new failed=%d",
36964562Sgshapiro				  result);
37064562Sgshapiro	}
37164562Sgshapiro# endif /* SASL */
37238032Speter
37364562Sgshapiro# if STARTTLS
37464562Sgshapiro#  if _FFR_TLS_O_T
37564562Sgshapiro	saveQuickAbort = QuickAbort;
37664562Sgshapiro	saveSuprErrs = SuprErrs;
37764562Sgshapiro	SuprErrs = TRUE;
37864562Sgshapiro	QuickAbort = FALSE;
37964562Sgshapiro	if (rscheck("offer_tls", CurSmtpClient, "", e, TRUE, FALSE, 8) != EX_OK
38064562Sgshapiro		    || Errors > 0)
38164562Sgshapiro		usetls = FALSE;
38264562Sgshapiro	QuickAbort = saveQuickAbort;
38364562Sgshapiro	SuprErrs = saveSuprErrs;
38464562Sgshapiro#  endif /* _FFR_TLS_O_T */
38564562Sgshapiro# endif /* STARTTLS */
38664562Sgshapiro
38764562Sgshapiro# if _FFR_MILTER
38864562Sgshapiro	if (milterize)
38964562Sgshapiro	{
39064562Sgshapiro		char state;
39164562Sgshapiro
39264562Sgshapiro		/* initialize mail filter connection */
39364562Sgshapiro		milter_init(e, &state);
39464562Sgshapiro		switch (state)
39564562Sgshapiro		{
39664562Sgshapiro		  case SMFIR_REJECT:
39764562Sgshapiro			greetcode = "554";
39864562Sgshapiro			nullserver = "Command rejected";
39964562Sgshapiro			milterize = FALSE;
40064562Sgshapiro			break;
40164562Sgshapiro
40264562Sgshapiro		  case SMFIR_TEMPFAIL:
40364562Sgshapiro			tempfail = TRUE;
40464562Sgshapiro			milterize = FALSE;
40564562Sgshapiro			break;
40664562Sgshapiro		}
40764562Sgshapiro	}
40864562Sgshapiro
40964562Sgshapiro	if (milterize && !bitset(EF_DISCARD, e->e_flags))
41064562Sgshapiro	{
41164562Sgshapiro		char state;
41264562Sgshapiro
41364562Sgshapiro		(void) milter_connect(peerhostname, RealHostAddr,
41464562Sgshapiro				      e, &state);
41564562Sgshapiro		switch (state)
41664562Sgshapiro		{
41764562Sgshapiro		  case SMFIR_REPLYCODE:	/* REPLYCODE shouldn't happen */
41864562Sgshapiro		  case SMFIR_REJECT:
41964562Sgshapiro			greetcode = "554";
42064562Sgshapiro			nullserver = "Command rejected";
42164562Sgshapiro			milterize = FALSE;
42264562Sgshapiro			break;
42364562Sgshapiro
42464562Sgshapiro		  case SMFIR_TEMPFAIL:
42564562Sgshapiro			tempfail = TRUE;
42664562Sgshapiro			milterize = FALSE;
42764562Sgshapiro			break;
42864562Sgshapiro		}
42964562Sgshapiro	}
43064562Sgshapiro# endif /* _FFR_MILTER */
43164562Sgshapiro
43238032Speter	/* output the first line, inserting "ESMTP" as second word */
43338032Speter	expand(SmtpGreeting, inp, sizeof inp, e);
43438032Speter	p = strchr(inp, '\n');
43538032Speter	if (p != NULL)
43638032Speter		*p++ = '\0';
43738032Speter	id = strchr(inp, ' ');
43838032Speter	if (id == NULL)
43938032Speter		id = &inp[strlen(inp)];
44064562Sgshapiro	if (p == NULL)
44164562Sgshapiro		snprintf(cmdbuf, sizeof cmdbuf,
44264562Sgshapiro			 "%s %%.*s ESMTP%%s", greetcode);
44364562Sgshapiro	else
44464562Sgshapiro		snprintf(cmdbuf, sizeof cmdbuf,
44564562Sgshapiro			 "%s-%%.*s ESMTP%%s", greetcode);
44666494Sgshapiro	message(cmdbuf, (int) (id - inp), inp, id);
44738032Speter
44838032Speter	/* output remaining lines */
44938032Speter	while ((id = p) != NULL && (p = strchr(id, '\n')) != NULL)
45038032Speter	{
45138032Speter		*p++ = '\0';
45238032Speter		if (isascii(*id) && isspace(*id))
45338032Speter			id++;
45464562Sgshapiro		(void) snprintf(cmdbuf, sizeof cmdbuf, "%s-%%s", greetcode);
45564562Sgshapiro		message(cmdbuf, id);
45638032Speter	}
45738032Speter	if (id != NULL)
45838032Speter	{
45938032Speter		if (isascii(*id) && isspace(*id))
46038032Speter			id++;
46164562Sgshapiro		(void) snprintf(cmdbuf, sizeof cmdbuf, "%s %%s", greetcode);
46264562Sgshapiro		message(cmdbuf, id);
46338032Speter	}
46438032Speter
46538032Speter	protocol = NULL;
46638032Speter	sendinghost = macvalue('s', e);
46738032Speter	gothello = FALSE;
46838032Speter	gotmail = FALSE;
46938032Speter	for (;;)
47038032Speter	{
47138032Speter		/* arrange for backout */
47238032Speter		(void) setjmp(TopFrame);
47338032Speter		QuickAbort = FALSE;
47438032Speter		HoldErrs = FALSE;
47538032Speter		SuprErrs = FALSE;
47638032Speter		LogUsrErrs = FALSE;
47738032Speter		OnlyOneError = TRUE;
47838032Speter		e->e_flags &= ~(EF_VRFYONLY|EF_GLOBALERRS);
47938032Speter
48038032Speter		/* setup for the read */
48138032Speter		e->e_to = NULL;
48238032Speter		Errors = 0;
48364562Sgshapiro		FileName = NULL;
48438032Speter		(void) fflush(stdout);
48538032Speter
48638032Speter		/* read the input line */
48738032Speter		SmtpPhase = "server cmd read";
48864562Sgshapiro		sm_setproctitle(TRUE, e, "server %s cmd read", CurSmtpClient);
48964562Sgshapiro# if SASL
49064562Sgshapiro		/*
49164562Sgshapiro		**  SMTP AUTH requires accepting any length,
49264562Sgshapiro		**  at least for challenge/response
49364562Sgshapiro		**  XXX
49464562Sgshapiro		*/
49564562Sgshapiro# endif /* SASL */
49638032Speter
49738032Speter		/* handle errors */
49864562Sgshapiro		if (ferror(OutChannel) ||
49964562Sgshapiro		    (p = sfgets(inp, sizeof inp, InChannel,
50064562Sgshapiro				TimeOuts.to_nextcommand, SmtpPhase)) == NULL)
50138032Speter		{
50264562Sgshapiro			char *d;
50364562Sgshapiro
50464562Sgshapiro			d = macvalue(macid("{daemon_name}", NULL), e);
50564562Sgshapiro			if (d == NULL)
50664562Sgshapiro				d = "stdin";
50738032Speter			/* end of file, just die */
50838032Speter			disconnect(1, e);
50964562Sgshapiro
51064562Sgshapiro# if _FFR_MILTER
51164562Sgshapiro			/* close out milter filters */
51264562Sgshapiro			milter_quit(e);
51364562Sgshapiro# endif /* _FFR_MILTER */
51464562Sgshapiro
51564562Sgshapiro			message("421 4.4.1 %s Lost input channel from %s",
51638032Speter				MyHostName, CurSmtpClient);
51738032Speter			if (LogLevel > (gotmail ? 1 : 19))
51838032Speter				sm_syslog(LOG_NOTICE, e->e_id,
51964562Sgshapiro					  "lost input channel from %.100s to %s after %s",
52064562Sgshapiro					  CurSmtpClient, d,
52164562Sgshapiro					  (c == NULL || c->cmd_name == NULL) ? "startup" : c->cmd_name);
52238032Speter			/*
52342575Speter			**  If have not accepted mail (DATA), do not bounce
52442575Speter			**  bad addresses back to sender.
52538032Speter			*/
52664562Sgshapiro
52738032Speter			if (bitset(EF_CLRQUEUE, e->e_flags))
52838032Speter				e->e_sendqueue = NULL;
52964562Sgshapiro			goto doquit;
53038032Speter		}
53138032Speter
53238032Speter		/* clean up end of line */
53338032Speter		fixcrlf(inp, TRUE);
53438032Speter
53564562Sgshapiro# if SASL
53664562Sgshapiro		if (authenticating == SASL_PROC_AUTH)
53764562Sgshapiro		{
53864562Sgshapiro#  if 0
53964562Sgshapiro			if (*inp == '\0')
54064562Sgshapiro			{
54164562Sgshapiro				authenticating = SASL_NOT_AUTH;
54264562Sgshapiro				message("501 5.5.2 missing input");
54364562Sgshapiro				continue;
54464562Sgshapiro			}
54564562Sgshapiro#  endif /* 0 */
54664562Sgshapiro			if (*inp == '*' && *(inp + 1) == '\0')
54764562Sgshapiro			{
54864562Sgshapiro				authenticating = SASL_NOT_AUTH;
54964562Sgshapiro
55064562Sgshapiro				/* rfc 2254 4. */
55164562Sgshapiro				message("501 5.0.0 AUTH aborted");
55264562Sgshapiro				continue;
55364562Sgshapiro			}
55464562Sgshapiro
55564562Sgshapiro			/* could this be shorter? XXX */
55664562Sgshapiro			out = xalloc(strlen(inp));
55764562Sgshapiro			result = sasl_decode64(inp, strlen(inp), out, &outlen);
55864562Sgshapiro			if (result != SASL_OK)
55964562Sgshapiro			{
56064562Sgshapiro				authenticating = SASL_NOT_AUTH;
56164562Sgshapiro
56264562Sgshapiro				/* rfc 2254 4. */
56364562Sgshapiro				message("501 5.5.4 cannot decode AUTH parameter %s",
56464562Sgshapiro					inp);
56564562Sgshapiro				continue;
56664562Sgshapiro			}
56764562Sgshapiro
56864562Sgshapiro			result = sasl_server_step(conn,	out, outlen,
56964562Sgshapiro						  &out, &outlen, &errstr);
57064562Sgshapiro
57164562Sgshapiro			/* get an OK if we're done */
57264562Sgshapiro			if (result == SASL_OK)
57364562Sgshapiro			{
57464562Sgshapiro  authenticated:
57564562Sgshapiro				message("235 2.0.0 OK Authenticated");
57664562Sgshapiro				authenticating = SASL_IS_AUTH;
57764562Sgshapiro				define(macid("{auth_type}", NULL),
57864562Sgshapiro				       newstr(auth_type), &BlankEnvelope);
57964562Sgshapiro
58064562Sgshapiro				result = sasl_getprop(conn, SASL_USERNAME,
58164562Sgshapiro						      (void **)&user);
58264562Sgshapiro				if (result != SASL_OK)
58364562Sgshapiro				{
58464562Sgshapiro					user = "";
58564562Sgshapiro					define(macid("{auth_authen}", NULL),
58664562Sgshapiro					       NULL, &BlankEnvelope);
58764562Sgshapiro				}
58864562Sgshapiro				else
58964562Sgshapiro				{
59064562Sgshapiro					define(macid("{auth_authen}", NULL),
59164562Sgshapiro					       newstr(user), &BlankEnvelope);
59264562Sgshapiro				}
59364562Sgshapiro
59464562Sgshapiro#  if 0
59564562Sgshapiro				/* get realm? */
59664562Sgshapiro				sasl_getprop(conn, SASL_REALM, (void **) &data);
59764562Sgshapiro#  endif /* 0 */
59864562Sgshapiro
59964562Sgshapiro
60064562Sgshapiro#  if SFIO
60164562Sgshapiro				/* get security strength (features) */
60264562Sgshapiro				result = sasl_getprop(conn, SASL_SSF,
60364562Sgshapiro						      (void **) &ssf);
60464562Sgshapiro				if (result != SASL_OK)
60564562Sgshapiro				{
60664562Sgshapiro					define(macid("{auth_ssf}", NULL),
60764562Sgshapiro					       "0", &BlankEnvelope);
60864562Sgshapiro					ssf = NULL;
60964562Sgshapiro				}
61064562Sgshapiro				else
61164562Sgshapiro				{
61264562Sgshapiro					char pbuf[8];
61364562Sgshapiro
61464562Sgshapiro					snprintf(pbuf, sizeof pbuf, "%u", *ssf);
61564562Sgshapiro					define(macid("{auth_ssf}", NULL),
61664562Sgshapiro					       newstr(pbuf), &BlankEnvelope);
61764562Sgshapiro					if (tTd(95, 8))
61864562Sgshapiro						dprintf("SASL auth_ssf: %u\n",
61964562Sgshapiro							*ssf);
62064562Sgshapiro				}
62164562Sgshapiro				/*
62264562Sgshapiro				**  only switch to encrypted connection
62364562Sgshapiro				**  if a security layer has been negotiated
62464562Sgshapiro				*/
62564562Sgshapiro				if (ssf != NULL && *ssf > 0)
62664562Sgshapiro				{
62764562Sgshapiro					/*
62864562Sgshapiro					**  convert sfio stuff to use SASL
62964562Sgshapiro					**  check return values
63064562Sgshapiro					**  if the call fails,
63164562Sgshapiro					**  fall back to unencrypted version
63264562Sgshapiro					**  unless some cf option requires
63364562Sgshapiro					**  encryption then the connection must
63464562Sgshapiro					**  be aborted
63564562Sgshapiro					*/
63664562Sgshapiro					if (sfdcsasl(InChannel, OutChannel,
63764562Sgshapiro					    conn) == 0)
63864562Sgshapiro					{
63964562Sgshapiro						/* restart dialogue */
64064562Sgshapiro						gothello = FALSE;
64164562Sgshapiro						OneXact = TRUE;
64264562Sgshapiro						n_helo = 0;
64364562Sgshapiro					}
64464562Sgshapiro					else
64564562Sgshapiro						syserr("503 5.3.3 SASL TLS failed");
64664562Sgshapiro					if (LogLevel > 9)
64764562Sgshapiro						sm_syslog(LOG_INFO,
64864562Sgshapiro							  NOQID,
64964562Sgshapiro							  "SASL: connection from %.64s: mech=%.16s, id=%.64s, bits=%d",
65064562Sgshapiro							  CurSmtpClient,
65164562Sgshapiro							  auth_type, user,
65264562Sgshapiro							  *ssf);
65364562Sgshapiro				}
65464562Sgshapiro#  else /* SFIO */
65564562Sgshapiro				if (LogLevel > 9)
65664562Sgshapiro					sm_syslog(LOG_INFO, NOQID,
65764562Sgshapiro						  "SASL: connection from %.64s: mech=%.16s, id=%.64s",
65864562Sgshapiro						  CurSmtpClient, auth_type,
65964562Sgshapiro						  user);
66064562Sgshapiro#  endif /* SFIO */
66164562Sgshapiro			}
66264562Sgshapiro			else if (result == SASL_CONTINUE)
66364562Sgshapiro			{
66464562Sgshapiro				len = ENC64LEN(outlen);
66564562Sgshapiro				out2 = xalloc(len);
66664562Sgshapiro				result = sasl_encode64(out, outlen, out2, len,
66764562Sgshapiro						       (u_int *)&out2len);
66864562Sgshapiro				if (result != SASL_OK)
66964562Sgshapiro				{
67064562Sgshapiro					/* correct code? XXX */
67164562Sgshapiro					/* 454 Temp. authentication failure */
67264562Sgshapiro					message("454 4.5.4 Internal error: unable to encode64");
67364562Sgshapiro					if (LogLevel > 5)
67464562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
67564562Sgshapiro							  "SASL encode64 error [%d for \"%s\"]",
67664562Sgshapiro							  result, out);
67764562Sgshapiro					/* start over? */
67864562Sgshapiro					authenticating = SASL_NOT_AUTH;
67964562Sgshapiro				}
68064562Sgshapiro				else
68164562Sgshapiro				{
68264562Sgshapiro					message("334 %s", out2);
68364562Sgshapiro					if (tTd(95, 2))
68464562Sgshapiro						dprintf("SASL continue: msg='%s' len=%d\n",
68564562Sgshapiro							out2, out2len);
68664562Sgshapiro				}
68764562Sgshapiro			}
68864562Sgshapiro			else
68964562Sgshapiro			{
69064562Sgshapiro				/* not SASL_OK or SASL_CONT */
69164562Sgshapiro				message("500 5.7.0 authentication failed");
69264562Sgshapiro				if (LogLevel > 9)
69364562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
69464562Sgshapiro						  "AUTH failure (%s): %s (%d)",
69564562Sgshapiro						  auth_type,
69664562Sgshapiro						  sasl_errstring(result, NULL,
69764562Sgshapiro								 NULL),
69864562Sgshapiro						  result);
69964562Sgshapiro				authenticating = SASL_NOT_AUTH;
70064562Sgshapiro			}
70164562Sgshapiro		}
70264562Sgshapiro		else
70364562Sgshapiro		{
70464562Sgshapiro			/* don't want to do any of this if authenticating */
70564562Sgshapiro# endif /* SASL */
70664562Sgshapiro
70738032Speter		/* echo command to transcript */
70838032Speter		if (e->e_xfp != NULL)
70938032Speter			fprintf(e->e_xfp, "<<< %s\n", inp);
71038032Speter
71138032Speter		if (LogLevel >= 15)
71238032Speter			sm_syslog(LOG_INFO, e->e_id,
71364562Sgshapiro				  "<-- %s",
71464562Sgshapiro				  inp);
71538032Speter
71638032Speter		if (e->e_id == NULL)
71764562Sgshapiro			sm_setproctitle(TRUE, e, "%s: %.80s",
71864562Sgshapiro					CurSmtpClient, inp);
71938032Speter		else
72064562Sgshapiro			sm_setproctitle(TRUE, e, "%s %s: %.80s",
72164562Sgshapiro					qid_printname(e),
72264562Sgshapiro					CurSmtpClient, inp);
72338032Speter
72438032Speter		/* break off command */
72538032Speter		for (p = inp; isascii(*p) && isspace(*p); p++)
72638032Speter			continue;
72738032Speter		cmd = cmdbuf;
72838032Speter		while (*p != '\0' &&
72938032Speter		       !(isascii(*p) && isspace(*p)) &&
73038032Speter		       cmd < &cmdbuf[sizeof cmdbuf - 2])
73138032Speter			*cmd++ = *p++;
73238032Speter		*cmd = '\0';
73338032Speter
73438032Speter		/* throw away leading whitespace */
73538032Speter		while (isascii(*p) && isspace(*p))
73638032Speter			p++;
73738032Speter
73838032Speter		/* decode command */
73964562Sgshapiro		for (c = CmdTab; c->cmd_name != NULL; c++)
74038032Speter		{
74164562Sgshapiro			if (strcasecmp(c->cmd_name, cmdbuf) == 0)
74238032Speter				break;
74338032Speter		}
74438032Speter
74538032Speter		/* reset errors */
74638032Speter		errno = 0;
74738032Speter
74838032Speter		/*
74938032Speter		**  Process command.
75038032Speter		**
75138032Speter		**	If we are running as a null server, return 550
75238032Speter		**	to everything.
75338032Speter		*/
75438032Speter
75564562Sgshapiro		if (nullserver != NULL || bitnset(D_ETRNONLY, d_flags))
75638032Speter		{
75764562Sgshapiro			switch (c->cmd_code)
75838032Speter			{
75938032Speter			  case CMDQUIT:
76038032Speter			  case CMDHELO:
76138032Speter			  case CMDEHLO:
76238032Speter			  case CMDNOOP:
76364562Sgshapiro			  case CMDRSET:
76438032Speter				/* process normally */
76538032Speter				break;
76638032Speter
76764562Sgshapiro			  case CMDETRN:
76864562Sgshapiro				if (bitnset(D_ETRNONLY, d_flags) &&
76964562Sgshapiro				    nullserver == NULL)
77064562Sgshapiro					break;
77164562Sgshapiro				continue;
77264562Sgshapiro
77338032Speter			  default:
77438032Speter				if (++badcommands > MAXBADCOMMANDS)
77564562Sgshapiro				{
77664562Sgshapiro					delay *= 2;
77764562Sgshapiro					if (delay >= MAXTIMEOUT)
77864562Sgshapiro						delay = MAXTIMEOUT;
77964562Sgshapiro					(void) sleep(delay);
78064562Sgshapiro				}
78164562Sgshapiro				if (nullserver != NULL)
78264562Sgshapiro				{
78364562Sgshapiro					if (ISSMTPREPLY(nullserver))
78464562Sgshapiro						usrerr(nullserver);
78564562Sgshapiro					else
78664562Sgshapiro						usrerr("550 5.0.0 %s", nullserver);
78764562Sgshapiro				}
78864562Sgshapiro				else
78964562Sgshapiro					usrerr("452 4.4.5 Insufficient disk space; try again later");
79038032Speter				continue;
79138032Speter			}
79238032Speter		}
79338032Speter
79438032Speter		/* non-null server */
79564562Sgshapiro		switch (c->cmd_code)
79638032Speter		{
79738032Speter		  case CMDMAIL:
79838032Speter		  case CMDEXPN:
79938032Speter		  case CMDVRFY:
80038032Speter		  case CMDETRN:
80138032Speter			lognullconnection = FALSE;
80238032Speter		}
80338032Speter
80464562Sgshapiro		switch (c->cmd_code)
80538032Speter		{
80664562Sgshapiro# if SASL
80764562Sgshapiro		  case CMDAUTH: /* sasl */
80864562Sgshapiro			if (!sasl_ok)
80964562Sgshapiro			{
81064562Sgshapiro				message("503 5.3.3 AUTH not available");
81164562Sgshapiro				break;
81264562Sgshapiro			}
81364562Sgshapiro			if (authenticating == SASL_IS_AUTH)
81464562Sgshapiro			{
81564562Sgshapiro				message("503 5.5.0 Already Authenticated");
81664562Sgshapiro				break;
81764562Sgshapiro			}
81864562Sgshapiro			if (gotmail)
81964562Sgshapiro			{
82064562Sgshapiro				message("503 5.5.0 AUTH not permitted during a mail transaction");
82164562Sgshapiro				break;
82264562Sgshapiro			}
82364562Sgshapiro			if (tempfail)
82464562Sgshapiro			{
82564562Sgshapiro				if (LogLevel > 9)
82664562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
82764562Sgshapiro						  "SMTP AUTH command (%.100s) from %.100s tempfailed (due to previous checks)",
82864562Sgshapiro						  p, CurSmtpClient);
82964562Sgshapiro				usrerr("454 4.7.1 Please try again later");
83064562Sgshapiro				break;
83164562Sgshapiro			}
83264562Sgshapiro
83364562Sgshapiro			ismore = FALSE;
83464562Sgshapiro
83564562Sgshapiro			/* crude way to avoid crack attempts */
83664562Sgshapiro			(void) checksmtpattack(&n_auth, n_mechs + 1, TRUE,
83764562Sgshapiro					       "AUTH", e);
83864562Sgshapiro
83964562Sgshapiro			/* make sure it's a valid string */
84064562Sgshapiro			for (q = p; *q != '\0' && isascii(*q); q++)
84164562Sgshapiro			{
84264562Sgshapiro				if (isspace(*q))
84364562Sgshapiro				{
84464562Sgshapiro					*q = '\0';
84564562Sgshapiro					while (*++q != '\0' &&
84664562Sgshapiro					       isascii(*q) && isspace(*q))
84764562Sgshapiro						continue;
84864562Sgshapiro					*(q - 1) = '\0';
84964562Sgshapiro					ismore = (*q != '\0');
85064562Sgshapiro					break;
85164562Sgshapiro				}
85264562Sgshapiro			}
85364562Sgshapiro
85464562Sgshapiro			/* check whether mechanism is available */
85564562Sgshapiro			if (iteminlist(p, mechlist, " ") == NULL)
85664562Sgshapiro			{
85764562Sgshapiro				message("503 5.3.3 AUTH mechanism %s not available",
85864562Sgshapiro					p);
85964562Sgshapiro				break;
86064562Sgshapiro			}
86164562Sgshapiro
86264562Sgshapiro			if (ismore)
86364562Sgshapiro			{
86464562Sgshapiro				/* could this be shorter? XXX */
86564562Sgshapiro				in = xalloc(strlen(q));
86664562Sgshapiro				result = sasl_decode64(q, strlen(q), in,
86764562Sgshapiro						       (u_int *)&inlen);
86864562Sgshapiro				if (result != SASL_OK)
86964562Sgshapiro				{
87064562Sgshapiro					message("501 5.5.4 cannot BASE64 decode '%s'",
87164562Sgshapiro						q);
87264562Sgshapiro					if (LogLevel > 5)
87364562Sgshapiro						sm_syslog(LOG_WARNING, e->e_id,
87464562Sgshapiro							  "SASL decode64 error [%d for \"%s\"]",
87564562Sgshapiro							  result, q);
87664562Sgshapiro					/* start over? */
87764562Sgshapiro					authenticating = SASL_NOT_AUTH;
87864562Sgshapiro					in = NULL;
87964562Sgshapiro					inlen = 0;
88064562Sgshapiro					break;
88164562Sgshapiro				}
88264562Sgshapiro#  if 0
88364562Sgshapiro				if (tTd(95, 99))
88464562Sgshapiro				{
88564562Sgshapiro					int i;
88664562Sgshapiro
88764562Sgshapiro					dprintf("AUTH: more \"");
88864562Sgshapiro					for (i = 0; i < inlen; i++)
88964562Sgshapiro					{
89064562Sgshapiro						if (isascii(in[i]) &&
89164562Sgshapiro						    isprint(in[i]))
89264562Sgshapiro							dprintf("%c", in[i]);
89364562Sgshapiro						else
89464562Sgshapiro							dprintf("_");
89564562Sgshapiro					}
89664562Sgshapiro					dprintf("\"\n");
89764562Sgshapiro				}
89864562Sgshapiro#  endif /* 0 */
89964562Sgshapiro			}
90064562Sgshapiro			else
90164562Sgshapiro			{
90264562Sgshapiro				in = NULL;
90364562Sgshapiro				inlen = 0;
90464562Sgshapiro			}
90564562Sgshapiro
90664562Sgshapiro			/* see if that auth type exists */
90764562Sgshapiro			result = sasl_server_start(conn, p, in, inlen,
90864562Sgshapiro						   &out, &outlen, &errstr);
90964562Sgshapiro
91064562Sgshapiro			if (result != SASL_OK && result != SASL_CONTINUE)
91164562Sgshapiro			{
91264562Sgshapiro				message("500 5.7.0 authentication failed");
91364562Sgshapiro				if (LogLevel > 9)
91464562Sgshapiro					sm_syslog(LOG_ERR, e->e_id,
91564562Sgshapiro						  "AUTH failure (%s): %s (%d)",
91664562Sgshapiro						  p,
91764562Sgshapiro						  sasl_errstring(result, NULL,
91864562Sgshapiro								 NULL),
91964562Sgshapiro						  result);
92064562Sgshapiro				break;
92164562Sgshapiro			}
92264562Sgshapiro			auth_type = newstr(p);
92364562Sgshapiro
92464562Sgshapiro			if (result == SASL_OK)
92564562Sgshapiro			{
92664562Sgshapiro				/* ugly, but same code */
92764562Sgshapiro				goto authenticated;
92864562Sgshapiro				/* authenticated by the initial response */
92964562Sgshapiro			}
93064562Sgshapiro
93164562Sgshapiro			/* len is at least 2 */
93264562Sgshapiro			len = ENC64LEN(outlen);
93364562Sgshapiro			out2 = xalloc(len);
93464562Sgshapiro			result = sasl_encode64(out, outlen, out2, len,
93564562Sgshapiro					       (u_int *)&out2len);
93664562Sgshapiro
93764562Sgshapiro			if (result != SASL_OK)
93864562Sgshapiro			{
93964562Sgshapiro				message("454 4.5.4 Temporary authentication failure");
94064562Sgshapiro				if (LogLevel > 5)
94164562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
94264562Sgshapiro						  "SASL encode64 error [%d for \"%s\"]",
94364562Sgshapiro						  result, out);
94464562Sgshapiro
94564562Sgshapiro				/* start over? */
94664562Sgshapiro				authenticating = SASL_NOT_AUTH;
94764562Sgshapiro			}
94864562Sgshapiro			else
94964562Sgshapiro			{
95064562Sgshapiro				message("334 %s", out2);
95164562Sgshapiro				authenticating = SASL_PROC_AUTH;
95264562Sgshapiro			}
95364562Sgshapiro
95464562Sgshapiro			break;
95564562Sgshapiro# endif /* SASL */
95664562Sgshapiro
95764562Sgshapiro# if STARTTLS
95864562Sgshapiro		  case CMDSTLS: /* starttls */
95964562Sgshapiro			if (*p != '\0')
96064562Sgshapiro			{
96164562Sgshapiro				message("501 5.5.2 Syntax error (no parameters allowed)");
96264562Sgshapiro				break;
96364562Sgshapiro			}
96464562Sgshapiro			if (!usetls)
96564562Sgshapiro			{
96664562Sgshapiro				message("503 5.5.0 TLS not available");
96764562Sgshapiro				break;
96864562Sgshapiro			}
96964562Sgshapiro			if (!tls_ok)
97064562Sgshapiro			{
97164562Sgshapiro				message("454 4.3.3 TLS not available after start");
97264562Sgshapiro				break;
97364562Sgshapiro			}
97464562Sgshapiro			if (gotmail)
97564562Sgshapiro			{
97664562Sgshapiro				message("503 5.5.0 TLS not permitted during a mail transaction");
97764562Sgshapiro				break;
97864562Sgshapiro			}
97964562Sgshapiro			if (tempfail)
98064562Sgshapiro			{
98164562Sgshapiro				if (LogLevel > 9)
98264562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
98364562Sgshapiro						  "SMTP STARTTLS command (%.100s) from %.100s tempfailed (due to previous checks)",
98464562Sgshapiro						  p, CurSmtpClient);
98564562Sgshapiro				usrerr("454 4.7.1 Please try again later");
98664562Sgshapiro				break;
98764562Sgshapiro			}
98864562Sgshapiro# if TLS_NO_RSA
98964562Sgshapiro			/*
99064562Sgshapiro			**  XXX do we need a temp key ?
99164562Sgshapiro			*/
99264562Sgshapiro# else /* TLS_NO_RSA */
99364562Sgshapiro			if (SSL_CTX_need_tmp_RSA(srv_ctx) &&
99464562Sgshapiro			   !SSL_CTX_set_tmp_rsa(srv_ctx,
99564562Sgshapiro				(rsa = RSA_generate_key(RSA_KEYLENGTH, RSA_F4,
99664562Sgshapiro							NULL, NULL)))
99764562Sgshapiro			  )
99864562Sgshapiro			{
99964562Sgshapiro				message("454 4.3.3 TLS not available: error generating RSA temp key");
100064562Sgshapiro				if (rsa != NULL)
100164562Sgshapiro					RSA_free(rsa);
100264562Sgshapiro				break;
100364562Sgshapiro			}
100464562Sgshapiro# endif /* TLS_NO_RSA */
100564562Sgshapiro			if (srv_ssl != NULL)
100664562Sgshapiro				SSL_clear(srv_ssl);
100764562Sgshapiro			else if ((srv_ssl = SSL_new(srv_ctx)) == NULL)
100864562Sgshapiro			{
100964562Sgshapiro				message("454 4.3.3 TLS not available: error generating SSL handle");
101064562Sgshapiro				break;
101164562Sgshapiro			}
101266494Sgshapiro			rfd = fileno(InChannel);
101366494Sgshapiro			wfd = fileno(OutChannel);
101466494Sgshapiro			if (rfd < 0 || wfd < 0 ||
101566494Sgshapiro			    SSL_set_rfd(srv_ssl, rfd) <= 0 ||
101666494Sgshapiro			    SSL_set_wfd(srv_ssl, wfd) <= 0)
101764562Sgshapiro			{
101864562Sgshapiro				message("454 4.3.3 TLS not available: error set fd");
101964562Sgshapiro				SSL_free(srv_ssl);
102064562Sgshapiro				srv_ssl = NULL;
102164562Sgshapiro				break;
102264562Sgshapiro			}
102364562Sgshapiro			message("220 2.0.0 Ready to start TLS");
102464562Sgshapiro			SSL_set_accept_state(srv_ssl);
102564562Sgshapiro
102664562Sgshapiro#  define SSL_ACC(s)	SSL_accept(s)
102764562Sgshapiro			if ((r = SSL_ACC(srv_ssl)) <= 0)
102864562Sgshapiro			{
102964562Sgshapiro				int i;
103064562Sgshapiro
103164562Sgshapiro				/* what to do in this case? */
103264562Sgshapiro				i = SSL_get_error(srv_ssl, r);
103364562Sgshapiro				if (LogLevel > 5)
103464562Sgshapiro				{
103564562Sgshapiro					sm_syslog(LOG_WARNING, e->e_id,
103664562Sgshapiro						  "TLS: error: accept failed=%d (%d)",
103764562Sgshapiro						  r, i);
103864562Sgshapiro					if (LogLevel > 9)
103964562Sgshapiro						tlslogerr();
104064562Sgshapiro				}
104164562Sgshapiro				tls_ok = FALSE;
104264562Sgshapiro				SSL_free(srv_ssl);
104364562Sgshapiro				srv_ssl = NULL;
104464562Sgshapiro
104564562Sgshapiro				/*
104664562Sgshapiro				**  according to the next draft of
104764562Sgshapiro				**  RFC 2487 the connection should be dropped
104864562Sgshapiro				*/
104964562Sgshapiro
105064562Sgshapiro				/* arrange to ignore any current send list */
105164562Sgshapiro				e->e_sendqueue = NULL;
105264562Sgshapiro				goto doquit;
105364562Sgshapiro			}
105464562Sgshapiro
105564562Sgshapiro			/* ignore return code for now, it's in {verify} */
105664562Sgshapiro			(void) tls_get_info(srv_ssl, &BlankEnvelope, TRUE,
105764562Sgshapiro					    CurSmtpClient);
105864562Sgshapiro
105964562Sgshapiro			/*
106064562Sgshapiro			**  call Stls_client to find out whether
106164562Sgshapiro			**  to accept the connection from the client
106264562Sgshapiro			*/
106364562Sgshapiro
106464562Sgshapiro			saveQuickAbort = QuickAbort;
106564562Sgshapiro			saveSuprErrs = SuprErrs;
106664562Sgshapiro			SuprErrs = TRUE;
106764562Sgshapiro			QuickAbort = FALSE;
106864562Sgshapiro			if (rscheck("tls_client",
106964562Sgshapiro				     macvalue(macid("{verify}", NULL), e),
107064562Sgshapiro				     "STARTTLS", e, TRUE, TRUE, 6) != EX_OK ||
107164562Sgshapiro			    Errors > 0)
107264562Sgshapiro			{
107364562Sgshapiro				extern char MsgBuf[];
107464562Sgshapiro
107564562Sgshapiro				if (MsgBuf[0] != '\0' && ISSMTPREPLY(MsgBuf))
107664562Sgshapiro					nullserver = newstr(MsgBuf);
107764562Sgshapiro				else
107864562Sgshapiro					nullserver = "503 5.7.0 Authentication required.";
107964562Sgshapiro			}
108064562Sgshapiro			QuickAbort = saveQuickAbort;
108164562Sgshapiro			SuprErrs = saveSuprErrs;
108264562Sgshapiro
108364562Sgshapiro			tls_ok = FALSE;	/* don't offer STARTTLS again */
108464562Sgshapiro			gothello = FALSE;	/* discard info */
108564562Sgshapiro			n_helo = 0;
108664562Sgshapiro			OneXact = TRUE;	/* only one xaction this run */
108764562Sgshapiro#  if SASL
108864562Sgshapiro			if (sasl_ok)
108964562Sgshapiro			{
109064562Sgshapiro				char *s;
109164562Sgshapiro
109264562Sgshapiro				if ((s = macvalue(macid("{cipher_bits}", NULL), e)) != NULL &&
109364562Sgshapiro				    (ext_ssf.ssf = atoi(s)) > 0)
109464562Sgshapiro				{
109564562Sgshapiro#  if _FFR_EXT_MECH
109664562Sgshapiro					ext_ssf.auth_id = macvalue(macid("{cert_subject}",
109764562Sgshapiro									 NULL),
109864562Sgshapiro								   e);
109964562Sgshapiro#  endif /* _FFR_EXT_MECH */
110064562Sgshapiro					sasl_ok = sasl_setprop(conn, SASL_SSF_EXTERNAL,
110164562Sgshapiro							       &ext_ssf) == SASL_OK;
110264562Sgshapiro					if (mechlist != NULL)
110364562Sgshapiro						free(mechlist);
110464562Sgshapiro					mechlist = NULL;
110564562Sgshapiro					if (sasl_ok)
110664562Sgshapiro					{
110764562Sgshapiro						n_mechs = saslmechs(conn,
110864562Sgshapiro								    &mechlist);
110964562Sgshapiro						sasl_ok = n_mechs > 0;
111064562Sgshapiro					}
111164562Sgshapiro				}
111264562Sgshapiro			}
111364562Sgshapiro#  endif /* SASL */
111464562Sgshapiro
111564562Sgshapiro			/* switch to secure connection */
111664562Sgshapiro#if SFIO
111764562Sgshapiro			r = sfdctls(InChannel, OutChannel, srv_ssl);
111864562Sgshapiro#else /* SFIO */
111964562Sgshapiro# if _FFR_TLS_TOREK
112064562Sgshapiro			r = sfdctls(&InChannel, &OutChannel, srv_ssl);
112164562Sgshapiro# endif /* _FFR_TLS_TOREK */
112264562Sgshapiro#endif /* SFIO */
112364562Sgshapiro			if (r == 0)
112464562Sgshapiro				tls_active = TRUE;
112564562Sgshapiro			else
112664562Sgshapiro			{
112764562Sgshapiro				/*
112864562Sgshapiro				**  XXX this is an internal error
112964562Sgshapiro				**  how to deal with it?
113064562Sgshapiro				**  we can't generate an error message
113164562Sgshapiro				**  since the other side switched to an
113264562Sgshapiro				**  encrypted layer, but we could not...
113364562Sgshapiro				**  just "hang up"?
113464562Sgshapiro				*/
113564562Sgshapiro				nullserver = "454 4.3.3 TLS not available: can't switch to encrypted layer";
113664562Sgshapiro				syserr("TLS: can't switch to encrypted layer");
113764562Sgshapiro			}
113864562Sgshapiro			break;
113964562Sgshapiro# endif /* STARTTLS */
114064562Sgshapiro
114138032Speter		  case CMDHELO:		/* hello -- introduce yourself */
114238032Speter		  case CMDEHLO:		/* extended hello */
114364562Sgshapiro			if (c->cmd_code == CMDEHLO)
114438032Speter			{
114538032Speter				protocol = "ESMTP";
114638032Speter				SmtpPhase = "server EHLO";
114738032Speter			}
114838032Speter			else
114938032Speter			{
115038032Speter				protocol = "SMTP";
115138032Speter				SmtpPhase = "server HELO";
115238032Speter			}
115338032Speter
115438032Speter			/* avoid denial-of-service */
115564562Sgshapiro			(void) checksmtpattack(&n_helo, MAXHELOCOMMANDS, TRUE,
115664562Sgshapiro					       "HELO/EHLO", e);
115738032Speter
115838032Speter			/* check for duplicate HELO/EHLO per RFC 1651 4.2 */
115938032Speter			if (gothello)
116038032Speter			{
116138032Speter				usrerr("503 %s Duplicate HELO/EHLO",
116238032Speter					MyHostName);
116338032Speter				break;
116438032Speter			}
116538032Speter
116638032Speter			/* check for valid domain name (re 1123 5.2.5) */
116738032Speter			if (*p == '\0' && !AllowBogusHELO)
116838032Speter			{
116938032Speter				usrerr("501 %s requires domain address",
117038032Speter					cmdbuf);
117138032Speter				break;
117238032Speter			}
117338032Speter
117438032Speter			/* check for long domain name (hides Received: info) */
117538032Speter			if (strlen(p) > MAXNAME)
117638032Speter			{
117738032Speter				usrerr("501 Invalid domain name");
117864562Sgshapiro				if (LogLevel > 9)
117964562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
118064562Sgshapiro						  "invalid domain name (too long) from %.100s",
118164562Sgshapiro						  CurSmtpClient);
118238032Speter				break;
118338032Speter			}
118438032Speter
118538032Speter			for (q = p; *q != '\0'; q++)
118638032Speter			{
118738032Speter				if (!isascii(*q))
118838032Speter					break;
118938032Speter				if (isalnum(*q))
119038032Speter					continue;
119138032Speter				if (isspace(*q))
119238032Speter				{
119338032Speter					*q = '\0';
119438032Speter					break;
119538032Speter				}
119638032Speter				if (strchr("[].-_#", *q) == NULL)
119738032Speter					break;
119838032Speter			}
119964562Sgshapiro
120038032Speter			if (*q == '\0')
120138032Speter			{
120238032Speter				q = "pleased to meet you";
120338032Speter				sendinghost = newstr(p);
120438032Speter			}
120538032Speter			else if (!AllowBogusHELO)
120638032Speter			{
120738032Speter				usrerr("501 Invalid domain name");
120864562Sgshapiro				if (LogLevel > 9)
120964562Sgshapiro					sm_syslog(LOG_INFO, CurEnv->e_id,
121064562Sgshapiro						  "invalid domain name (%.100s) from %.100s",
121164562Sgshapiro						  p, CurSmtpClient);
121238032Speter				break;
121338032Speter			}
121438032Speter			else
121538032Speter			{
121638032Speter				q = "accepting invalid domain name";
121738032Speter			}
121838032Speter
121938032Speter			gothello = TRUE;
122064562Sgshapiro
122164562Sgshapiro# if _FFR_MILTER
122264562Sgshapiro			if (milterize && !bitset(EF_DISCARD, e->e_flags))
122364562Sgshapiro			{
122464562Sgshapiro				char state;
122564562Sgshapiro				char *response;
122664562Sgshapiro
122764562Sgshapiro				response = milter_helo(p, e, &state);
122864562Sgshapiro				switch (state)
122964562Sgshapiro				{
123064562Sgshapiro				  case SMFIR_REPLYCODE:
123164562Sgshapiro					nullserver = response;
123264562Sgshapiro					milterize = FALSE;
123364562Sgshapiro					break;
123464562Sgshapiro
123564562Sgshapiro				  case SMFIR_REJECT:
123664562Sgshapiro					nullserver = "Command rejected";
123764562Sgshapiro					milterize = FALSE;
123864562Sgshapiro					break;
123964562Sgshapiro
124064562Sgshapiro				  case SMFIR_TEMPFAIL:
124164562Sgshapiro					tempfail = TRUE;
124264562Sgshapiro					milterize = FALSE;
124364562Sgshapiro					break;
124464562Sgshapiro				}
124564562Sgshapiro			}
124664562Sgshapiro# endif /* _FFR_MILTER */
124764562Sgshapiro
124838032Speter			/* print HELO response message */
124964562Sgshapiro			if (c->cmd_code != CMDEHLO)
125038032Speter			{
125138032Speter				message("250 %s Hello %s, %s",
125238032Speter					MyHostName, CurSmtpClient, q);
125338032Speter				break;
125438032Speter			}
125538032Speter
125638032Speter			message("250-%s Hello %s, %s",
125738032Speter				MyHostName, CurSmtpClient, q);
125838032Speter
125964562Sgshapiro			/* offer ENHSC even for nullserver */
126064562Sgshapiro			if (nullserver != NULL)
126164562Sgshapiro			{
126264562Sgshapiro				message("250 ENHANCEDSTATUSCODES");
126364562Sgshapiro				break;
126464562Sgshapiro			}
126564562Sgshapiro
126666494Sgshapiro			/*
126766494Sgshapiro			**  print EHLO features list
126866494Sgshapiro			**
126966494Sgshapiro			**  Note: If you change this list,
127066494Sgshapiro			**        remember to update 'helpfile'
127166494Sgshapiro			*/
127266494Sgshapiro
127366494Sgshapiro
127464562Sgshapiro			message("250-ENHANCEDSTATUSCODES");
127538032Speter			if (!bitset(PRIV_NOEXPN, PrivacyFlags))
127638032Speter			{
127738032Speter				message("250-EXPN");
127838032Speter				if (!bitset(PRIV_NOVERB, PrivacyFlags))
127938032Speter					message("250-VERB");
128038032Speter			}
128164562Sgshapiro# if MIME8TO7
128238032Speter			message("250-8BITMIME");
128364562Sgshapiro# endif /* MIME8TO7 */
128438032Speter			if (MaxMessageSize > 0)
128538032Speter				message("250-SIZE %ld", MaxMessageSize);
128638032Speter			else
128738032Speter				message("250-SIZE");
128864562Sgshapiro# if DSN
128964562Sgshapiro			if (SendMIMEErrors &&
129064562Sgshapiro			    !bitset(PRIV_NORECEIPTS, PrivacyFlags))
129138032Speter				message("250-DSN");
129264562Sgshapiro# endif /* DSN */
129338032Speter			message("250-ONEX");
129464562Sgshapiro			if (!bitset(PRIV_NOETRN, PrivacyFlags) &&
129564562Sgshapiro			    !bitnset(D_NOETRN, d_flags))
129638032Speter				message("250-ETRN");
129738032Speter			message("250-XUSR");
129864562Sgshapiro
129964562Sgshapiro# if SASL
130064562Sgshapiro			if (sasl_ok && mechlist != NULL && *mechlist != '\0')
130164562Sgshapiro				message("250-AUTH %s", mechlist);
130264562Sgshapiro# endif /* SASL */
130364562Sgshapiro# if STARTTLS
130464562Sgshapiro			if (tls_ok && usetls)
130564562Sgshapiro				message("250-STARTTLS");
130664562Sgshapiro# endif /* STARTTLS */
130738032Speter			message("250 HELP");
130838032Speter			break;
130938032Speter
131038032Speter		  case CMDMAIL:		/* mail -- designate sender */
131138032Speter			SmtpPhase = "server MAIL";
131238032Speter
131338032Speter			/* check for validity of this command */
131438032Speter			if (!gothello && bitset(PRIV_NEEDMAILHELO, PrivacyFlags))
131538032Speter			{
131664562Sgshapiro				usrerr("503 5.0.0 Polite people say HELO first");
131738032Speter				break;
131838032Speter			}
131938032Speter			if (gotmail)
132038032Speter			{
132164562Sgshapiro				usrerr("503 5.5.0 Sender already specified");
132238032Speter				break;
132338032Speter			}
132438032Speter			if (InChild)
132538032Speter			{
132638032Speter				errno = 0;
132764562Sgshapiro				syserr("503 5.5.0 Nested MAIL command: MAIL %s", p);
132842575Speter				finis(TRUE, ExitStat);
132938032Speter			}
133064562Sgshapiro# if SASL
133164562Sgshapiro			if (bitnset(D_AUTHREQ, d_flags) &&
133264562Sgshapiro			    authenticating != SASL_IS_AUTH)
133364562Sgshapiro			{
133464562Sgshapiro				usrerr("530 5.7.0 Authentication required");
133564562Sgshapiro				break;
133664562Sgshapiro			}
133764562Sgshapiro# endif /* SASL */
133838032Speter
133964562Sgshapiro			p = skipword(p, "from");
134064562Sgshapiro			if (p == NULL)
134164562Sgshapiro				break;
134264562Sgshapiro			if (tempfail)
134364562Sgshapiro			{
134464562Sgshapiro				if (LogLevel > 9)
134564562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
134664562Sgshapiro						  "SMTP MAIL command (%.100s) from %.100s tempfailed (due to previous checks)",
134764562Sgshapiro						  p, CurSmtpClient);
134864562Sgshapiro				usrerr("451 4.7.1 Please try again later");
134964562Sgshapiro				break;
135064562Sgshapiro			}
135164562Sgshapiro
135238032Speter			/* make sure we know who the sending host is */
135338032Speter			if (sendinghost == NULL)
135438032Speter				sendinghost = peerhostname;
135538032Speter
135638032Speter
135738032Speter			/* fork a subprocess to process this command */
135864562Sgshapiro			ric = runinchild("SMTP-MAIL", e);
135964562Sgshapiro
136064562Sgshapiro			/* Catch a problem and stop processing */
136164562Sgshapiro			if (ric == RIC_TEMPFAIL && nullserver == NULL)
136264562Sgshapiro				nullserver = "452 4.3.0 Internal software error";
136364562Sgshapiro			if (ric != RIC_INCHILD)
136438032Speter				break;
136564562Sgshapiro
136638032Speter			if (Errors > 0)
136738032Speter				goto undo_subproc_no_pm;
136838032Speter			if (!gothello)
136938032Speter			{
137038032Speter				auth_warning(e,
137138032Speter					"%s didn't use HELO protocol",
137238032Speter					CurSmtpClient);
137338032Speter			}
137464562Sgshapiro# ifdef PICKY_HELO_CHECK
137538032Speter			if (strcasecmp(sendinghost, peerhostname) != 0 &&
137638032Speter			    (strcasecmp(peerhostname, "localhost") != 0 ||
137738032Speter			     strcasecmp(sendinghost, MyHostName) != 0))
137838032Speter			{
137938032Speter				auth_warning(e, "Host %s claimed to be %s",
138038032Speter					CurSmtpClient, sendinghost);
138138032Speter			}
138264562Sgshapiro# endif /* PICKY_HELO_CHECK */
138338032Speter
138438032Speter			if (protocol == NULL)
138538032Speter				protocol = "SMTP";
138638032Speter			define('r', protocol, e);
138738032Speter			define('s', sendinghost, e);
138864562Sgshapiro
138938032Speter			if (Errors > 0)
139038032Speter				goto undo_subproc_no_pm;
139138032Speter			nrcpts = 0;
139264562Sgshapiro			define(macid("{ntries}", NULL), "0", e);
139364562Sgshapiro			e->e_flags |= EF_CLRQUEUE;
139464562Sgshapiro			sm_setproctitle(TRUE, e, "%s %s: %.80s",
139564562Sgshapiro					qid_printname(e),
139664562Sgshapiro					CurSmtpClient, inp);
139738032Speter
139838032Speter			/* child -- go do the processing */
139938032Speter			if (setjmp(TopFrame) > 0)
140038032Speter			{
140138032Speter				/* this failed -- undo work */
140238032Speter undo_subproc_no_pm:
140338032Speter				e->e_flags &= ~EF_PM_NOTIFY;
140438032Speter undo_subproc:
140538032Speter				if (InChild)
140638032Speter				{
140738032Speter					QuickAbort = FALSE;
140838032Speter					SuprErrs = TRUE;
140938032Speter					e->e_flags &= ~EF_FATALERRS;
141064562Sgshapiro
141164562Sgshapiro					if (LogLevel > 4 &&
141264562Sgshapiro					    bitset(EF_LOGSENDER, e->e_flags))
141364562Sgshapiro						logsender(e, NULL);
141464562Sgshapiro					e->e_flags &= ~EF_LOGSENDER;
141564562Sgshapiro
141642575Speter					finis(TRUE, ExitStat);
141738032Speter				}
141838032Speter				break;
141938032Speter			}
142038032Speter			QuickAbort = TRUE;
142138032Speter
142238032Speter			/* must parse sender first */
142338032Speter			delimptr = NULL;
142438032Speter			setsender(p, e, &delimptr, ' ', FALSE);
142538032Speter			if (delimptr != NULL && *delimptr != '\0')
142638032Speter				*delimptr++ = '\0';
142738032Speter			if (Errors > 0)
142838032Speter				goto undo_subproc_no_pm;
142938032Speter
143064562Sgshapiro			/* Successfully set e_from, allow logging */
143164562Sgshapiro			e->e_flags |= EF_LOGSENDER;
143238032Speter
143364562Sgshapiro			/* put resulting triple from parseaddr() into macros */
143464562Sgshapiro			if (e->e_from.q_mailer != NULL)
143564562Sgshapiro				 define(macid("{mail_mailer}", NULL),
143664562Sgshapiro					e->e_from.q_mailer->m_name, e);
143764562Sgshapiro			else
143864562Sgshapiro				 define(macid("{mail_mailer}", NULL),
143964562Sgshapiro					NULL, e);
144064562Sgshapiro			if (e->e_from.q_host != NULL)
144164562Sgshapiro				define(macid("{mail_host}", NULL),
144264562Sgshapiro				       e->e_from.q_host, e);
144364562Sgshapiro			else
144464562Sgshapiro				define(macid("{mail_host}", NULL),
144564562Sgshapiro				       "localhost", e);
144664562Sgshapiro			if (e->e_from.q_user != NULL)
144764562Sgshapiro				define(macid("{mail_addr}", NULL),
144864562Sgshapiro				       e->e_from.q_user, e);
144964562Sgshapiro			else
145064562Sgshapiro				define(macid("{mail_addr}", NULL),
145164562Sgshapiro				       NULL, e);
145264562Sgshapiro			if (Errors > 0)
145364562Sgshapiro			  goto undo_subproc_no_pm;
145464562Sgshapiro
145538032Speter			/* check for possible spoofing */
145638032Speter			if (RealUid != 0 && OpMode == MD_SMTP &&
145738032Speter			    !wordinclass(RealUserName, 't') &&
145864562Sgshapiro			    (!bitnset(M_LOCALMAILER,
145964562Sgshapiro				      e->e_from.q_mailer->m_flags) ||
146064562Sgshapiro			     strcmp(e->e_from.q_user, RealUserName) != 0))
146138032Speter			{
146238032Speter				auth_warning(e, "%s owned process doing -bs",
146338032Speter					RealUserName);
146438032Speter			}
146538032Speter
146638032Speter			/* now parse ESMTP arguments */
146738032Speter			e->e_msgsize = 0;
146864562Sgshapiro			addr = p;
146964562Sgshapiro			argno = 0;
147064562Sgshapiro			args[argno++] = p;
147138032Speter			p = delimptr;
147238032Speter			while (p != NULL && *p != '\0')
147338032Speter			{
147438032Speter				char *kp;
147538032Speter				char *vp = NULL;
147664562Sgshapiro				char *equal = NULL;
147738032Speter
147838032Speter				/* locate the beginning of the keyword */
147938032Speter				while (isascii(*p) && isspace(*p))
148038032Speter					p++;
148138032Speter				if (*p == '\0')
148238032Speter					break;
148338032Speter				kp = p;
148438032Speter
148538032Speter				/* skip to the value portion */
148638032Speter				while ((isascii(*p) && isalnum(*p)) || *p == '-')
148738032Speter					p++;
148838032Speter				if (*p == '=')
148938032Speter				{
149064562Sgshapiro					equal = p;
149138032Speter					*p++ = '\0';
149238032Speter					vp = p;
149338032Speter
149438032Speter					/* skip to the end of the value */
149538032Speter					while (*p != '\0' && *p != ' ' &&
149638032Speter					       !(isascii(*p) && iscntrl(*p)) &&
149738032Speter					       *p != '=')
149838032Speter						p++;
149938032Speter				}
150038032Speter
150138032Speter				if (*p != '\0')
150238032Speter					*p++ = '\0';
150338032Speter
150438032Speter				if (tTd(19, 1))
150564562Sgshapiro					dprintf("MAIL: got arg %s=\"%s\"\n", kp,
150638032Speter						vp == NULL ? "<null>" : vp);
150738032Speter
150838032Speter				mail_esmtp_args(kp, vp, e);
150964562Sgshapiro				if (equal != NULL)
151064562Sgshapiro					*equal = '=';
151164562Sgshapiro				args[argno++] = kp;
151264562Sgshapiro				if (argno >= MAXSMTPARGS - 1)
151364562Sgshapiro					usrerr("501 5.5.4 Too many parameters");
151438032Speter				if (Errors > 0)
151538032Speter					goto undo_subproc_no_pm;
151638032Speter			}
151764562Sgshapiro			args[argno] = NULL;
151838032Speter			if (Errors > 0)
151938032Speter				goto undo_subproc_no_pm;
152038032Speter
152164562Sgshapiro			/* do config file checking of the sender */
152264562Sgshapiro			if (rscheck("check_mail", addr,
152364562Sgshapiro				    NULL, e, TRUE, TRUE, 4) != EX_OK ||
152464562Sgshapiro			    Errors > 0)
152564562Sgshapiro				goto undo_subproc_no_pm;
152664562Sgshapiro
152766494Sgshapiro			if (MaxMessageSize > 0 &&
152866494Sgshapiro			   (e->e_msgsize > MaxMessageSize || e->e_msgsize < 0))
152938032Speter			{
153064562Sgshapiro				usrerr("552 5.2.3 Message size exceeds fixed maximum message size (%ld)",
153138032Speter					MaxMessageSize);
153238032Speter				goto undo_subproc_no_pm;
153338032Speter			}
153464562Sgshapiro
153564562Sgshapiro			if (!enoughdiskspace(e->e_msgsize, TRUE))
153638032Speter			{
153764562Sgshapiro				usrerr("452 4.4.5 Insufficient disk space; try again later");
153838032Speter				goto undo_subproc_no_pm;
153938032Speter			}
154038032Speter			if (Errors > 0)
154138032Speter				goto undo_subproc_no_pm;
154264562Sgshapiro
154364562Sgshapiro# if _FFR_MILTER
154464562Sgshapiro			LogUsrErrs = TRUE;
154564562Sgshapiro			if (milterize && !bitset(EF_DISCARD, e->e_flags))
154664562Sgshapiro			{
154764562Sgshapiro				char state;
154864562Sgshapiro				char *response;
154964562Sgshapiro
155064562Sgshapiro				response = milter_envfrom(args, e, &state);
155164562Sgshapiro				switch (state)
155264562Sgshapiro				{
155364562Sgshapiro				  case SMFIR_REPLYCODE:
155464562Sgshapiro					usrerr(response);
155564562Sgshapiro					break;
155664562Sgshapiro
155764562Sgshapiro				  case SMFIR_REJECT:
155864562Sgshapiro					usrerr("550 5.7.1 Command rejected");
155964562Sgshapiro					break;
156064562Sgshapiro
156164562Sgshapiro				  case SMFIR_DISCARD:
156264562Sgshapiro					e->e_flags |= EF_DISCARD;
156364562Sgshapiro					break;
156464562Sgshapiro
156564562Sgshapiro				  case SMFIR_TEMPFAIL:
156666494Sgshapiro					usrerr("451 4.7.1 Please try again later");
156764562Sgshapiro					break;
156864562Sgshapiro				}
156964562Sgshapiro				if (response != NULL)
157064562Sgshapiro					free(response);
157164562Sgshapiro			}
157264562Sgshapiro# endif /* _FFR_MILTER */
157364562Sgshapiro			if (Errors > 0)
157464562Sgshapiro				goto undo_subproc_no_pm;
157564562Sgshapiro
157664562Sgshapiro			message("250 2.1.0 Sender ok");
157738032Speter			gotmail = TRUE;
157838032Speter			break;
157938032Speter
158038032Speter		  case CMDRCPT:		/* rcpt -- designate recipient */
158138032Speter			if (!gotmail)
158238032Speter			{
158364562Sgshapiro				usrerr("503 5.0.0 Need MAIL before RCPT");
158438032Speter				break;
158538032Speter			}
158638032Speter			SmtpPhase = "server RCPT";
158738032Speter			if (setjmp(TopFrame) > 0)
158838032Speter			{
158964562Sgshapiro				e->e_flags &= ~(EF_FATALERRS|EF_PM_NOTIFY);
159038032Speter				break;
159138032Speter			}
159238032Speter			QuickAbort = TRUE;
159338032Speter			LogUsrErrs = TRUE;
159438032Speter
159538032Speter			/* limit flooding of our machine */
159638032Speter			if (MaxRcptPerMsg > 0 && nrcpts >= MaxRcptPerMsg)
159738032Speter			{
159864562Sgshapiro				usrerr("452 4.5.3 Too many recipients");
159938032Speter				break;
160038032Speter			}
160138032Speter
160238032Speter			if (e->e_sendmode != SM_DELIVER)
160338032Speter				e->e_flags |= EF_VRFYONLY;
160438032Speter
160564562Sgshapiro# if _FFR_MILTER
160664562Sgshapiro			/*
160764562Sgshapiro			**  If the filter will be deleting recipients,
160864562Sgshapiro			**  don't expand them at RCPT time (in the call
160964562Sgshapiro			**  to recipient()).  If they are expanded, it
161064562Sgshapiro			**  is impossible for removefromlist() to figure
161164562Sgshapiro			**  out the expanded members of the original
161264562Sgshapiro			**  recipient and mark them as QS_DONTSEND.
161364562Sgshapiro			*/
161464562Sgshapiro
161564562Sgshapiro			if (milter_can_delrcpts())
161664562Sgshapiro				e->e_flags |= EF_VRFYONLY;
161764562Sgshapiro# endif /* _FFR_MILTER */
161864562Sgshapiro
161938032Speter			p = skipword(p, "to");
162038032Speter			if (p == NULL)
162138032Speter				break;
162264562Sgshapiro# if _FFR_ADDR_TYPE
162364562Sgshapiro			define(macid("{addr_type}", NULL), "e r", e);
162464562Sgshapiro# endif /* _FFR_ADDR_TYPE */
162538032Speter			a = parseaddr(p, NULLADDR, RF_COPYALL, ' ', &delimptr, e);
162664562Sgshapiro#if _FFR_ADDR_TYPE
162764562Sgshapiro			define(macid("{addr_type}", NULL), NULL, e);
162864562Sgshapiro#endif /* _FFR_ADDR_TYPE */
162942575Speter			if (Errors > 0)
163038032Speter				break;
163142575Speter			if (a == NULL)
163242575Speter			{
163364562Sgshapiro				usrerr("501 5.0.0 Missing recipient");
163442575Speter				break;
163542575Speter			}
163642575Speter
163738032Speter			if (delimptr != NULL && *delimptr != '\0')
163838032Speter				*delimptr++ = '\0';
163938032Speter
164064562Sgshapiro			/* put resulting triple from parseaddr() into macros */
164164562Sgshapiro			if (a->q_mailer != NULL)
164264562Sgshapiro				define(macid("{rcpt_mailer}", NULL),
164364562Sgshapiro				       a->q_mailer->m_name, e);
164464562Sgshapiro			else
164564562Sgshapiro				define(macid("{rcpt_mailer}", NULL),
164664562Sgshapiro				       NULL, e);
164764562Sgshapiro			if (a->q_host != NULL)
164864562Sgshapiro				define(macid("{rcpt_host}", NULL),
164964562Sgshapiro				       a->q_host, e);
165064562Sgshapiro			else
165164562Sgshapiro				define(macid("{rcpt_host}", NULL),
165264562Sgshapiro				       "localhost", e);
165364562Sgshapiro			if (a->q_user != NULL)
165464562Sgshapiro				define(macid("{rcpt_addr}", NULL),
165564562Sgshapiro				       a->q_user, e);
165664562Sgshapiro			else
165764562Sgshapiro				define(macid("{rcpt_addr}", NULL),
165864562Sgshapiro				       NULL, e);
165964562Sgshapiro			if (Errors > 0)
166038032Speter				break;
166138032Speter
166238032Speter			/* now parse ESMTP arguments */
166364562Sgshapiro			addr = p;
166464562Sgshapiro			argno = 0;
166564562Sgshapiro			args[argno++] = p;
166638032Speter			p = delimptr;
166738032Speter			while (p != NULL && *p != '\0')
166838032Speter			{
166938032Speter				char *kp;
167038032Speter				char *vp = NULL;
167164562Sgshapiro				char *equal = NULL;
167238032Speter
167338032Speter				/* locate the beginning of the keyword */
167438032Speter				while (isascii(*p) && isspace(*p))
167538032Speter					p++;
167638032Speter				if (*p == '\0')
167738032Speter					break;
167838032Speter				kp = p;
167938032Speter
168038032Speter				/* skip to the value portion */
168138032Speter				while ((isascii(*p) && isalnum(*p)) || *p == '-')
168238032Speter					p++;
168338032Speter				if (*p == '=')
168438032Speter				{
168564562Sgshapiro					equal = p;
168638032Speter					*p++ = '\0';
168738032Speter					vp = p;
168838032Speter
168938032Speter					/* skip to the end of the value */
169038032Speter					while (*p != '\0' && *p != ' ' &&
169138032Speter					       !(isascii(*p) && iscntrl(*p)) &&
169238032Speter					       *p != '=')
169338032Speter						p++;
169438032Speter				}
169538032Speter
169638032Speter				if (*p != '\0')
169738032Speter					*p++ = '\0';
169838032Speter
169938032Speter				if (tTd(19, 1))
170064562Sgshapiro					dprintf("RCPT: got arg %s=\"%s\"\n", kp,
170138032Speter						vp == NULL ? "<null>" : vp);
170238032Speter
170338032Speter				rcpt_esmtp_args(a, kp, vp, e);
170464562Sgshapiro				if (equal != NULL)
170564562Sgshapiro					*equal = '=';
170664562Sgshapiro				args[argno++] = kp;
170764562Sgshapiro				if (argno >= MAXSMTPARGS - 1)
170864562Sgshapiro					usrerr("501 5.5.4 Too many parameters");
170938032Speter				if (Errors > 0)
171038032Speter					break;
171138032Speter			}
171264562Sgshapiro			args[argno] = NULL;
171338032Speter			if (Errors > 0)
171438032Speter				break;
171538032Speter
171664562Sgshapiro			/* do config file checking of the recipient */
171764562Sgshapiro			if (rscheck("check_rcpt", addr,
171864562Sgshapiro				    NULL, e, TRUE, TRUE, 4) != EX_OK ||
171964562Sgshapiro			    Errors > 0)
172064562Sgshapiro				break;
172164562Sgshapiro
172264562Sgshapiro# if _FFR_MILTER
172364562Sgshapiro			if (milterize && !bitset(EF_DISCARD, e->e_flags))
172464562Sgshapiro			{
172564562Sgshapiro				char state;
172664562Sgshapiro				char *response;
172764562Sgshapiro
172864562Sgshapiro				response = milter_envrcpt(args, e, &state);
172964562Sgshapiro				switch (state)
173064562Sgshapiro				{
173164562Sgshapiro				  case SMFIR_REPLYCODE:
173264562Sgshapiro					usrerr(response);
173364562Sgshapiro					break;
173464562Sgshapiro
173564562Sgshapiro				  case SMFIR_REJECT:
173664562Sgshapiro					usrerr("550 5.7.1 Command rejected");
173764562Sgshapiro					break;
173864562Sgshapiro
173964562Sgshapiro				  case SMFIR_DISCARD:
174064562Sgshapiro					e->e_flags |= EF_DISCARD;
174164562Sgshapiro					break;
174264562Sgshapiro
174364562Sgshapiro				  case SMFIR_TEMPFAIL:
174466494Sgshapiro					usrerr("451 4.7.1 Please try again later");
174564562Sgshapiro					break;
174664562Sgshapiro				}
174764562Sgshapiro				if (response != NULL)
174864562Sgshapiro					free(response);
174964562Sgshapiro			}
175064562Sgshapiro# endif /* _FFR_MILTER */
175164562Sgshapiro
175264562Sgshapiro			define(macid("{rcpt_mailer}", NULL), NULL, e);
175364562Sgshapiro			define(macid("{rcpt_relay}", NULL), NULL, e);
175464562Sgshapiro			define(macid("{rcpt_addr}", NULL), NULL, e);
175564562Sgshapiro			define(macid("{dsn_notify}", NULL), NULL, e);
175664562Sgshapiro			if (Errors > 0)
175764562Sgshapiro				break;
175864562Sgshapiro
175938032Speter			/* save in recipient list after ESMTP mods */
176038032Speter			a = recipient(a, &e->e_sendqueue, 0, e);
176138032Speter			if (Errors > 0)
176238032Speter				break;
176338032Speter
176438032Speter			/* no errors during parsing, but might be a duplicate */
176538032Speter			e->e_to = a->q_paddr;
176664562Sgshapiro			if (!QS_IS_BADADDR(a->q_state))
176738032Speter			{
176864562Sgshapiro				if (e->e_queuedir == NOQDIR)
176964562Sgshapiro					initsys(e);
177064562Sgshapiro				message("250 2.1.5 Recipient ok%s",
177164562Sgshapiro					QS_IS_QUEUEUP(a->q_state) ?
177238032Speter						" (will queue)" : "");
177338032Speter				nrcpts++;
177438032Speter			}
177538032Speter			else
177638032Speter			{
177738032Speter				/* punt -- should keep message in ADDRESS.... */
177864562Sgshapiro				usrerr("550 5.1.1 Addressee unknown");
177938032Speter			}
178038032Speter			break;
178138032Speter
178238032Speter		  case CMDDATA:		/* data -- text of mail */
178338032Speter			SmtpPhase = "server DATA";
178438032Speter			if (!gotmail)
178538032Speter			{
178664562Sgshapiro				usrerr("503 5.0.0 Need MAIL command");
178738032Speter				break;
178838032Speter			}
178938032Speter			else if (nrcpts <= 0)
179038032Speter			{
179164562Sgshapiro				usrerr("503 5.0.0 Need RCPT (recipient)");
179238032Speter				break;
179338032Speter			}
179438032Speter
179538032Speter			/* put back discard bit */
179638032Speter			if (discard)
179738032Speter				e->e_flags |= EF_DISCARD;
179838032Speter
179938032Speter			/* check to see if we need to re-expand aliases */
180064562Sgshapiro			/* also reset QS_BADADDR on already-diagnosted addrs */
180138032Speter			doublequeue = FALSE;
180238032Speter			for (a = e->e_sendqueue; a != NULL; a = a->q_next)
180338032Speter			{
180464562Sgshapiro				if (QS_IS_VERIFIED(a->q_state) &&
180538032Speter				    !bitset(EF_DISCARD, e->e_flags))
180638032Speter				{
180738032Speter					/* need to re-expand aliases */
180838032Speter					doublequeue = TRUE;
180938032Speter				}
181064562Sgshapiro				if (QS_IS_BADADDR(a->q_state))
181138032Speter				{
181238032Speter					/* make this "go away" */
181364562Sgshapiro					a->q_state = QS_DONTSEND;
181438032Speter				}
181538032Speter			}
181638032Speter
181738032Speter			/* collect the text of the message */
181838032Speter			SmtpPhase = "collect";
181938032Speter			buffer_errors();
182038032Speter			collect(InChannel, TRUE, NULL, e);
182164562Sgshapiro
182264562Sgshapiro# if _FFR_MILTER
182364562Sgshapiro			if (milterize &&
182464562Sgshapiro			    Errors <= 0 &&
182564562Sgshapiro			    !bitset(EF_DISCARD, e->e_flags))
182664562Sgshapiro			{
182764562Sgshapiro				char state;
182864562Sgshapiro				char *response;
182964562Sgshapiro
183064562Sgshapiro				response = milter_data(e, &state);
183164562Sgshapiro				switch (state)
183264562Sgshapiro				{
183364562Sgshapiro				  case SMFIR_REPLYCODE:
183464562Sgshapiro					usrerr(response);
183564562Sgshapiro					break;
183664562Sgshapiro
183764562Sgshapiro				  case SMFIR_REJECT:
183864562Sgshapiro					usrerr("554 5.7.1 Command rejected");
183964562Sgshapiro					break;
184064562Sgshapiro
184164562Sgshapiro				  case SMFIR_DISCARD:
184264562Sgshapiro					e->e_flags |= EF_DISCARD;
184364562Sgshapiro					break;
184464562Sgshapiro
184564562Sgshapiro				  case SMFIR_TEMPFAIL:
184666494Sgshapiro					usrerr("451 4.7.1 Please try again later");
184764562Sgshapiro					break;
184864562Sgshapiro				}
184964562Sgshapiro				if (response != NULL)
185064562Sgshapiro					free(response);
185164562Sgshapiro			}
185264562Sgshapiro
185364562Sgshapiro			/* abort message filters that didn't get the body */
185464562Sgshapiro			if (milterize)
185564562Sgshapiro				milter_abort(e);
185664562Sgshapiro# endif /* _FFR_MILTER */
185764562Sgshapiro
185864562Sgshapiro			/* redefine message size */
185964562Sgshapiro			if ((q = macvalue(macid("{msg_size}", NULL), e))
186064562Sgshapiro			    != NULL)
186164562Sgshapiro				free(q);
186264562Sgshapiro			snprintf(inp, sizeof inp, "%ld", e->e_msgsize);
186364562Sgshapiro			define(macid("{msg_size}", NULL), newstr(inp), e);
186438032Speter			if (Errors > 0)
186538032Speter			{
186664562Sgshapiro				/* Log who the mail would have gone to */
186764562Sgshapiro				if (LogLevel > 8 &&
186864562Sgshapiro				    e->e_message != NULL)
186964562Sgshapiro				{
187064562Sgshapiro					for (a = e->e_sendqueue;
187164562Sgshapiro					     a != NULL;
187264562Sgshapiro					     a = a->q_next)
187364562Sgshapiro					{
187464562Sgshapiro						if (!QS_IS_UNDELIVERED(a->q_state))
187564562Sgshapiro							continue;
187664562Sgshapiro
187764562Sgshapiro						e->e_to = a->q_paddr;
187864562Sgshapiro						logdelivery(NULL, NULL,
187964562Sgshapiro							    a->q_status,
188064562Sgshapiro							    e->e_message,
188164562Sgshapiro							    NULL,
188264562Sgshapiro							    (time_t) 0, e);
188364562Sgshapiro					}
188464562Sgshapiro					e->e_to = NULL;
188564562Sgshapiro				}
188638032Speter				flush_errors(TRUE);
188738032Speter				buffer_errors();
188838032Speter				goto abortmessage;
188938032Speter			}
189038032Speter
189138032Speter			/* make sure we actually do delivery */
189238032Speter			e->e_flags &= ~EF_CLRQUEUE;
189338032Speter
189438032Speter			/* from now on, we have to operate silently */
189538032Speter			buffer_errors();
189638032Speter			e->e_errormode = EM_MAIL;
189738032Speter
189838032Speter			/*
189938032Speter			**  Arrange to send to everyone.
190038032Speter			**	If sending to multiple people, mail back
190138032Speter			**		errors rather than reporting directly.
190238032Speter			**	In any case, don't mail back errors for
190338032Speter			**		anything that has happened up to
190438032Speter			**		now (the other end will do this).
190538032Speter			**	Truncate our transcript -- the mail has gotten
190638032Speter			**		to us successfully, and if we have
190738032Speter			**		to mail this back, it will be easier
190838032Speter			**		on the reader.
190938032Speter			**	Then send to everyone.
191038032Speter			**	Finally give a reply code.  If an error has
191138032Speter			**		already been given, don't mail a
191238032Speter			**		message back.
191338032Speter			**	We goose error returns by clearing error bit.
191438032Speter			*/
191538032Speter
191638032Speter			SmtpPhase = "delivery";
191764562Sgshapiro			(void) bftruncate(e->e_xfp);
191838032Speter			id = e->e_id;
191938032Speter
192064562Sgshapiro			/*
192164562Sgshapiro			**  If a header/body check (header checks or milter)
192264562Sgshapiro			**  set EF_DISCARD, don't queueup the message --
192364562Sgshapiro			**  that would lose the EF_DISCARD bit and deliver
192464562Sgshapiro			**  the message.
192564562Sgshapiro			*/
192664562Sgshapiro
192764562Sgshapiro			if (bitset(EF_DISCARD, e->e_flags))
192864562Sgshapiro				doublequeue = FALSE;
192964562Sgshapiro
193038032Speter			if (doublequeue)
193138032Speter			{
193238032Speter				/* make sure it is in the queue */
193338032Speter				queueup(e, FALSE);
193438032Speter			}
193538032Speter			else
193638032Speter			{
193738032Speter				/* send to all recipients */
193864562Sgshapiro# if NAMED_BIND
193964562Sgshapiro				_res.retry = TimeOuts.res_retry[RES_TO_FIRST];
194064562Sgshapiro				_res.retrans = TimeOuts.res_retrans[RES_TO_FIRST];
194164562Sgshapiro# endif /* NAMED_BIND */
194238032Speter				sendall(e, SM_DEFAULT);
194338032Speter			}
194438032Speter			e->e_to = NULL;
194538032Speter
194638032Speter			/* issue success message */
194764562Sgshapiro			message("250 2.0.0 %s Message accepted for delivery", id);
194838032Speter
194938032Speter			/* if we just queued, poke it */
195038032Speter			if (doublequeue &&
195138032Speter			    e->e_sendmode != SM_QUEUE &&
195238032Speter			    e->e_sendmode != SM_DEFER)
195338032Speter			{
195464562Sgshapiro				CurrentLA = sm_getla(e);
195538032Speter
195638032Speter				if (!shouldqueue(e->e_msgpriority, e->e_ctime))
195738032Speter				{
195864562Sgshapiro					/* close all the queue files */
195964562Sgshapiro					closexscript(e);
196064562Sgshapiro					if (e->e_dfp != NULL)
196164562Sgshapiro						(void) bfclose(e->e_dfp);
196264562Sgshapiro					e->e_dfp = NULL;
196338032Speter					unlockqueue(e);
196464562Sgshapiro
196564562Sgshapiro					(void) dowork(e->e_queuedir, id,
196664562Sgshapiro						      TRUE, TRUE, e);
196738032Speter				}
196838032Speter			}
196938032Speter
197038032Speter  abortmessage:
197164562Sgshapiro			if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
197264562Sgshapiro				logsender(e, NULL);
197364562Sgshapiro			e->e_flags &= ~EF_LOGSENDER;
197464562Sgshapiro
197538032Speter			/* if in a child, pop back to our parent */
197638032Speter			if (InChild)
197742575Speter				finis(TRUE, ExitStat);
197838032Speter
197938032Speter			/* clean up a bit */
198038032Speter			gotmail = FALSE;
198138032Speter			dropenvelope(e, TRUE);
198238032Speter			CurEnv = e = newenvelope(e, CurEnv);
198338032Speter			e->e_flags = BlankEnvelope.e_flags;
198438032Speter			break;
198538032Speter
198638032Speter		  case CMDRSET:		/* rset -- reset state */
198764562Sgshapiro# if _FFR_MILTER
198864562Sgshapiro			/* abort milter filters */
198964562Sgshapiro			milter_abort(e);
199064562Sgshapiro# endif /* _FFR_MILTER */
199164562Sgshapiro
199238032Speter			if (tTd(94, 100))
199364562Sgshapiro				message("451 4.0.0 Test failure");
199438032Speter			else
199564562Sgshapiro				message("250 2.0.0 Reset state");
199638032Speter
199738032Speter			/* arrange to ignore any current send list */
199838032Speter			e->e_sendqueue = NULL;
199938032Speter			e->e_flags |= EF_CLRQUEUE;
200064562Sgshapiro
200164562Sgshapiro			if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
200264562Sgshapiro				logsender(e, NULL);
200364562Sgshapiro			e->e_flags &= ~EF_LOGSENDER;
200464562Sgshapiro
200538032Speter			if (InChild)
200642575Speter				finis(TRUE, ExitStat);
200738032Speter
200838032Speter			/* clean up a bit */
200938032Speter			gotmail = FALSE;
201038032Speter			SuprErrs = TRUE;
201138032Speter			dropenvelope(e, TRUE);
201238032Speter			CurEnv = e = newenvelope(e, CurEnv);
201338032Speter			break;
201438032Speter
201538032Speter		  case CMDVRFY:		/* vrfy -- verify address */
201638032Speter		  case CMDEXPN:		/* expn -- expand address */
201764562Sgshapiro			if (tempfail)
201864562Sgshapiro			{
201964562Sgshapiro				if (LogLevel > 9)
202064562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
202164562Sgshapiro						  "SMTP %s command (%.100s) from %.100s tempfailed (due to previous checks)",
202264562Sgshapiro						  c->cmd_code == CMDVRFY ? "VRFY" : "EXPN",
202364562Sgshapiro						  p, CurSmtpClient);
202464562Sgshapiro				usrerr("550 5.7.1 Please try again later");
202564562Sgshapiro				break;
202664562Sgshapiro			}
202764562Sgshapiro			wt = checksmtpattack(&nverifies, MAXVRFYCOMMANDS, FALSE,
202864562Sgshapiro				c->cmd_code == CMDVRFY ? "VRFY" : "EXPN", e);
202964562Sgshapiro			previous = curtime();
203064562Sgshapiro			vrfy = c->cmd_code == CMDVRFY;
203138032Speter			if (bitset(vrfy ? PRIV_NOVRFY : PRIV_NOEXPN,
203238032Speter						PrivacyFlags))
203338032Speter			{
203438032Speter				if (vrfy)
203564562Sgshapiro					message("252 2.5.2 Cannot VRFY user; try RCPT to attempt delivery (or try finger)");
203638032Speter				else
203764562Sgshapiro					message("502 5.7.0 Sorry, we do not allow this operation");
203838032Speter				if (LogLevel > 5)
203938032Speter					sm_syslog(LOG_INFO, e->e_id,
204064562Sgshapiro						  "%.100s: %s [rejected]",
204164562Sgshapiro						  CurSmtpClient,
204264562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
204338032Speter				break;
204438032Speter			}
204538032Speter			else if (!gothello &&
204638032Speter				 bitset(vrfy ? PRIV_NEEDVRFYHELO : PRIV_NEEDEXPNHELO,
204738032Speter						PrivacyFlags))
204838032Speter			{
204964562Sgshapiro				usrerr("503 5.0.0 I demand that you introduce yourself first");
205038032Speter				break;
205138032Speter			}
205238032Speter			if (runinchild(vrfy ? "SMTP-VRFY" : "SMTP-EXPN", e) > 0)
205338032Speter				break;
205438032Speter			if (Errors > 0)
205538032Speter				goto undo_subproc;
205638032Speter			if (LogLevel > 5)
205738032Speter				sm_syslog(LOG_INFO, e->e_id,
205864562Sgshapiro					  "%.100s: %s",
205964562Sgshapiro					  CurSmtpClient,
206064562Sgshapiro					  shortenstring(inp, MAXSHORTSTR));
206138032Speter			if (setjmp(TopFrame) > 0)
206238032Speter				goto undo_subproc;
206338032Speter			QuickAbort = TRUE;
206438032Speter			vrfyqueue = NULL;
206538032Speter			if (vrfy)
206638032Speter				e->e_flags |= EF_VRFYONLY;
206738032Speter			while (*p != '\0' && isascii(*p) && isspace(*p))
206838032Speter				p++;
206938032Speter			if (*p == '\0')
207038032Speter			{
207164562Sgshapiro				usrerr("501 5.5.2 Argument required");
207238032Speter			}
207338032Speter			else
207438032Speter			{
207564562Sgshapiro				/* do config file checking of the address */
207664562Sgshapiro				if (rscheck(vrfy ? "check_vrfy" : "check_expn",
207764562Sgshapiro					    p, NULL, e, TRUE, FALSE, 4)
207864562Sgshapiro				    != EX_OK || Errors > 0)
207964562Sgshapiro					goto undo_subproc;
208038032Speter				(void) sendtolist(p, NULLADDR, &vrfyqueue, 0, e);
208138032Speter			}
208264562Sgshapiro			if (wt > 0)
208364562Sgshapiro				(void) sleep(wt - (curtime() - previous));
208438032Speter			if (Errors > 0)
208538032Speter				goto undo_subproc;
208638032Speter			if (vrfyqueue == NULL)
208738032Speter			{
208864562Sgshapiro				usrerr("554 5.5.2 Nothing to %s", vrfy ? "VRFY" : "EXPN");
208938032Speter			}
209038032Speter			while (vrfyqueue != NULL)
209138032Speter			{
209264562Sgshapiro				if (!QS_IS_UNDELIVERED(vrfyqueue->q_state))
209364562Sgshapiro				{
209464562Sgshapiro					vrfyqueue = vrfyqueue->q_next;
209564562Sgshapiro					continue;
209664562Sgshapiro				}
209738032Speter
209864562Sgshapiro				/* see if there is more in the vrfy list */
209938032Speter				a = vrfyqueue;
210038032Speter				while ((a = a->q_next) != NULL &&
210166494Sgshapiro				       (!QS_IS_UNDELIVERED(a->q_state)))
210238032Speter					continue;
210364562Sgshapiro				printvrfyaddr(vrfyqueue, a == NULL, vrfy);
210464562Sgshapiro				vrfyqueue = a;
210538032Speter			}
210638032Speter			if (InChild)
210742575Speter				finis(TRUE, ExitStat);
210838032Speter			break;
210938032Speter
211038032Speter		  case CMDETRN:		/* etrn -- force queue flush */
211164562Sgshapiro			if (bitset(PRIV_NOETRN, PrivacyFlags) ||
211264562Sgshapiro			    bitnset(D_NOETRN, d_flags))
211338032Speter			{
211464562Sgshapiro				/* different message for MSA ? */
211564562Sgshapiro				message("502 5.7.0 Sorry, we do not allow this operation");
211638032Speter				if (LogLevel > 5)
211738032Speter					sm_syslog(LOG_INFO, e->e_id,
211864562Sgshapiro						  "%.100s: %s [rejected]",
211964562Sgshapiro						  CurSmtpClient,
212064562Sgshapiro						  shortenstring(inp, MAXSHORTSTR));
212138032Speter				break;
212238032Speter			}
212364562Sgshapiro			if (tempfail)
212464562Sgshapiro			{
212564562Sgshapiro				if (LogLevel > 9)
212664562Sgshapiro					sm_syslog(LOG_INFO, e->e_id,
212764562Sgshapiro						  "SMTP ETRN command (%.100s) from %.100s tempfailed (due to previous checks)",
212864562Sgshapiro						  p, CurSmtpClient);
212964562Sgshapiro				usrerr("451 4.7.1 Please try again later");
213064562Sgshapiro				break;
213164562Sgshapiro			}
213238032Speter
213338032Speter			if (strlen(p) <= 0)
213438032Speter			{
213564562Sgshapiro				usrerr("500 5.5.2 Parameter required");
213638032Speter				break;
213738032Speter			}
213838032Speter
213938032Speter			/* crude way to avoid denial-of-service attacks */
214064562Sgshapiro			(void) checksmtpattack(&n_etrn, MAXETRNCOMMANDS, TRUE,
214164562Sgshapiro					     "ETRN", e);
214238032Speter
214364562Sgshapiro			/* do config file checking of the parameter */
214464562Sgshapiro			if (rscheck("check_etrn", p, NULL, e, TRUE, FALSE, 4)
214564562Sgshapiro			    != EX_OK || Errors > 0)
214664562Sgshapiro				break;
214764562Sgshapiro
214838032Speter			if (LogLevel > 5)
214938032Speter				sm_syslog(LOG_INFO, e->e_id,
215064562Sgshapiro					  "%.100s: ETRN %s",
215164562Sgshapiro					  CurSmtpClient,
215264562Sgshapiro					  shortenstring(p, MAXSHORTSTR));
215338032Speter
215438032Speter			id = p;
215538032Speter			if (*id == '@')
215638032Speter				id++;
215738032Speter			else
215838032Speter				*--id = '@';
215964562Sgshapiro
216038032Speter			if ((new = (QUEUE_CHAR *)malloc(sizeof(QUEUE_CHAR))) == NULL)
216138032Speter			{
216264562Sgshapiro				syserr("500 5.5.0 ETRN out of memory");
216338032Speter				break;
216438032Speter			}
216538032Speter			new->queue_match = id;
216638032Speter			new->queue_next = NULL;
216738032Speter			QueueLimitRecipient = new;
216864562Sgshapiro			ok = runqueue(TRUE, FALSE);
216938032Speter			free(QueueLimitRecipient);
217038032Speter			QueueLimitRecipient = NULL;
217138032Speter			if (ok && Errors == 0)
217264562Sgshapiro				message("250 2.0.0 Queuing for node %s started", p);
217338032Speter			break;
217438032Speter
217538032Speter		  case CMDHELP:		/* help -- give user info */
217664562Sgshapiro			help(p, e);
217738032Speter			break;
217838032Speter
217938032Speter		  case CMDNOOP:		/* noop -- do nothing */
218064562Sgshapiro			(void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
218164562Sgshapiro					       "NOOP", e);
218264562Sgshapiro			message("250 2.0.0 OK");
218338032Speter			break;
218438032Speter
218538032Speter		  case CMDQUIT:		/* quit -- leave mail */
218664562Sgshapiro			message("221 2.0.0 %s closing connection", MyHostName);
218738032Speter
218838032Speter			/* arrange to ignore any current send list */
218938032Speter			e->e_sendqueue = NULL;
219038032Speter
219164562Sgshapiro# if STARTTLS
219264562Sgshapiro			/* shutdown TLS connection */
219364562Sgshapiro			if (tls_active)
219464562Sgshapiro			{
219564562Sgshapiro				(void) endtls(srv_ssl, "server");
219664562Sgshapiro				tls_active = FALSE;
219764562Sgshapiro			}
219864562Sgshapiro# endif /* STARTTLS */
219964562Sgshapiro# if SASL
220064562Sgshapiro			if (authenticating == SASL_IS_AUTH)
220164562Sgshapiro			{
220264562Sgshapiro				sasl_dispose(&conn);
220364562Sgshapiro				authenticating = SASL_NOT_AUTH;
220464562Sgshapiro			}
220564562Sgshapiro# endif /* SASL */
220664562Sgshapiro
220764562Sgshapirodoquit:
220838032Speter			/* avoid future 050 messages */
220938032Speter			disconnect(1, e);
221038032Speter
221164562Sgshapiro# if _FFR_MILTER
221264562Sgshapiro			/* close out milter filters */
221364562Sgshapiro			milter_quit(e);
221464562Sgshapiro# endif /* _FFR_MILTER */
221564562Sgshapiro
221638032Speter			if (InChild)
221738032Speter				ExitStat = EX_QUIT;
221864562Sgshapiro
221964562Sgshapiro			if (LogLevel > 4 && bitset(EF_LOGSENDER, e->e_flags))
222064562Sgshapiro				logsender(e, NULL);
222164562Sgshapiro			e->e_flags &= ~EF_LOGSENDER;
222264562Sgshapiro
222338032Speter			if (lognullconnection && LogLevel > 5)
222464562Sgshapiro			{
222564562Sgshapiro				char *d;
222664562Sgshapiro
222764562Sgshapiro				d = macvalue(macid("{daemon_name}", NULL), e);
222864562Sgshapiro				if (d == NULL)
222964562Sgshapiro					d = "stdin";
223038032Speter				sm_syslog(LOG_INFO, NULL,
223164562Sgshapiro					 "%.100s did not issue MAIL/EXPN/VRFY/ETRN during connection to %s",
223264562Sgshapiro					  CurSmtpClient, d);
223364562Sgshapiro			}
223442575Speter			finis(TRUE, ExitStat);
223564562Sgshapiro			/* NOTREACHED */
223638032Speter
223738032Speter		  case CMDVERB:		/* set verbose mode */
223838032Speter			if (bitset(PRIV_NOEXPN, PrivacyFlags) ||
223938032Speter			    bitset(PRIV_NOVERB, PrivacyFlags))
224038032Speter			{
224138032Speter				/* this would give out the same info */
224264562Sgshapiro				message("502 5.7.0 Verbose unavailable");
224338032Speter				break;
224438032Speter			}
224564562Sgshapiro			(void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
224664562Sgshapiro					       "VERB", e);
224738032Speter			Verbose = 1;
224864562Sgshapiro			set_delivery_mode(SM_DELIVER, e);
224964562Sgshapiro			message("250 2.0.0 Verbose mode");
225038032Speter			break;
225138032Speter
225238032Speter		  case CMDONEX:		/* doing one transaction only */
225364562Sgshapiro			(void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
225464562Sgshapiro					       "ONEX", e);
225538032Speter			OneXact = TRUE;
225664562Sgshapiro			message("250 2.0.0 Only one transaction");
225738032Speter			break;
225838032Speter
225938032Speter		  case CMDXUSR:		/* initial (user) submission */
226064562Sgshapiro			(void) checksmtpattack(&n_noop, MAXNOOPCOMMANDS, TRUE,
226164562Sgshapiro					       "XUSR", e);
226264562Sgshapiro			define(macid("{daemon_flags}", NULL), "c u", CurEnv);
226364562Sgshapiro			message("250 2.0.0 Initial submission");
226438032Speter			break;
226538032Speter
226638032Speter# if SMTPDEBUG
226738032Speter		  case CMDDBGQSHOW:	/* show queues */
226838032Speter			printf("Send Queue=");
226938032Speter			printaddr(e->e_sendqueue, TRUE);
227038032Speter			break;
227138032Speter
227238032Speter		  case CMDDBGDEBUG:	/* set debug mode */
227338032Speter			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
227438032Speter			tTflag(p);
227564562Sgshapiro			message("200 2.0.0 Debug set");
227638032Speter			break;
227738032Speter
227864562Sgshapiro# else /* SMTPDEBUG */
227938032Speter		  case CMDDBGQSHOW:	/* show queues */
228038032Speter		  case CMDDBGDEBUG:	/* set debug mode */
228138032Speter# endif /* SMTPDEBUG */
228238032Speter		  case CMDLOGBOGUS:	/* bogus command */
228338032Speter			if (LogLevel > 0)
228438032Speter				sm_syslog(LOG_CRIT, e->e_id,
228564562Sgshapiro					  "\"%s\" command from %.100s (%.100s)",
228664562Sgshapiro					  c->cmd_name, CurSmtpClient,
228764562Sgshapiro					  anynet_ntoa(&RealHostAddr));
228864562Sgshapiro			/* FALLTHROUGH */
228938032Speter
229038032Speter		  case CMDERROR:	/* unknown command */
229138032Speter			if (++badcommands > MAXBADCOMMANDS)
229238032Speter			{
229364562Sgshapiro				message("421 4.7.0 %s Too many bad commands; closing connection",
229438032Speter					MyHostName);
229564562Sgshapiro
229664562Sgshapiro				/* arrange to ignore any current send list */
229764562Sgshapiro				e->e_sendqueue = NULL;
229838032Speter				goto doquit;
229938032Speter			}
230038032Speter
230164562Sgshapiro			usrerr("500 5.5.1 Command unrecognized: \"%s\"",
230264562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
230338032Speter			break;
230438032Speter
230564562Sgshapiro		  case CMDUNIMPL:
230664562Sgshapiro			usrerr("502 5.5.1 Command not implemented: \"%s\"",
230764562Sgshapiro			       shortenstring(inp, MAXSHORTSTR));
230864562Sgshapiro			break;
230964562Sgshapiro
231038032Speter		  default:
231138032Speter			errno = 0;
231264562Sgshapiro			syserr("500 5.5.0 smtp: unknown code %d", c->cmd_code);
231338032Speter			break;
231438032Speter		}
231564562Sgshapiro# if SASL
231664562Sgshapiro		}
231764562Sgshapiro# endif /* SASL */
231838032Speter	}
231964562Sgshapiro
232038032Speter}
232138032Speter/*
232238032Speter**  CHECKSMTPATTACK -- check for denial-of-service attack by repetition
232338032Speter**
232438032Speter**	Parameters:
232538032Speter**		pcounter -- pointer to a counter for this command.
232638032Speter**		maxcount -- maximum value for this counter before we
232738032Speter**			slow down.
232864562Sgshapiro**		waitnow -- sleep now (in this routine)?
232938032Speter**		cname -- command name for logging.
233038032Speter**		e -- the current envelope.
233138032Speter**
233238032Speter**	Returns:
233338032Speter**		none.
233438032Speter**
233538032Speter**	Side Effects:
233638032Speter**		Slows down if we seem to be under attack.
233738032Speter*/
233838032Speter
233964562Sgshapirostatic time_t
234064562Sgshapirochecksmtpattack(pcounter, maxcount, waitnow, cname, e)
234138032Speter	volatile int *pcounter;
234238032Speter	int maxcount;
234364562Sgshapiro	bool waitnow;
234438032Speter	char *cname;
234538032Speter	ENVELOPE *e;
234638032Speter{
234738032Speter	if (++(*pcounter) >= maxcount)
234838032Speter	{
234964562Sgshapiro		time_t s;
235064562Sgshapiro
235138032Speter		if (*pcounter == maxcount && LogLevel > 5)
235238032Speter		{
235338032Speter			sm_syslog(LOG_INFO, e->e_id,
235464562Sgshapiro				  "%.100s: %.40s attack?",
235564562Sgshapiro				  CurSmtpClient, cname);
235638032Speter		}
235764562Sgshapiro		s = 1 << (*pcounter - maxcount);
235864562Sgshapiro		if (s >= MAXTIMEOUT)
235964562Sgshapiro			s = MAXTIMEOUT;
236064562Sgshapiro		/* sleep at least 1 second before returning */
236164562Sgshapiro		(void) sleep(*pcounter / maxcount);
236264562Sgshapiro		s -= *pcounter / maxcount;
236364562Sgshapiro		if (waitnow)
236464562Sgshapiro		{
236564562Sgshapiro			(void) sleep(s);
236664562Sgshapiro			return(0);
236764562Sgshapiro		}
236864562Sgshapiro		return(s);
236938032Speter	}
237064562Sgshapiro	return((time_t) 0);
237138032Speter}
237238032Speter/*
237338032Speter**  SKIPWORD -- skip a fixed word.
237438032Speter**
237538032Speter**	Parameters:
237638032Speter**		p -- place to start looking.
237738032Speter**		w -- word to skip.
237838032Speter**
237938032Speter**	Returns:
238038032Speter**		p following w.
238138032Speter**		NULL on error.
238238032Speter**
238338032Speter**	Side Effects:
238438032Speter**		clobbers the p data area.
238538032Speter*/
238638032Speter
238738032Speterstatic char *
238838032Speterskipword(p, w)
238938032Speter	register char *volatile p;
239038032Speter	char *w;
239138032Speter{
239238032Speter	register char *q;
239338032Speter	char *firstp = p;
239438032Speter
239538032Speter	/* find beginning of word */
239638032Speter	while (isascii(*p) && isspace(*p))
239738032Speter		p++;
239838032Speter	q = p;
239938032Speter
240038032Speter	/* find end of word */
240138032Speter	while (*p != '\0' && *p != ':' && !(isascii(*p) && isspace(*p)))
240238032Speter		p++;
240338032Speter	while (isascii(*p) && isspace(*p))
240438032Speter		*p++ = '\0';
240538032Speter	if (*p != ':')
240638032Speter	{
240738032Speter	  syntax:
240864562Sgshapiro		usrerr("501 5.5.2 Syntax error in parameters scanning \"%s\"",
240938032Speter			shortenstring(firstp, MAXSHORTSTR));
241064562Sgshapiro		return NULL;
241138032Speter	}
241238032Speter	*p++ = '\0';
241338032Speter	while (isascii(*p) && isspace(*p))
241438032Speter		p++;
241538032Speter
241638032Speter	if (*p == '\0')
241738032Speter		goto syntax;
241838032Speter
241938032Speter	/* see if the input word matches desired word */
242038032Speter	if (strcasecmp(q, w))
242138032Speter		goto syntax;
242238032Speter
242364562Sgshapiro	return p;
242438032Speter}
242538032Speter/*
242638032Speter**  MAIL_ESMTP_ARGS -- process ESMTP arguments from MAIL line
242738032Speter**
242838032Speter**	Parameters:
242938032Speter**		kp -- the parameter key.
243038032Speter**		vp -- the value of that parameter.
243138032Speter**		e -- the envelope.
243238032Speter**
243338032Speter**	Returns:
243438032Speter**		none.
243538032Speter*/
243638032Speter
243764562Sgshapirostatic void
243838032Spetermail_esmtp_args(kp, vp, e)
243938032Speter	char *kp;
244038032Speter	char *vp;
244138032Speter	ENVELOPE *e;
244238032Speter{
244338032Speter	if (strcasecmp(kp, "size") == 0)
244438032Speter	{
244538032Speter		if (vp == NULL)
244638032Speter		{
244764562Sgshapiro			usrerr("501 5.5.2 SIZE requires a value");
244838032Speter			/* NOTREACHED */
244938032Speter		}
245064562Sgshapiro		define(macid("{msg_size}", NULL), newstr(vp), e);
245166494Sgshapiro  		e->e_msgsize = strtol(vp, (char **) NULL, 10);
245266494Sgshapiro		if (e->e_msgsize == LONG_MAX && errno == ERANGE)
245366494Sgshapiro		{
245466494Sgshapiro			usrerr("552 5.2.3 Message size exceeds maximum value");
245566494Sgshapiro			/* NOTREACHED */
245666494Sgshapiro		}
245738032Speter	}
245838032Speter	else if (strcasecmp(kp, "body") == 0)
245938032Speter	{
246038032Speter		if (vp == NULL)
246138032Speter		{
246264562Sgshapiro			usrerr("501 5.5.2 BODY requires a value");
246338032Speter			/* NOTREACHED */
246438032Speter		}
246538032Speter		else if (strcasecmp(vp, "8bitmime") == 0)
246638032Speter		{
246738032Speter			SevenBitInput = FALSE;
246838032Speter		}
246938032Speter		else if (strcasecmp(vp, "7bit") == 0)
247038032Speter		{
247138032Speter			SevenBitInput = TRUE;
247238032Speter		}
247338032Speter		else
247438032Speter		{
247564562Sgshapiro			usrerr("501 5.5.4 Unknown BODY type %s",
247638032Speter				vp);
247738032Speter			/* NOTREACHED */
247838032Speter		}
247938032Speter		e->e_bodytype = newstr(vp);
248038032Speter	}
248138032Speter	else if (strcasecmp(kp, "envid") == 0)
248238032Speter	{
248364562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
248464562Sgshapiro		{
248564562Sgshapiro			usrerr("504 5.7.0 Sorry, ENVID not supported, we do not allow DSN");
248664562Sgshapiro			/* NOTREACHED */
248764562Sgshapiro		}
248838032Speter		if (vp == NULL)
248938032Speter		{
249064562Sgshapiro			usrerr("501 5.5.2 ENVID requires a value");
249138032Speter			/* NOTREACHED */
249238032Speter		}
249338032Speter		if (!xtextok(vp))
249438032Speter		{
249564562Sgshapiro			usrerr("501 5.5.4 Syntax error in ENVID parameter value");
249638032Speter			/* NOTREACHED */
249738032Speter		}
249838032Speter		if (e->e_envid != NULL)
249938032Speter		{
250064562Sgshapiro			usrerr("501 5.5.0 Duplicate ENVID parameter");
250138032Speter			/* NOTREACHED */
250238032Speter		}
250338032Speter		e->e_envid = newstr(vp);
250464562Sgshapiro		define(macid("{dsn_envid}", NULL), newstr(vp), e);
250538032Speter	}
250638032Speter	else if (strcasecmp(kp, "ret") == 0)
250738032Speter	{
250864562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
250964562Sgshapiro		{
251064562Sgshapiro			usrerr("504 5.7.0 Sorry, RET not supported, we do not allow DSN");
251164562Sgshapiro			/* NOTREACHED */
251264562Sgshapiro		}
251338032Speter		if (vp == NULL)
251438032Speter		{
251564562Sgshapiro			usrerr("501 5.5.2 RET requires a value");
251638032Speter			/* NOTREACHED */
251738032Speter		}
251838032Speter		if (bitset(EF_RET_PARAM, e->e_flags))
251938032Speter		{
252064562Sgshapiro			usrerr("501 5.5.0 Duplicate RET parameter");
252138032Speter			/* NOTREACHED */
252238032Speter		}
252338032Speter		e->e_flags |= EF_RET_PARAM;
252438032Speter		if (strcasecmp(vp, "hdrs") == 0)
252538032Speter			e->e_flags |= EF_NO_BODY_RETN;
252638032Speter		else if (strcasecmp(vp, "full") != 0)
252738032Speter		{
252864562Sgshapiro			usrerr("501 5.5.2 Bad argument \"%s\" to RET", vp);
252938032Speter			/* NOTREACHED */
253038032Speter		}
253164562Sgshapiro		define(macid("{dsn_ret}", NULL), newstr(vp), e);
253238032Speter	}
253364562Sgshapiro# if SASL
253464562Sgshapiro	else if (strcasecmp(kp, "auth") == 0)
253564562Sgshapiro	{
253664562Sgshapiro		int len;
253764562Sgshapiro		char *q;
253864562Sgshapiro		char *auth_param;	/* the value of the AUTH=x */
253964562Sgshapiro		bool saveQuickAbort = QuickAbort;
254064562Sgshapiro		bool saveSuprErrs = SuprErrs;
254164562Sgshapiro		char pbuf[256];
254264562Sgshapiro
254364562Sgshapiro		if (vp == NULL)
254464562Sgshapiro		{
254564562Sgshapiro			usrerr("501 5.5.2 AUTH= requires a value");
254664562Sgshapiro			/* NOTREACHED */
254764562Sgshapiro		}
254864562Sgshapiro		if (e->e_auth_param != NULL)
254964562Sgshapiro		{
255064562Sgshapiro			usrerr("501 5.5.0 Duplicate AUTH parameter");
255164562Sgshapiro			/* NOTREACHED */
255264562Sgshapiro		}
255364562Sgshapiro		if ((q = strchr(vp, ' ')) != NULL)
255464562Sgshapiro			len = q - vp + 1;
255564562Sgshapiro		else
255664562Sgshapiro			len = strlen(vp) + 1;
255764562Sgshapiro		auth_param = xalloc(len);
255864562Sgshapiro		(void) strlcpy(auth_param, vp, len);
255964562Sgshapiro		if (!xtextok(auth_param))
256064562Sgshapiro		{
256164562Sgshapiro			usrerr("501 5.5.4 Syntax error in AUTH parameter value");
256264562Sgshapiro			/* just a warning? */
256364562Sgshapiro			/* NOTREACHED */
256464562Sgshapiro		}
256564562Sgshapiro
256664562Sgshapiro		/* XXX this might be cut off */
256764562Sgshapiro		snprintf(pbuf, sizeof pbuf, "%s", xuntextify(auth_param));
256864562Sgshapiro		/* xalloc() the buffer instead? */
256964562Sgshapiro
257064562Sgshapiro		/* XXX define this always or only if trusted? */
257164562Sgshapiro		define(macid("{auth_author}", NULL), newstr(pbuf), e);
257264562Sgshapiro
257364562Sgshapiro		/*
257464562Sgshapiro		**  call Strust_auth to find out whether
257564562Sgshapiro		**  auth_param is acceptable (trusted)
257664562Sgshapiro		**  we shouldn't trust it if not authenticated
257764562Sgshapiro		**  (required by RFC, leave it to ruleset?)
257864562Sgshapiro		*/
257964562Sgshapiro
258064562Sgshapiro		SuprErrs = TRUE;
258164562Sgshapiro		QuickAbort = FALSE;
258264562Sgshapiro		if (strcmp(auth_param, "<>") != 0 &&
258364562Sgshapiro		     (rscheck("trust_auth", pbuf, NULL, e, TRUE, FALSE, 10)
258464562Sgshapiro		      != EX_OK || Errors > 0))
258564562Sgshapiro		{
258664562Sgshapiro			if (tTd(95, 8))
258764562Sgshapiro			{
258864562Sgshapiro				q = e->e_auth_param;
258964562Sgshapiro				dprintf("auth=\"%.100s\" not trusted user=\"%.100s\"\n",
259064562Sgshapiro					pbuf, (q == NULL) ? "" : q);
259164562Sgshapiro			}
259264562Sgshapiro			/* not trusted */
259364562Sgshapiro			e->e_auth_param = newstr("<>");
259464562Sgshapiro		}
259564562Sgshapiro		else
259664562Sgshapiro		{
259764562Sgshapiro			if (tTd(95, 8))
259864562Sgshapiro				dprintf("auth=\"%.100s\" trusted\n", pbuf);
259964562Sgshapiro			e->e_auth_param = newstr(auth_param);
260064562Sgshapiro		}
260164562Sgshapiro		free(auth_param);
260264562Sgshapiro		/* reset values */
260364562Sgshapiro		Errors = 0;
260464562Sgshapiro		QuickAbort = saveQuickAbort;
260564562Sgshapiro		SuprErrs = saveSuprErrs;
260664562Sgshapiro	}
260764562Sgshapiro# endif /* SASL */
260838032Speter	else
260938032Speter	{
261066494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
261138032Speter		/* NOTREACHED */
261238032Speter	}
261338032Speter}
261438032Speter/*
261538032Speter**  RCPT_ESMTP_ARGS -- process ESMTP arguments from RCPT line
261638032Speter**
261738032Speter**	Parameters:
261838032Speter**		a -- the address corresponding to the To: parameter.
261938032Speter**		kp -- the parameter key.
262038032Speter**		vp -- the value of that parameter.
262138032Speter**		e -- the envelope.
262238032Speter**
262338032Speter**	Returns:
262438032Speter**		none.
262538032Speter*/
262638032Speter
262764562Sgshapirostatic void
262838032Speterrcpt_esmtp_args(a, kp, vp, e)
262938032Speter	ADDRESS *a;
263038032Speter	char *kp;
263138032Speter	char *vp;
263238032Speter	ENVELOPE *e;
263338032Speter{
263438032Speter	if (strcasecmp(kp, "notify") == 0)
263538032Speter	{
263638032Speter		char *p;
263738032Speter
263864562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
263964562Sgshapiro		{
264064562Sgshapiro			usrerr("504 5.7.0 Sorry, NOTIFY not supported, we do not allow DSN");
264164562Sgshapiro			/* NOTREACHED */
264264562Sgshapiro		}
264338032Speter		if (vp == NULL)
264438032Speter		{
264564562Sgshapiro			usrerr("501 5.5.2 NOTIFY requires a value");
264638032Speter			/* NOTREACHED */
264738032Speter		}
264838032Speter		a->q_flags &= ~(QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY);
264938032Speter		a->q_flags |= QHASNOTIFY;
265064562Sgshapiro		define(macid("{dsn_notify}", NULL), newstr(vp), e);
265164562Sgshapiro
265238032Speter		if (strcasecmp(vp, "never") == 0)
265338032Speter			return;
265438032Speter		for (p = vp; p != NULL; vp = p)
265538032Speter		{
265638032Speter			p = strchr(p, ',');
265738032Speter			if (p != NULL)
265838032Speter				*p++ = '\0';
265938032Speter			if (strcasecmp(vp, "success") == 0)
266038032Speter				a->q_flags |= QPINGONSUCCESS;
266138032Speter			else if (strcasecmp(vp, "failure") == 0)
266238032Speter				a->q_flags |= QPINGONFAILURE;
266338032Speter			else if (strcasecmp(vp, "delay") == 0)
266438032Speter				a->q_flags |= QPINGONDELAY;
266538032Speter			else
266638032Speter			{
266764562Sgshapiro				usrerr("501 5.5.4 Bad argument \"%s\"  to NOTIFY",
266838032Speter					vp);
266938032Speter				/* NOTREACHED */
267038032Speter			}
267138032Speter		}
267238032Speter	}
267338032Speter	else if (strcasecmp(kp, "orcpt") == 0)
267438032Speter	{
267564562Sgshapiro		if (bitset(PRIV_NORECEIPTS, PrivacyFlags))
267664562Sgshapiro		{
267764562Sgshapiro			usrerr("504 5.7.0 Sorry, ORCPT not supported, we do not allow DSN");
267864562Sgshapiro			/* NOTREACHED */
267964562Sgshapiro		}
268038032Speter		if (vp == NULL)
268138032Speter		{
268264562Sgshapiro			usrerr("501 5.5.2 ORCPT requires a value");
268338032Speter			/* NOTREACHED */
268438032Speter		}
268538032Speter		if (strchr(vp, ';') == NULL || !xtextok(vp))
268638032Speter		{
268764562Sgshapiro			usrerr("501 5.5.4 Syntax error in ORCPT parameter value");
268838032Speter			/* NOTREACHED */
268938032Speter		}
269038032Speter		if (a->q_orcpt != NULL)
269138032Speter		{
269264562Sgshapiro			usrerr("501 5.5.0 Duplicate ORCPT parameter");
269338032Speter			/* NOTREACHED */
269438032Speter		}
269538032Speter		a->q_orcpt = newstr(vp);
269638032Speter	}
269738032Speter	else
269838032Speter	{
269966494Sgshapiro		usrerr("555 5.5.4 %s parameter unrecognized", kp);
270038032Speter		/* NOTREACHED */
270138032Speter	}
270238032Speter}
270338032Speter/*
270438032Speter**  PRINTVRFYADDR -- print an entry in the verify queue
270538032Speter**
270638032Speter**	Parameters:
270738032Speter**		a -- the address to print
270838032Speter**		last -- set if this is the last one.
270938032Speter**		vrfy -- set if this is a VRFY command.
271038032Speter**
271138032Speter**	Returns:
271238032Speter**		none.
271338032Speter**
271438032Speter**	Side Effects:
271538032Speter**		Prints the appropriate 250 codes.
271638032Speter*/
271764562Sgshapiro#define OFFF	(3 + 1 + 5 + 1)	/* offset in fmt: SMTP reply + enh. code */
271838032Speter
271964562Sgshapirostatic void
272038032Speterprintvrfyaddr(a, last, vrfy)
272138032Speter	register ADDRESS *a;
272238032Speter	bool last;
272338032Speter	bool vrfy;
272438032Speter{
272564562Sgshapiro	char fmtbuf[30];
272638032Speter
272738032Speter	if (vrfy && a->q_mailer != NULL &&
272838032Speter	    !bitnset(M_VRFY250, a->q_mailer->m_flags))
272964562Sgshapiro		(void) strlcpy(fmtbuf, "252", sizeof fmtbuf);
273038032Speter	else
273164562Sgshapiro		(void) strlcpy(fmtbuf, "250", sizeof fmtbuf);
273238032Speter	fmtbuf[3] = last ? ' ' : '-';
273364562Sgshapiro	(void) strlcpy(&fmtbuf[4], "2.1.5 ", sizeof fmtbuf - 4);
273438032Speter	if (a->q_fullname == NULL)
273538032Speter	{
273664562Sgshapiro		if ((a->q_mailer == NULL ||
273764562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
273864562Sgshapiro		     strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
273964562Sgshapiro		    strchr(a->q_user, '@') == NULL)
274064562Sgshapiro			(void) strlcpy(&fmtbuf[OFFF], "<%s@%s>",
274164562Sgshapiro				       sizeof fmtbuf - OFFF);
274238032Speter		else
274364562Sgshapiro			(void) strlcpy(&fmtbuf[OFFF], "<%s>",
274464562Sgshapiro				       sizeof fmtbuf - OFFF);
274538032Speter		message(fmtbuf, a->q_user, MyHostName);
274638032Speter	}
274738032Speter	else
274838032Speter	{
274964562Sgshapiro		if ((a->q_mailer == NULL ||
275064562Sgshapiro		     a->q_mailer->m_addrtype == NULL ||
275164562Sgshapiro		     strcasecmp(a->q_mailer->m_addrtype, "rfc822") == 0) &&
275264562Sgshapiro		    strchr(a->q_user, '@') == NULL)
275364562Sgshapiro			(void) strlcpy(&fmtbuf[OFFF], "%s <%s@%s>",
275464562Sgshapiro				       sizeof fmtbuf - OFFF);
275538032Speter		else
275664562Sgshapiro			(void) strlcpy(&fmtbuf[OFFF], "%s <%s>",
275764562Sgshapiro				       sizeof fmtbuf - OFFF);
275838032Speter		message(fmtbuf, a->q_fullname, a->q_user, MyHostName);
275938032Speter	}
276038032Speter}
276138032Speter/*
276238032Speter**  RUNINCHILD -- return twice -- once in the child, then in the parent again
276338032Speter**
276438032Speter**	Parameters:
276538032Speter**		label -- a string used in error messages
276638032Speter**
276738032Speter**	Returns:
276864562Sgshapiro**		RIC_INCHILD in the child
276964562Sgshapiro**		RIC_INPARENT in the parent
277064562Sgshapiro**		RIC_TEMPFAIL tempfail condition
277138032Speter**
277238032Speter**	Side Effects:
277338032Speter**		none.
277438032Speter*/
277538032Speter
277664562Sgshapirostatic int
277738032Speterruninchild(label, e)
277838032Speter	char *label;
277938032Speter	register ENVELOPE *e;
278038032Speter{
278138032Speter	pid_t childpid;
278238032Speter
278338032Speter	if (!OneXact)
278438032Speter	{
278564562Sgshapiro		extern int NumQueues;
278664562Sgshapiro
278738032Speter		/*
278864562Sgshapiro		**  advance state of PRNG
278964562Sgshapiro		**  this is necessary because otherwise all child processes
279064562Sgshapiro		**  will produce the same PRN sequence and hence the selection
279164562Sgshapiro		**  of a queue directory is not "really" random.
279264562Sgshapiro		*/
279364562Sgshapiro		if (NumQueues > 1)
279464562Sgshapiro			(void) get_random();
279564562Sgshapiro
279664562Sgshapiro		/*
279764562Sgshapiro		**  Disable child process reaping, in case ETRN has preceded
279838032Speter		**  MAIL command, and then fork.
279938032Speter		*/
280038032Speter
280138032Speter		(void) blocksignal(SIGCHLD);
280238032Speter
280338032Speter		childpid = dofork();
280438032Speter		if (childpid < 0)
280538032Speter		{
280664562Sgshapiro			syserr("451 4.3.0 %s: cannot fork", label);
280738032Speter			(void) releasesignal(SIGCHLD);
280864562Sgshapiro			return RIC_INPARENT;
280938032Speter		}
281038032Speter		if (childpid > 0)
281138032Speter		{
281238032Speter			auto int st;
281338032Speter
281438032Speter			/* parent -- wait for child to complete */
281564562Sgshapiro			sm_setproctitle(TRUE, e, "server %s child wait",
281664562Sgshapiro					CurSmtpClient);
281738032Speter			st = waitfor(childpid);
281838032Speter			if (st == -1)
281964562Sgshapiro				syserr("451 4.3.0 %s: lost child", label);
282038032Speter			else if (!WIFEXITED(st))
282164562Sgshapiro			{
282264562Sgshapiro				syserr("451 4.3.0 %s: died on signal %d",
282364562Sgshapiro				       label, st & 0177);
282464562Sgshapiro				return RIC_TEMPFAIL;
282564562Sgshapiro			}
282638032Speter
282764562Sgshapiro			/* if exited on a QUIT command, complete the process */
282838032Speter			if (WEXITSTATUS(st) == EX_QUIT)
282938032Speter			{
283038032Speter				disconnect(1, e);
283142575Speter				finis(TRUE, ExitStat);
283238032Speter			}
283338032Speter
283438032Speter			/* restore the child signal */
283538032Speter			(void) releasesignal(SIGCHLD);
283638032Speter
283764562Sgshapiro			return RIC_INPARENT;
283838032Speter		}
283938032Speter		else
284038032Speter		{
284138032Speter			/* child */
284238032Speter			InChild = TRUE;
284338032Speter			QuickAbort = FALSE;
284464562Sgshapiro			clearstats();
284538032Speter			clearenvelope(e, FALSE);
284664562Sgshapiro			assign_queueid(e);
284738032Speter			(void) setsignal(SIGCHLD, SIG_DFL);
284838032Speter			(void) releasesignal(SIGCHLD);
284938032Speter		}
285038032Speter	}
285164562Sgshapiro	return RIC_INCHILD;
285238032Speter}
285338032Speter
285464562Sgshapiro# if SASL
285564562Sgshapiro
285638032Speter/*
285764562Sgshapiro**  SASLMECHS -- get list of possible AUTH mechanisms
285864562Sgshapiro**
285964562Sgshapiro**	Parameters:
286064562Sgshapiro**		conn -- SASL connection info
286164562Sgshapiro**		mechlist -- output parameter for list of mechanisms
286264562Sgshapiro**
286364562Sgshapiro**	Returns:
286464562Sgshapiro**		number of mechs
286564562Sgshapiro*/
286664562Sgshapiro
286764562Sgshapirostatic int
286864562Sgshapirosaslmechs(conn, mechlist)
286964562Sgshapiro	sasl_conn_t *conn;
287064562Sgshapiro	char **mechlist;
287164562Sgshapiro{
287264562Sgshapiro	int len, num, result;
287364562Sgshapiro
287464562Sgshapiro	/* "user" is currently unused */
287564562Sgshapiro	result = sasl_listmech(conn, "user", /* XXX */
287664562Sgshapiro			       "", " ", "", mechlist,
287764562Sgshapiro			       (u_int *)&len, (u_int *)&num);
287864562Sgshapiro	if (result == SASL_OK && num > 0)
287964562Sgshapiro	{
288064562Sgshapiro		if (LogLevel > 11)
288164562Sgshapiro			sm_syslog(LOG_INFO, NOQID,
288264562Sgshapiro				  "SASL: available mech=%s, allowed mech=%s",
288364562Sgshapiro				  *mechlist, AuthMechanisms);
288464562Sgshapiro		*mechlist = intersect(AuthMechanisms, *mechlist);
288564562Sgshapiro	}
288664562Sgshapiro	else
288764562Sgshapiro	{
288864562Sgshapiro		if (LogLevel > 9)
288964562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
289064562Sgshapiro				  "SASL error: listmech=%d, num=%d",
289164562Sgshapiro				  result, num);
289264562Sgshapiro	}
289364562Sgshapiro	return num;
289464562Sgshapiro}
289564562Sgshapiro
289664562Sgshapiro/*
289764562Sgshapiro**  PROXY_POLICY -- define proxy policy for AUTH
289864562Sgshapiro**
289964562Sgshapiro**	Parameters:
290064562Sgshapiro**		conntext -- unused
290164562Sgshapiro**		auth_identity -- authentication identity
290264562Sgshapiro**		requested_user -- authorization identity
290364562Sgshapiro**		user -- allowed user (output)
290464562Sgshapiro**		errstr -- possible error string (output)
290564562Sgshapiro**
290664562Sgshapiro**	Returns:
290764562Sgshapiro**		ok?
290864562Sgshapiro*/
290964562Sgshapiro
291064562Sgshapiroint
291164562Sgshapiroproxy_policy(context, auth_identity, requested_user, user, errstr)
291264562Sgshapiro	void *context;
291364562Sgshapiro	const char *auth_identity;
291464562Sgshapiro	const char *requested_user;
291564562Sgshapiro	const char **user;
291664562Sgshapiro	const char **errstr;
291764562Sgshapiro{
291864562Sgshapiro	if (user == NULL || auth_identity == NULL)
291964562Sgshapiro		return SASL_FAIL;
292064562Sgshapiro	*user = newstr(auth_identity);
292164562Sgshapiro	return SASL_OK;
292264562Sgshapiro}
292364562Sgshapiro
292464562Sgshapiro# endif /* SASL */
292564562Sgshapiro
292664562Sgshapiro# if STARTTLS
292764562Sgshapiro#  if !TLS_NO_RSA
292864562SgshapiroRSA *rsa_tmp;	/* temporary RSA key */
292964562Sgshapirostatic RSA * tmp_rsa_key __P((SSL *, int, int));
293064562Sgshapiro#  endif /* !TLS_NO_RSA */
293164562Sgshapiro
293264562Sgshapiro# if !NO_DH
293364562Sgshapirostatic DH *get_dh512 __P((void));
293464562Sgshapiro
293564562Sgshapirostatic unsigned char dh512_p[] =
293664562Sgshapiro{
293764562Sgshapiro	0xDA,0x58,0x3C,0x16,0xD9,0x85,0x22,0x89,0xD0,0xE4,0xAF,0x75,
293864562Sgshapiro	0x6F,0x4C,0xCA,0x92,0xDD,0x4B,0xE5,0x33,0xB8,0x04,0xFB,0x0F,
293964562Sgshapiro	0xED,0x94,0xEF,0x9C,0x8A,0x44,0x03,0xED,0x57,0x46,0x50,0xD3,
294064562Sgshapiro	0x69,0x99,0xDB,0x29,0xD7,0x76,0x27,0x6B,0xA2,0xD3,0xD4,0x12,
294164562Sgshapiro	0xE2,0x18,0xF4,0xDD,0x1E,0x08,0x4C,0xF6,0xD8,0x00,0x3E,0x7C,
294264562Sgshapiro	0x47,0x74,0xE8,0x33
294364562Sgshapiro};
294464562Sgshapirostatic unsigned char dh512_g[] =
294564562Sgshapiro{
294664562Sgshapiro	0x02
294764562Sgshapiro};
294864562Sgshapiro
294964562Sgshapirostatic DH *
295064562Sgshapiroget_dh512()
295164562Sgshapiro{
295264562Sgshapiro	DH *dh = NULL;
295364562Sgshapiro
295464562Sgshapiro	if ((dh = DH_new()) == NULL)
295564562Sgshapiro		return(NULL);
295664562Sgshapiro	dh->p = BN_bin2bn(dh512_p, sizeof(dh512_p), NULL);
295764562Sgshapiro	dh->g = BN_bin2bn(dh512_g, sizeof(dh512_g), NULL);
295864562Sgshapiro	if ((dh->p == NULL) || (dh->g == NULL))
295964562Sgshapiro		return(NULL);
296064562Sgshapiro	return(dh);
296164562Sgshapiro}
296264562Sgshapiro# endif /* !NO_DH */
296364562Sgshapiro
296464562Sgshapiro/*
296564562Sgshapiro**  TLS_RAND_INIT -- initialize STARTTLS random generator
296664562Sgshapiro**
296764562Sgshapiro**	Parameters:
296864562Sgshapiro**		randfile -- name of file with random data
296964562Sgshapiro**		logl -- loglevel
297064562Sgshapiro**
297164562Sgshapiro**	Returns:
297266494Sgshapiro**		success/failure
297364562Sgshapiro**
297464562Sgshapiro**	Side Effects:
297564562Sgshapiro**		initializes PRNG for tls library.
297664562Sgshapiro*/
297764562Sgshapiro
297864562Sgshapiro#define MIN_RAND_BYTES	16	/* 128 bits */
297964562Sgshapiro
298066494Sgshapirobool
298164562Sgshapirotls_rand_init(randfile, logl)
298264562Sgshapiro	char *randfile;
298364562Sgshapiro	int logl;
298464562Sgshapiro{
298566494Sgshapiro# ifndef HASURANDOMDEV
298664562Sgshapiro	/* not required if /dev/urandom exists, OpenSSL does it internally */
298764562Sgshapiro
298864562Sgshapiro#define RF_OK		0	/* randfile OK */
298964562Sgshapiro#define RF_MISS		1	/* randfile == NULL || *randfile == '\0' */
299064562Sgshapiro#define RF_UNKNOWN	2	/* unknown prefix for randfile */
299164562Sgshapiro
299266494Sgshapiro#define RI_NONE		0	/* no init yet */
299366494Sgshapiro#define RI_SUCCESS	1	/* init was successful */
299466494Sgshapiro#define RI_FAIL		2	/* init failed */
299566494Sgshapiro
299664562Sgshapiro	bool ok;
299764562Sgshapiro	int randdef;
299866494Sgshapiro	static int done = RI_NONE;
299964562Sgshapiro
300064562Sgshapiro	/*
300164562Sgshapiro	**  initialize PRNG
300264562Sgshapiro	*/
300364562Sgshapiro
300466494Sgshapiro	/* did we try this before? if yes: return old value */
300566494Sgshapiro	if (done != RI_NONE)
300666494Sgshapiro		return done == RI_SUCCESS;
300766494Sgshapiro
300866494Sgshapiro	/* set default values */
300964562Sgshapiro	ok = FALSE;
301066494Sgshapiro	done = RI_FAIL;
301164562Sgshapiro	randdef = (randfile == NULL || *randfile == '\0') ? RF_MISS : RF_OK;
301264562Sgshapiro#   if EGD
301364562Sgshapiro	if (randdef == RF_OK && strncasecmp(randfile, "egd:", 4) == 0)
301464562Sgshapiro	{
301564562Sgshapiro		randfile += 4;
301664562Sgshapiro		if (RAND_egd(randfile) < 0)
301764562Sgshapiro		{
301864562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
301964562Sgshapiro				  "TLS: RAND_egd(%s) failed: random number generator not seeded",
302064562Sgshapiro				   randfile);
302164562Sgshapiro		}
302264562Sgshapiro		else
302364562Sgshapiro			ok = TRUE;
302464562Sgshapiro	}
302564562Sgshapiro	else
302664562Sgshapiro#   endif /* EGD */
302764562Sgshapiro	if (randdef == RF_OK && strncasecmp(randfile, "file:", 5) == 0)
302864562Sgshapiro	{
302964562Sgshapiro		int fd;
303064562Sgshapiro		long sff;
303164562Sgshapiro		struct stat st;
303264562Sgshapiro
303364562Sgshapiro		randfile += 5;
303464562Sgshapiro		sff = SFF_SAFEDIRPATH | SFF_NOWLINK
303564562Sgshapiro		      | SFF_NOGWFILES | SFF_NOWWFILES
303664562Sgshapiro		      | SFF_NOGRFILES | SFF_NOWRFILES
303764562Sgshapiro		      | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT;
303864562Sgshapiro		if ((fd = safeopen(randfile, O_RDONLY, 0, sff)) >= 0)
303964562Sgshapiro		{
304064562Sgshapiro			if (fstat(fd, &st) < 0)
304164562Sgshapiro			{
304264562Sgshapiro				if (LogLevel > logl)
304364562Sgshapiro					sm_syslog(LOG_ERR, NOQID,
304464562Sgshapiro						  "TLS: can't fstat(%s)",
304564562Sgshapiro						  randfile);
304664562Sgshapiro			}
304764562Sgshapiro			else
304864562Sgshapiro			{
304964562Sgshapiro				bool use, problem;
305064562Sgshapiro
305164562Sgshapiro				use = TRUE;
305264562Sgshapiro				problem = FALSE;
305364562Sgshapiro				if (st.st_mtime + 600 < curtime())
305464562Sgshapiro				{
305564562Sgshapiro					use = bitnset(DBS_INSUFFICIENTENTROPY,
305664562Sgshapiro						      DontBlameSendmail);
305764562Sgshapiro					problem = TRUE;
305864562Sgshapiro					if (LogLevel > logl)
305964562Sgshapiro						sm_syslog(LOG_ERR, NOQID,
306064562Sgshapiro							  "TLS: RandFile %s too old: %s",
306164562Sgshapiro							  randfile,
306264562Sgshapiro							  use ? "unsafe" :
306364562Sgshapiro								"unusable");
306464562Sgshapiro				}
306564562Sgshapiro				if (use && st.st_size < MIN_RAND_BYTES)
306664562Sgshapiro				{
306764562Sgshapiro					use = bitnset(DBS_INSUFFICIENTENTROPY,
306864562Sgshapiro						      DontBlameSendmail);
306964562Sgshapiro					problem = TRUE;
307064562Sgshapiro					if (LogLevel > logl)
307164562Sgshapiro						sm_syslog(LOG_ERR, NOQID,
307264562Sgshapiro							  "TLS: size(%s) < %d: %s",
307364562Sgshapiro							  randfile,
307464562Sgshapiro							  MIN_RAND_BYTES,
307564562Sgshapiro							  use ? "unsafe" :
307664562Sgshapiro								"unusable");
307764562Sgshapiro				}
307864562Sgshapiro				if (use)
307964562Sgshapiro					ok = RAND_load_file(randfile, -1) >=
308064562Sgshapiro					     MIN_RAND_BYTES;
308164562Sgshapiro				if (use && !ok)
308264562Sgshapiro				{
308364562Sgshapiro					if (LogLevel > logl)
308464562Sgshapiro						sm_syslog(LOG_WARNING,
308564562Sgshapiro							  NOQID,
308664562Sgshapiro							  "TLS: RAND_load_file(%s) failed: random number generator not seeded",
308764562Sgshapiro							  randfile);
308864562Sgshapiro				}
308964562Sgshapiro				if (problem)
309064562Sgshapiro					ok = FALSE;
309164562Sgshapiro			}
309264562Sgshapiro			if (ok || bitnset(DBS_INSUFFICIENTENTROPY,
309364562Sgshapiro					  DontBlameSendmail))
309464562Sgshapiro			{
309564562Sgshapiro				/* add this even if fstat() failed */
309664562Sgshapiro				RAND_seed((void *) &st, sizeof st);
309764562Sgshapiro			}
309864562Sgshapiro			(void) close(fd);
309964562Sgshapiro		}
310064562Sgshapiro		else
310164562Sgshapiro		{
310264562Sgshapiro			if (LogLevel > logl)
310364562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
310464562Sgshapiro					  "TLS: Warning: safeopen(%s) failed",
310564562Sgshapiro					  randfile);
310664562Sgshapiro		}
310764562Sgshapiro	}
310864562Sgshapiro	else if (randdef == RF_OK)
310964562Sgshapiro	{
311064562Sgshapiro		if (LogLevel > logl)
311164562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
311264562Sgshapiro				  "TLS: Error: no proper random file definition %s",
311364562Sgshapiro				  randfile);
311464562Sgshapiro		randdef = RF_UNKNOWN;
311564562Sgshapiro	}
311664562Sgshapiro	if (randdef == RF_MISS)
311764562Sgshapiro	{
311864562Sgshapiro		if (LogLevel > logl)
311964562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
312064562Sgshapiro				  "TLS: Error: missing random file definition");
312164562Sgshapiro	}
312264562Sgshapiro	if (!ok && bitnset(DBS_INSUFFICIENTENTROPY, DontBlameSendmail))
312364562Sgshapiro	{
312464562Sgshapiro		int i;
312564562Sgshapiro		long r;
312664562Sgshapiro		unsigned char buf[MIN_RAND_BYTES];
312764562Sgshapiro
312864562Sgshapiro		/* assert((MIN_RAND_BYTES % sizeof(long)) == 0); */
312964562Sgshapiro		for (i = 0; i <= sizeof(buf) - sizeof(long); i += sizeof(long))
313064562Sgshapiro		{
313164562Sgshapiro			r = get_random();
313264562Sgshapiro			(void) memcpy(buf + i, (void *) &r, sizeof(long));
313364562Sgshapiro		}
313464562Sgshapiro		RAND_seed(buf, sizeof buf);
313564562Sgshapiro		if (LogLevel > logl)
313664562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
313764562Sgshapiro				  "TLS: Warning: random number generator not properly seeded");
313866494Sgshapiro		ok = TRUE;
313964562Sgshapiro	}
314066494Sgshapiro	done = ok ? RI_SUCCESS : RI_FAIL;
314166494Sgshapiro	return ok;
314266494Sgshapiro# else /* !HASURANDOMDEV */
314366494Sgshapiro	return TRUE;
314466494Sgshapiro# endif /* !HASURANDOMDEV */
314564562Sgshapiro}
314664562Sgshapiro
314764562Sgshapiro/*
314864562Sgshapiro**  status in initialization
314964562Sgshapiro**  these flags keep track of the status of the initialization
315064562Sgshapiro**  i.e., whether a file exists (_EX) and whether it can be used (_OK)
315164562Sgshapiro**  [due to permissions]
315264562Sgshapiro*/
315364562Sgshapiro#define TLS_S_NONE	0x00000000	/* none yet  */
315464562Sgshapiro#define TLS_S_CERT_EX	0x00000001	/* CERT file exists */
315564562Sgshapiro#define TLS_S_CERT_OK	0x00000002	/* CERT file is ok */
315664562Sgshapiro#define TLS_S_KEY_EX	0x00000004	/* KEY file exists */
315764562Sgshapiro#define TLS_S_KEY_OK	0x00000008	/* KEY file is ok */
315864562Sgshapiro#define TLS_S_CERTP_EX	0x00000010	/* CA CERT PATH exists */
315964562Sgshapiro#define TLS_S_CERTP_OK	0x00000020	/* CA CERT PATH is ok */
316064562Sgshapiro#define TLS_S_CERTF_EX	0x00000040	/* CA CERT FILE exists */
316164562Sgshapiro#define TLS_S_CERTF_OK	0x00000080	/* CA CERT FILE is ok */
316264562Sgshapiro
316364562Sgshapiro# if _FFR_TLS_1
316464562Sgshapiro#define TLS_S_CERT2_EX	0x00001000	/* 2nd CERT file exists */
316564562Sgshapiro#define TLS_S_CERT2_OK	0x00002000	/* 2nd CERT file is ok */
316664562Sgshapiro#define TLS_S_KEY2_EX	0x00004000	/* 2nd KEY file exists */
316764562Sgshapiro#define TLS_S_KEY2_OK	0x00008000	/* 2nd KEY file is ok */
316864562Sgshapiro# endif /* _FFR_TLS_1 */
316964562Sgshapiro
317064562Sgshapiro#define TLS_S_DH_OK	0x00200000	/* DH cert is ok */
317164562Sgshapiro#define TLS_S_DHPAR_EX	0x00400000	/* DH param file exists */
317264562Sgshapiro#define TLS_S_DHPAR_OK	0x00800000	/* DH param file is ok to use */
317364562Sgshapiro
317464562Sgshapiro/*
317564562Sgshapiro**  TLS_OK_F -- can var be an absolute filename?
317664562Sgshapiro**
317764562Sgshapiro**	Parameters:
317864562Sgshapiro**		var -- filename
317964562Sgshapiro**		fn -- what is the filename used for?
318064562Sgshapiro**
318164562Sgshapiro**	Returns:
318264562Sgshapiro**		ok?
318364562Sgshapiro*/
318464562Sgshapiro
318564562Sgshapirostatic bool
318664562Sgshapirotls_ok_f(var, fn)
318764562Sgshapiro	char *var;
318864562Sgshapiro	char *fn;
318964562Sgshapiro{
319064562Sgshapiro	/* must be absolute pathname */
319164562Sgshapiro	if (var != NULL && *var == '/')
319264562Sgshapiro		return TRUE;
319364562Sgshapiro	if (LogLevel > 12)
319464562Sgshapiro		sm_syslog(LOG_WARNING, NOQID, "TLS: file %s missing", fn);
319564562Sgshapiro	return FALSE;
319664562Sgshapiro}
319764562Sgshapiro
319864562Sgshapiro/*
319964562Sgshapiro**  TLS_SAFE_F -- is a file safe to use?
320064562Sgshapiro**
320164562Sgshapiro**	Parameters:
320264562Sgshapiro**		var -- filename
320364562Sgshapiro**		sff -- flags for safefile()
320464562Sgshapiro**
320564562Sgshapiro**	Returns:
320664562Sgshapiro**		ok?
320764562Sgshapiro*/
320864562Sgshapiro
320964562Sgshapirostatic bool
321064562Sgshapirotls_safe_f(var, sff)
321164562Sgshapiro	char *var;
321264562Sgshapiro	long sff;
321364562Sgshapiro{
321464562Sgshapiro	int ret;
321564562Sgshapiro
321664562Sgshapiro	if ((ret = safefile(var, RunAsUid, RunAsGid, RunAsUserName, sff,
321764562Sgshapiro			    S_IRUSR, NULL)) == 0)
321864562Sgshapiro		return TRUE;
321964562Sgshapiro	if (LogLevel > 7)
322064562Sgshapiro		sm_syslog(LOG_WARNING, NOQID, "TLS: file %s unsafe: %s",
322164562Sgshapiro			  var, errstring(ret));
322264562Sgshapiro	return FALSE;
322364562Sgshapiro}
322464562Sgshapiro
322564562Sgshapiro/*
322664562Sgshapiro**  TLS_OK_F -- macro to simplify calls to tls_ok_f
322764562Sgshapiro**
322864562Sgshapiro**	Parameters:
322964562Sgshapiro**		var -- filename
323064562Sgshapiro**		fn -- what is the filename used for?
323164562Sgshapiro**		req -- is the file required?
323264562Sgshapiro**		st -- status bit to set if ok
323364562Sgshapiro**
323464562Sgshapiro**	Side Effects:
323564562Sgshapiro**		uses r, ok; may change ok and status.
323664562Sgshapiro**
323764562Sgshapiro*/
323864562Sgshapiro
323964562Sgshapiro#define TLS_OK_F(var, fn, req, st) if (ok) \
324064562Sgshapiro	{ \
324164562Sgshapiro		r = tls_ok_f(var, fn); \
324264562Sgshapiro		if (r) \
324364562Sgshapiro			status |= st; \
324464562Sgshapiro		else if (req) \
324564562Sgshapiro			ok = FALSE; \
324664562Sgshapiro	}
324764562Sgshapiro
324864562Sgshapiro/*
324964562Sgshapiro**  TLS_UNR -- macro to return whether a file should be unreadable
325064562Sgshapiro**
325164562Sgshapiro**	Parameters:
325264562Sgshapiro**		bit -- flag to test
325364562Sgshapiro**		req -- flags
325464562Sgshapiro**
325564562Sgshapiro**	Returns:
325664562Sgshapiro**		0/SFF_NORFILES
325764562Sgshapiro*/
325864562Sgshapiro#define TLS_UNR(bit, req)	(bitset(bit, req) ? SFF_NORFILES : 0)
325964562Sgshapiro
326064562Sgshapiro/*
326164562Sgshapiro**  TLS_SAFE_F -- macro to simplify calls to tls_safe_f
326264562Sgshapiro**
326364562Sgshapiro**	Parameters:
326464562Sgshapiro**		var -- filename
326564562Sgshapiro**		sff -- flags for safefile()
326664562Sgshapiro**		req -- is the file required?
326764562Sgshapiro**		ex -- does the file exist?
326864562Sgshapiro**		st -- status bit to set if ok
326964562Sgshapiro**
327064562Sgshapiro**	Side Effects:
327164562Sgshapiro**		uses r, ok, ex; may change ok and status.
327264562Sgshapiro**
327364562Sgshapiro*/
327464562Sgshapiro
327564562Sgshapiro#define TLS_SAFE_F(var, sff, req, ex, st) if (ex && ok) \
327664562Sgshapiro	{ \
327764562Sgshapiro		r = tls_safe_f(var, sff); \
327864562Sgshapiro		if (r) \
327964562Sgshapiro			status |= st;	\
328064562Sgshapiro		else if (req) \
328164562Sgshapiro			ok = FALSE;	\
328264562Sgshapiro	}
328364562Sgshapiro
328464562Sgshapiro/*
328564562Sgshapiro**  INITTLS -- initialize TLS
328664562Sgshapiro**
328764562Sgshapiro**	Parameters:
328864562Sgshapiro**		ctx -- pointer to context
328964562Sgshapiro**		req -- requirements for initialization (see sendmail.h)
329064562Sgshapiro**		srv -- server side?
329164562Sgshapiro**		certfile -- filename of certificate
329264562Sgshapiro**		keyfile -- filename of private key
329364562Sgshapiro**		cacertpath -- path to CAs
329464562Sgshapiro**		cacertfile -- file with CA
329564562Sgshapiro**		dhparam -- parameters for DH
329664562Sgshapiro**
329764562Sgshapiro**	Returns:
329864562Sgshapiro**		succeeded?
329964562Sgshapiro*/
330064562Sgshapiro
330164562Sgshapirobool
330264562Sgshapiroinittls(ctx, req, srv, certfile, keyfile, cacertpath, cacertfile, dhparam)
330364562Sgshapiro	SSL_CTX **ctx;
330464562Sgshapiro	u_long req;
330564562Sgshapiro	bool srv;
330664562Sgshapiro	char *certfile, *keyfile, *cacertpath, *cacertfile, *dhparam;
330764562Sgshapiro{
330864562Sgshapiro# if !NO_DH
330964562Sgshapiro	static DH *dh = NULL;
331064562Sgshapiro# endif /* !NO_DH */
331164562Sgshapiro	int r;
331264562Sgshapiro	bool ok;
331364562Sgshapiro	long sff, status;
331464562Sgshapiro	char *who;
331564562Sgshapiro# if _FFR_TLS_1
331664562Sgshapiro	char *cf2, *kf2;
331764562Sgshapiro# endif /* _FFR_TLS_1 */
331864562Sgshapiro
331964562Sgshapiro	status = TLS_S_NONE;
332064562Sgshapiro	who = srv ? "srv" : "clt";
332164562Sgshapiro	if (ctx == NULL)
332264562Sgshapiro		syserr("TLS: %s:inittls: ctx == NULL", who);
332364562Sgshapiro
332464562Sgshapiro	/* already initialized? (we could re-init...) */
332564562Sgshapiro	if (*ctx != NULL)
332664562Sgshapiro		return TRUE;
332766494Sgshapiro
332866494Sgshapiro	/* PRNG seeded? */
332966494Sgshapiro	if (!tls_rand_init(RandFile, 10))
333066494Sgshapiro		return FALSE;
333166494Sgshapiro
333266494Sgshapiro	/* let's start with the assumption it will work */
333364562Sgshapiro	ok = TRUE;
333464562Sgshapiro
333564562Sgshapiro# if _FFR_TLS_1
333664562Sgshapiro	/*
333764562Sgshapiro	**  look for a second filename: it must be separated by a ','
333864562Sgshapiro	**  no blanks allowed (they won't be skipped).
333964562Sgshapiro	**  we change a global variable here! this change will be undone
334064562Sgshapiro	**  before return from the function but only if it returns TRUE.
334164562Sgshapiro	**  this isn't a problem since in a failure case this function
334264562Sgshapiro	**  won't be called again with the same (overwritten) values.
334364562Sgshapiro	**  otherwise each return must be replaced with a goto endinittls.
334464562Sgshapiro	*/
334564562Sgshapiro	cf2 = NULL;
334664562Sgshapiro	kf2 = NULL;
334764562Sgshapiro	if (certfile != NULL && (cf2 = strchr(certfile, ',')) != NULL)
334864562Sgshapiro	{
334964562Sgshapiro		*cf2++ = '\0';
335064562Sgshapiro		if (keyfile != NULL && (kf2 = strchr(keyfile, ',')) != NULL)
335164562Sgshapiro			*kf2++ = '\0';
335264562Sgshapiro	}
335364562Sgshapiro# endif /* _FFR_TLS_1 */
335464562Sgshapiro
335564562Sgshapiro	/*
335664562Sgshapiro	**  what do we require from the client?
335764562Sgshapiro	**  must it have CERTs?
335864562Sgshapiro	**  introduce an option and decide based on that
335964562Sgshapiro	*/
336064562Sgshapiro
336164562Sgshapiro	TLS_OK_F(certfile, "CertFile", bitset(TLS_I_CERT_EX, req),
336264562Sgshapiro		 TLS_S_CERT_EX);
336364562Sgshapiro	TLS_OK_F(keyfile, "KeyFile", bitset(TLS_I_KEY_EX, req),
336464562Sgshapiro		 TLS_S_KEY_EX);
336564562Sgshapiro	TLS_OK_F(cacertpath, "CACERTPath", bitset(TLS_I_CERTP_EX, req),
336664562Sgshapiro		 TLS_S_CERTP_EX);
336764562Sgshapiro	TLS_OK_F(cacertfile, "CACERTFile", bitset(TLS_I_CERTF_EX, req),
336864562Sgshapiro		 TLS_S_CERTF_EX);
336964562Sgshapiro
337064562Sgshapiro# if _FFR_TLS_1
337164562Sgshapiro	if (cf2 != NULL)
337264562Sgshapiro	{
337364562Sgshapiro		TLS_OK_F(cf2, "CertFile", bitset(TLS_I_CERT_EX, req),
337464562Sgshapiro			 TLS_S_CERT2_EX);
337564562Sgshapiro	}
337664562Sgshapiro	if (kf2 != NULL)
337764562Sgshapiro	{
337864562Sgshapiro		TLS_OK_F(kf2, "KeyFile", bitset(TLS_I_KEY_EX, req),
337964562Sgshapiro			 TLS_S_KEY2_EX);
338064562Sgshapiro	}
338164562Sgshapiro# endif /* _FFR_TLS_1 */
338264562Sgshapiro
338364562Sgshapiro	/*
338464562Sgshapiro	**  valid values for dhparam are (only the first char is checked)
338564562Sgshapiro	**  none	no parameters: don't use DH
338664562Sgshapiro	**  512		generate 512 bit parameters (fixed)
338764562Sgshapiro	**  1024	generate 1024 bit parameters
338864562Sgshapiro	**  /file/name	read parameters from /file/name
338964562Sgshapiro	**  default is: 1024 for server, 512 for client (OK? XXX)
339064562Sgshapiro	*/
339164562Sgshapiro	if (bitset(TLS_I_TRY_DH, req))
339264562Sgshapiro	{
339364562Sgshapiro		if (dhparam != NULL)
339464562Sgshapiro		{
339564562Sgshapiro			char c = *dhparam;
339664562Sgshapiro
339764562Sgshapiro			if (c == '1')
339864562Sgshapiro				req |= TLS_I_DH1024;
339964562Sgshapiro			else if (c == '5')
340064562Sgshapiro				req |= TLS_I_DH512;
340164562Sgshapiro			else if (c != 'n' && c != 'N' && c != '/')
340264562Sgshapiro			{
340364562Sgshapiro				if (LogLevel > 12)
340464562Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
340564562Sgshapiro						  "TLS: error: illegal value '%s' for DHParam",
340664562Sgshapiro						  dhparam);
340764562Sgshapiro				dhparam = NULL;
340864562Sgshapiro			}
340964562Sgshapiro		}
341064562Sgshapiro		if (dhparam == NULL)
341166494Sgshapiro			dhparam = srv ? "1" : "5";
341264562Sgshapiro		else if (*dhparam == '/')
341364562Sgshapiro		{
341464562Sgshapiro			TLS_OK_F(dhparam, "DHParameters",
341564562Sgshapiro				 bitset(TLS_I_DHPAR_EX, req),
341664562Sgshapiro				 TLS_S_DHPAR_EX);
341764562Sgshapiro		}
341864562Sgshapiro	}
341964562Sgshapiro	if (!ok)
342064562Sgshapiro		return ok;
342164562Sgshapiro
342264562Sgshapiro	/* certfile etc. must be "safe". */
342364562Sgshapiro	sff = SFF_REGONLY | SFF_SAFEDIRPATH | SFF_NOWLINK
342464562Sgshapiro	     | SFF_NOGWFILES | SFF_NOWWFILES
342564562Sgshapiro	     | SFF_MUSTOWN | SFF_ROOTOK | SFF_OPENASROOT;
342664562Sgshapiro	if (DontLockReadFiles)
342764562Sgshapiro		sff |= SFF_NOLOCK;
342864562Sgshapiro
342964562Sgshapiro	TLS_SAFE_F(certfile, sff | TLS_UNR(TLS_I_CERT_UNR, req),
343064562Sgshapiro		   bitset(TLS_I_CERT_EX, req),
343164562Sgshapiro		   bitset(TLS_S_CERT_EX, status), TLS_S_CERT_OK);
343264562Sgshapiro	TLS_SAFE_F(keyfile, sff | TLS_UNR(TLS_I_KEY_UNR, req),
343364562Sgshapiro		   bitset(TLS_I_KEY_EX, req),
343464562Sgshapiro		   bitset(TLS_S_KEY_EX, status), TLS_S_KEY_OK);
343564562Sgshapiro	TLS_SAFE_F(cacertfile, sff | TLS_UNR(TLS_I_CERTF_UNR, req),
343664562Sgshapiro		   bitset(TLS_I_CERTF_EX, req),
343764562Sgshapiro		   bitset(TLS_S_CERTF_EX, status), TLS_S_CERTF_OK);
343864562Sgshapiro	TLS_SAFE_F(dhparam, sff | TLS_UNR(TLS_I_DHPAR_UNR, req),
343964562Sgshapiro		   bitset(TLS_I_DHPAR_EX, req),
344064562Sgshapiro		   bitset(TLS_S_DHPAR_EX, status), TLS_S_DHPAR_OK);
344164562Sgshapiro	if (!ok)
344264562Sgshapiro		return ok;
344364562Sgshapiro# if _FFR_TLS_1
344464562Sgshapiro	if (cf2 != NULL)
344564562Sgshapiro	{
344664562Sgshapiro		TLS_SAFE_F(cf2, sff | TLS_UNR(TLS_I_CERT_UNR, req),
344764562Sgshapiro			   bitset(TLS_I_CERT_EX, req),
344864562Sgshapiro			   bitset(TLS_S_CERT2_EX, status), TLS_S_CERT2_OK);
344964562Sgshapiro	}
345064562Sgshapiro	if (kf2 != NULL)
345164562Sgshapiro	{
345264562Sgshapiro		TLS_SAFE_F(kf2, sff | TLS_UNR(TLS_I_KEY_UNR, req),
345364562Sgshapiro			   bitset(TLS_I_KEY_EX, req),
345464562Sgshapiro			   bitset(TLS_S_KEY2_EX, status), TLS_S_KEY2_OK);
345564562Sgshapiro	}
345664562Sgshapiro# endif /* _FFR_TLS_1 */
345764562Sgshapiro
345864562Sgshapiro	/* create a method and a new context */
345964562Sgshapiro	if (srv)
346064562Sgshapiro	{
346164562Sgshapiro		if ((*ctx = SSL_CTX_new(SSLv23_server_method())) == NULL)
346264562Sgshapiro		{
346364562Sgshapiro			if (LogLevel > 7)
346464562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
346564562Sgshapiro					  "TLS: error: SSL_CTX_new(SSLv23_server_method()) failed");
346664562Sgshapiro			return FALSE;
346764562Sgshapiro		}
346864562Sgshapiro	}
346964562Sgshapiro	else
347064562Sgshapiro	{
347164562Sgshapiro		if ((*ctx = SSL_CTX_new(SSLv23_client_method())) == NULL)
347264562Sgshapiro		{
347364562Sgshapiro			if (LogLevel > 7)
347464562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
347564562Sgshapiro					  "TLS: error: SSL_CTX_new(SSLv23_client_method()) failed");
347664562Sgshapiro			return FALSE;
347764562Sgshapiro		}
347864562Sgshapiro	}
347964562Sgshapiro
348064562Sgshapiro#  if TLS_NO_RSA
348164562Sgshapiro	/* turn off backward compatibility, required for no-rsa */
348264562Sgshapiro	SSL_CTX_set_options(*ctx, SSL_OP_NO_SSLv2);
348364562Sgshapiro#  endif /* TLS_NO_RSA */
348464562Sgshapiro
348564562Sgshapiro
348664562Sgshapiro#  if !TLS_NO_RSA
348764562Sgshapiro	/*
348864562Sgshapiro	**  Create a temporary RSA key
348964562Sgshapiro	**  XXX  Maybe we shouldn't create this always (even though it
349064562Sgshapiro	**  is only at startup).
349164562Sgshapiro	**  It is a time-consuming operation and it is not always necessary.
349264562Sgshapiro	**  maybe we should do it only on demand...
349364562Sgshapiro	*/
349464562Sgshapiro	if (bitset(TLS_I_RSA_TMP, req) &&
349564562Sgshapiro	    (rsa_tmp = RSA_generate_key(RSA_KEYLENGTH, RSA_F4, NULL,
349664562Sgshapiro					NULL)) == NULL
349764562Sgshapiro	   )
349864562Sgshapiro	{
349964562Sgshapiro		if (LogLevel > 7)
350064562Sgshapiro		{
350164562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
350264562Sgshapiro				  "TLS: error: %s: RSA_generate_key failed",
350364562Sgshapiro				  who);
350464562Sgshapiro			if (LogLevel > 9)
350564562Sgshapiro				tlslogerr();
350664562Sgshapiro		}
350764562Sgshapiro		return FALSE;
350864562Sgshapiro	}
350964562Sgshapiro#  endif /* !TLS_NO_RSA */
351064562Sgshapiro
351164562Sgshapiro	/*
351264562Sgshapiro	**  load private key
351364562Sgshapiro	**  XXX change this for DSA-only version
351464562Sgshapiro	*/
351564562Sgshapiro	if (bitset(TLS_S_KEY_OK, status) &&
351664562Sgshapiro	    SSL_CTX_use_PrivateKey_file(*ctx, keyfile,
351764562Sgshapiro					 SSL_FILETYPE_PEM) <= 0)
351864562Sgshapiro	{
351964562Sgshapiro		if (LogLevel > 7)
352064562Sgshapiro		{
352164562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
352264562Sgshapiro				  "TLS: error: %s: SSL_CTX_use_PrivateKey_file(%s) failed",
352364562Sgshapiro				  who, keyfile);
352464562Sgshapiro			if (LogLevel > 9)
352564562Sgshapiro				tlslogerr();
352664562Sgshapiro		}
352764562Sgshapiro		if (bitset(TLS_I_USE_KEY, req))
352864562Sgshapiro			return FALSE;
352964562Sgshapiro	}
353064562Sgshapiro
353164562Sgshapiro	/* get the certificate file */
353264562Sgshapiro	if (bitset(TLS_S_CERT_OK, status) &&
353364562Sgshapiro	    SSL_CTX_use_certificate_file(*ctx, certfile,
353464562Sgshapiro					 SSL_FILETYPE_PEM) <= 0)
353564562Sgshapiro	{
353664562Sgshapiro		if (LogLevel > 7)
353764562Sgshapiro		{
353864562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
353964562Sgshapiro				  "TLS: error: %s: SSL_CTX_use_certificate_file(%s) failed",
354064562Sgshapiro				  who, certfile);
354164562Sgshapiro			if (LogLevel > 9)
354264562Sgshapiro				tlslogerr();
354364562Sgshapiro		}
354464562Sgshapiro		if (bitset(TLS_I_USE_CERT, req))
354564562Sgshapiro			return FALSE;
354664562Sgshapiro	}
354764562Sgshapiro
354864562Sgshapiro	/* check the private key */
354964562Sgshapiro	if (bitset(TLS_S_KEY_OK, status) &&
355064562Sgshapiro	    (r = SSL_CTX_check_private_key(*ctx)) <= 0)
355164562Sgshapiro	{
355264562Sgshapiro		/* Private key does not match the certificate public key */
355364562Sgshapiro		if (LogLevel > 5)
355464562Sgshapiro		{
355564562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
355664562Sgshapiro				  "TLS: error: %s: SSL_CTX_check_private_key failed(%s): %d",
355764562Sgshapiro				  who, keyfile, r);
355864562Sgshapiro			if (LogLevel > 9)
355964562Sgshapiro				tlslogerr();
356064562Sgshapiro		}
356164562Sgshapiro		if (bitset(TLS_I_USE_KEY, req))
356264562Sgshapiro			return FALSE;
356364562Sgshapiro	}
356464562Sgshapiro
356564562Sgshapiro# if _FFR_TLS_1
356664562Sgshapiro	/* XXX this code is pretty much duplicated from above! */
356764562Sgshapiro
356864562Sgshapiro	/* load private key */
356964562Sgshapiro	if (bitset(TLS_S_KEY2_OK, status) &&
357064562Sgshapiro	    SSL_CTX_use_PrivateKey_file(*ctx, kf2, SSL_FILETYPE_PEM) <= 0)
357164562Sgshapiro	{
357264562Sgshapiro		if (LogLevel > 7)
357364562Sgshapiro		{
357464562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
357564562Sgshapiro				  "TLS: error: %s: SSL_CTX_use_PrivateKey_file(%s) failed",
357664562Sgshapiro				  who, kf2);
357764562Sgshapiro			if (LogLevel > 9)
357864562Sgshapiro				tlslogerr();
357964562Sgshapiro		}
358064562Sgshapiro	}
358164562Sgshapiro
358264562Sgshapiro	/* get the certificate file */
358364562Sgshapiro	if (bitset(TLS_S_CERT2_OK, status) &&
358464562Sgshapiro	    SSL_CTX_use_certificate_file(*ctx, cf2, SSL_FILETYPE_PEM) <= 0)
358564562Sgshapiro	{
358664562Sgshapiro		if (LogLevel > 7)
358764562Sgshapiro		{
358864562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
358964562Sgshapiro				  "TLS: error: %s: SSL_CTX_use_certificate_file(%s) failed",
359064562Sgshapiro				  who, cf2);
359164562Sgshapiro			if (LogLevel > 9)
359264562Sgshapiro				tlslogerr();
359364562Sgshapiro		}
359464562Sgshapiro	}
359564562Sgshapiro
359664562Sgshapiro	/* we should also check the private key: */
359764562Sgshapiro	if (bitset(TLS_S_KEY2_OK, status) &&
359864562Sgshapiro	    (r = SSL_CTX_check_private_key(*ctx)) <= 0)
359964562Sgshapiro	{
360064562Sgshapiro		/* Private key does not match the certificate public key */
360164562Sgshapiro		if (LogLevel > 5)
360264562Sgshapiro		{
360364562Sgshapiro			sm_syslog(LOG_WARNING, NOQID,
360464562Sgshapiro				  "TLS: error: %s: SSL_CTX_check_private_key 2 failed: %d",
360564562Sgshapiro				  who, r);
360664562Sgshapiro			if (LogLevel > 9)
360764562Sgshapiro				tlslogerr();
360864562Sgshapiro		}
360964562Sgshapiro	}
361064562Sgshapiro# endif /* _FFR_TLS_1 */
361164562Sgshapiro
361264562Sgshapiro	/* SSL_CTX_set_quiet_shutdown(*ctx, 1); violation of standard? */
361364562Sgshapiro	SSL_CTX_set_options(*ctx, SSL_OP_ALL);	/* XXX bug compatibility? */
361464562Sgshapiro
361564562Sgshapiro# if !NO_DH
361664562Sgshapiro	/* Diffie-Hellman initialization */
361764562Sgshapiro	if (bitset(TLS_I_TRY_DH, req))
361864562Sgshapiro	{
361964562Sgshapiro		if (bitset(TLS_S_DHPAR_OK, status))
362064562Sgshapiro		{
362164562Sgshapiro			BIO *bio;
362264562Sgshapiro
362364562Sgshapiro			if ((bio = BIO_new_file(dhparam, "r")) != NULL)
362464562Sgshapiro			{
362564562Sgshapiro				dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
362664562Sgshapiro				BIO_free(bio);
362764562Sgshapiro				if (dh == NULL && LogLevel > 7)
362864562Sgshapiro				{
362964562Sgshapiro					u_long err;
363064562Sgshapiro
363164562Sgshapiro					err = ERR_get_error();
363264562Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
363364562Sgshapiro						  "TLS: error: %s: cannot read DH parameters(%s): %s",
363464562Sgshapiro						  who, dhparam,
363564562Sgshapiro						  ERR_error_string(err, NULL));
363664562Sgshapiro					if (LogLevel > 9)
363764562Sgshapiro						tlslogerr();
363864562Sgshapiro				}
363964562Sgshapiro			}
364064562Sgshapiro			else
364164562Sgshapiro			{
364264562Sgshapiro				if (LogLevel > 5)
364364562Sgshapiro				{
364464562Sgshapiro					sm_syslog(LOG_WARNING, NOQID,
364564562Sgshapiro						  "TLS: error: %s: BIO_new_file(%s) failed",
364664562Sgshapiro						  who, dhparam);
364764562Sgshapiro					if (LogLevel > 9)
364864562Sgshapiro						tlslogerr();
364964562Sgshapiro				}
365064562Sgshapiro			}
365164562Sgshapiro		}
365264562Sgshapiro		if (dh == NULL && bitset(TLS_I_DH1024, req))
365364562Sgshapiro		{
365464562Sgshapiro			DSA *dsa;
365564562Sgshapiro
365664562Sgshapiro			/* this takes a while! (7-130s on a 450MHz AMD K6-2) */
365764562Sgshapiro			dsa = DSA_generate_parameters(1024, NULL, 0, NULL,
365864562Sgshapiro						      NULL, 0, NULL);
365964562Sgshapiro			dh = DSA_dup_DH(dsa);
366064562Sgshapiro			DSA_free(dsa);
366164562Sgshapiro		}
366264562Sgshapiro		else
366364562Sgshapiro		if (dh == NULL && bitset(TLS_I_DH512, req))
366464562Sgshapiro			dh = get_dh512();
366564562Sgshapiro
366664562Sgshapiro		if (dh == NULL)
366764562Sgshapiro		{
366864562Sgshapiro			if (LogLevel > 9)
366964562Sgshapiro			{
367064562Sgshapiro				u_long err;
367164562Sgshapiro
367264562Sgshapiro				err = ERR_get_error();
367364562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
367464562Sgshapiro					  "TLS: error: %s: cannot read or set DH parameters(%s): %s",
367564562Sgshapiro					  who, dhparam,
367664562Sgshapiro					  ERR_error_string(err, NULL));
367764562Sgshapiro			}
367864562Sgshapiro			if (bitset(TLS_I_REQ_DH, req))
367964562Sgshapiro				return FALSE;
368064562Sgshapiro		}
368164562Sgshapiro		else
368264562Sgshapiro		{
368364562Sgshapiro			SSL_CTX_set_tmp_dh(*ctx, dh);
368464562Sgshapiro
368564562Sgshapiro			/* important to avoid small subgroup attacks */
368664562Sgshapiro			SSL_CTX_set_options(*ctx, SSL_OP_SINGLE_DH_USE);
368764562Sgshapiro			if (LogLevel > 12)
368864562Sgshapiro				sm_syslog(LOG_INFO, NOQID,
368964562Sgshapiro					  "TLS: %s: Diffie-Hellman init, key=%d bit (%c)",
369064562Sgshapiro					  who, 8 * DH_size(dh), *dhparam);
369164562Sgshapiro			DH_free(dh);
369264562Sgshapiro		}
369364562Sgshapiro	}
369464562Sgshapiro# endif /* !NO_DH */
369564562Sgshapiro
369664562Sgshapiro
369764562Sgshapiro	/* XXX do we need this cache here? */
369864562Sgshapiro	if (bitset(TLS_I_CACHE, req))
369964562Sgshapiro		SSL_CTX_sess_set_cache_size(*ctx, 128);
370064562Sgshapiro	/* timeout? SSL_CTX_set_timeout(*ctx, TimeOut...); */
370164562Sgshapiro
370264562Sgshapiro	/* load certificate locations and default CA paths */
370364562Sgshapiro	if (bitset(TLS_S_CERTP_EX, status) && bitset(TLS_S_CERTF_EX, status))
370464562Sgshapiro	{
370564562Sgshapiro		if ((r = SSL_CTX_load_verify_locations(*ctx, cacertfile,
370664562Sgshapiro						       cacertpath)) == 1)
370764562Sgshapiro		{
370864562Sgshapiro#  if !TLS_NO_RSA
370964562Sgshapiro			if (bitset(TLS_I_RSA_TMP, req))
371064562Sgshapiro				SSL_CTX_set_tmp_rsa_callback(*ctx, tmp_rsa_key);
371164562Sgshapiro#  endif /* !TLS_NO_RSA */
371264562Sgshapiro
371364562Sgshapiro			/* ask to verify the peer */
371464562Sgshapiro			SSL_CTX_set_verify(*ctx, SSL_VERIFY_PEER, NULL);
371564562Sgshapiro
371664562Sgshapiro			/* install verify callback */
371764562Sgshapiro			SSL_CTX_set_cert_verify_callback(*ctx, tls_verify_cb,
371864562Sgshapiro							 NULL);
371964562Sgshapiro			SSL_CTX_set_client_CA_list(*ctx,
372064562Sgshapiro				SSL_load_client_CA_file(cacertfile));
372164562Sgshapiro		}
372264562Sgshapiro		else
372364562Sgshapiro		{
372464562Sgshapiro			/*
372564562Sgshapiro			**  can't load CA data; do we care?
372664562Sgshapiro			**  the data is necessary to authenticate the client,
372764562Sgshapiro			**  which in turn would be necessary
372864562Sgshapiro			**  if we want to allow relaying based on it.
372964562Sgshapiro			*/
373064562Sgshapiro			if (LogLevel > 5)
373164562Sgshapiro			{
373264562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
373364562Sgshapiro					  "TLS: error: %s: %d load verify locs %s, %s",
373464562Sgshapiro					  who, r, cacertpath, cacertfile);
373564562Sgshapiro				if (LogLevel > 9)
373664562Sgshapiro					tlslogerr();
373764562Sgshapiro			}
373864562Sgshapiro			if (bitset(TLS_I_VRFY_LOC, req))
373964562Sgshapiro				return FALSE;
374064562Sgshapiro		}
374164562Sgshapiro	}
374264562Sgshapiro
374364562Sgshapiro	/* XXX: make this dependent on an option? */
374464562Sgshapiro	if (tTd(96, 9))
374564562Sgshapiro		SSL_CTX_set_info_callback(*ctx, apps_ssl_info_cb);
374664562Sgshapiro
374764562Sgshapiro#  if _FFR_TLS_1
374864562Sgshapiro	/*
374964562Sgshapiro	**  XXX install our own cipher list: option?
375064562Sgshapiro	*/
375164562Sgshapiro	if (CipherList != NULL && *CipherList != '\0')
375264562Sgshapiro	{
375364562Sgshapiro		if (SSL_CTX_set_cipher_list(*ctx, CipherList) <= 0)
375464562Sgshapiro		{
375564562Sgshapiro			if (LogLevel > 7)
375664562Sgshapiro			{
375764562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
375864562Sgshapiro					  "TLS: error: %s: SSL_CTX_set_cipher_list(%s) failed, list ignored",
375964562Sgshapiro					  who, CipherList);
376064562Sgshapiro
376164562Sgshapiro				if (LogLevel > 9)
376264562Sgshapiro					tlslogerr();
376364562Sgshapiro			}
376464562Sgshapiro			/* failure if setting to this list is required? */
376564562Sgshapiro		}
376664562Sgshapiro	}
376764562Sgshapiro#  endif /* _FFR_TLS_1 */
376864562Sgshapiro	if (LogLevel > 12)
376964562Sgshapiro		sm_syslog(LOG_INFO, NOQID, "TLS: init(%s)=%d", who, ok);
377064562Sgshapiro
377164562Sgshapiro# if _FFR_TLS_1
377264562Sgshapiro#  if 0
377364562Sgshapiro	/*
377464562Sgshapiro	**  this label is required if we want to have a "clean" exit
377564562Sgshapiro	**  see the comments above at the initialization of cf2
377664562Sgshapiro	*/
377764562Sgshapiro    endinittls:
377864562Sgshapiro#  endif /* 0 */
377964562Sgshapiro
378064562Sgshapiro	/* undo damage to global variables */
378164562Sgshapiro	if (cf2 != NULL)
378264562Sgshapiro		*--cf2 = ',';
378364562Sgshapiro	if (kf2 != NULL)
378464562Sgshapiro		*--kf2 = ',';
378564562Sgshapiro# endif /* _FFR_TLS_1 */
378664562Sgshapiro
378764562Sgshapiro	return ok;
378864562Sgshapiro}
378964562Sgshapiro/*
379064562Sgshapiro**  INITSRVTLS -- initialize server side TLS
379164562Sgshapiro**
379264562Sgshapiro**	Parameters:
379364562Sgshapiro**		none.
379464562Sgshapiro**
379564562Sgshapiro**	Returns:
379664562Sgshapiro**		succeeded?
379764562Sgshapiro*/
379864562Sgshapiro
379964562Sgshapirobool
380064562Sgshapiroinitsrvtls()
380164562Sgshapiro{
380264562Sgshapiro
380364562Sgshapiro	tls_ok = inittls(&srv_ctx, TLS_I_SRV, TRUE, SrvCERTfile, Srvkeyfile,
380464562Sgshapiro			 CACERTpath, CACERTfile, DHParams);
380564562Sgshapiro	return tls_ok;
380664562Sgshapiro}
380764562Sgshapiro/*
380864562Sgshapiro**  TLS_GET_INFO -- get information about TLS connection
380964562Sgshapiro**
381064562Sgshapiro**	Parameters:
381164562Sgshapiro**		ssl -- SSL connection structure
381264562Sgshapiro**		e -- current envelope
381364562Sgshapiro**		srv -- server or client
381464562Sgshapiro**		host -- hostname of other side
381564562Sgshapiro**
381664562Sgshapiro**	Returns:
381764562Sgshapiro**		result of authentication.
381864562Sgshapiro**
381964562Sgshapiro**	Side Effects:
382064562Sgshapiro**		sets ${cipher}, ${tls_version}, ${verify}, ${cipher_bits},
382164562Sgshapiro**		${cert}
382264562Sgshapiro*/
382364562Sgshapiro
382464562Sgshapiroint
382564562Sgshapirotls_get_info(ssl, e, srv, host)
382664562Sgshapiro	SSL *ssl;
382764562Sgshapiro	ENVELOPE *e;
382864562Sgshapiro	bool srv;
382964562Sgshapiro	char *host;
383064562Sgshapiro{
383164562Sgshapiro	SSL_CIPHER *c;
383264562Sgshapiro	int b, r;
383364562Sgshapiro	char *s;
383464562Sgshapiro	char bitstr[16];
383564562Sgshapiro	X509 *cert;
383664562Sgshapiro
383764562Sgshapiro	c = SSL_get_current_cipher(ssl);
383864562Sgshapiro	define(macid("{cipher}", NULL), newstr(SSL_CIPHER_get_name(c)), e);
383964562Sgshapiro	b = SSL_CIPHER_get_bits(c, &r);
384064562Sgshapiro	(void) snprintf(bitstr, sizeof bitstr, "%d", b);
384164562Sgshapiro	define(macid("{cipher_bits}", NULL), newstr(bitstr), e);
384264562Sgshapiro# if _FFR_TLS_1
384364562Sgshapiro	(void) snprintf(bitstr, sizeof bitstr, "%d", r);
384464562Sgshapiro	define(macid("{alg_bits}", NULL), newstr(bitstr), e);
384564562Sgshapiro# endif /* _FFR_TLS_1 */
384664562Sgshapiro	s = SSL_CIPHER_get_version(c);
384764562Sgshapiro	if (s == NULL)
384864562Sgshapiro		s = "UNKNOWN";
384964562Sgshapiro	define(macid("{tls_version}", NULL), newstr(s), e);
385064562Sgshapiro
385164562Sgshapiro	cert = SSL_get_peer_certificate(ssl);
385264562Sgshapiro	if (LogLevel >= 14)
385364562Sgshapiro		sm_syslog(LOG_INFO, e->e_id,
385466494Sgshapiro			  "TLS: get_verify in %s: %ld get_peer: 0x%lx",
385564562Sgshapiro			  srv ? "srv" : "clt",
385666494Sgshapiro			  SSL_get_verify_result(ssl), (u_long) cert);
385764562Sgshapiro	if (cert != NULL)
385864562Sgshapiro	{
385964562Sgshapiro		char buf[MAXNAME];
386064562Sgshapiro
386164562Sgshapiro		X509_NAME_oneline(X509_get_subject_name(cert),
386264562Sgshapiro				  buf, sizeof buf);
386364562Sgshapiro		define(macid("{cert_subject}", NULL),
386464562Sgshapiro			       newstr(xtextify(buf, "<>\")")), e);
386564562Sgshapiro		X509_NAME_oneline(X509_get_issuer_name(cert),
386664562Sgshapiro				  buf, sizeof buf);
386764562Sgshapiro		define(macid("{cert_issuer}", NULL),
386864562Sgshapiro		       newstr(xtextify(buf, "<>\")")), e);
386964562Sgshapiro# if _FFR_TLS_1
387064562Sgshapiro		X509_NAME_get_text_by_NID(X509_get_subject_name(cert),
387164562Sgshapiro					  NID_commonName, buf, sizeof buf);
387264562Sgshapiro		define(macid("{cn_subject}", NULL),
387364562Sgshapiro		       newstr(xtextify(buf, "<>\")")), e);
387464562Sgshapiro		X509_NAME_get_text_by_NID(X509_get_issuer_name(cert),
387564562Sgshapiro					  NID_commonName, buf, sizeof buf);
387664562Sgshapiro		define(macid("{cn_issuer}", NULL),
387764562Sgshapiro		       newstr(xtextify(buf, "<>\")")), e);
387864562Sgshapiro# endif /* _FFR_TLS_1 */
387964562Sgshapiro	}
388064562Sgshapiro	else
388164562Sgshapiro	{
388264562Sgshapiro		define(macid("{cert_subject}", NULL), "", e);
388364562Sgshapiro		define(macid("{cert_issuer}", NULL), "", e);
388464562Sgshapiro# if _FFR_TLS_1
388564562Sgshapiro		define(macid("{cn_subject}", NULL), "", e);
388664562Sgshapiro		define(macid("{cn_issuer}", NULL), "", e);
388764562Sgshapiro# endif /* _FFR_TLS_1 */
388864562Sgshapiro	}
388964562Sgshapiro	switch(SSL_get_verify_result(ssl))
389064562Sgshapiro	{
389164562Sgshapiro	  case X509_V_OK:
389264562Sgshapiro		if (cert != NULL)
389364562Sgshapiro		{
389464562Sgshapiro			s = "OK";
389564562Sgshapiro			r = TLS_AUTH_OK;
389664562Sgshapiro		}
389764562Sgshapiro		else
389864562Sgshapiro		{
389964562Sgshapiro			s = "NO";
390064562Sgshapiro			r = TLS_AUTH_NO;
390164562Sgshapiro		}
390264562Sgshapiro		break;
390364562Sgshapiro	  default:
390464562Sgshapiro		s = "FAIL";
390564562Sgshapiro		r = TLS_AUTH_FAIL;
390664562Sgshapiro		break;
390764562Sgshapiro	}
390864562Sgshapiro	define(macid("{verify}", NULL), newstr(s), e);
390964562Sgshapiro	if (cert != NULL)
391064562Sgshapiro		X509_free(cert);
391164562Sgshapiro
391264562Sgshapiro	/* do some logging */
391364562Sgshapiro	if (LogLevel > 9)
391464562Sgshapiro	{
391564562Sgshapiro		char *vers, *s1, *s2, *bits;
391664562Sgshapiro
391764562Sgshapiro		vers = macvalue(macid("{tls_version}", NULL), e);
391864562Sgshapiro		bits = macvalue(macid("{cipher_bits}", NULL), e);
391964562Sgshapiro		s1 = macvalue(macid("{verify}", NULL), e);
392064562Sgshapiro		s2 = macvalue(macid("{cipher}", NULL), e);
392164562Sgshapiro		sm_syslog(LOG_INFO, NOQID,
392264562Sgshapiro			  "TLS: connection %s %.64s, version=%.16s, verify=%.16s, cipher=%.64s, bits=%.6s",
392364562Sgshapiro			  srv ? "from" : "to",
392464562Sgshapiro			  host == NULL ? "none" : host,
392564562Sgshapiro			  vers == NULL ? "none" : vers,
392664562Sgshapiro			  s1 == NULL ? "none" : s1,
392764562Sgshapiro			  s2 == NULL ? "none" : s2,
392864562Sgshapiro			  bits == NULL ? "0" : bits);
392964562Sgshapiro		if (LogLevel > 11)
393064562Sgshapiro		{
393164562Sgshapiro			/*
393264562Sgshapiro			**  maybe run xuntextify on the strings?
393364562Sgshapiro			**  that is easier to read but makes it maybe a bit
393464562Sgshapiro			**  more complicated to figure out the right values
393564562Sgshapiro			**  for the access map...
393664562Sgshapiro			*/
393764562Sgshapiro			s1 = macvalue(macid("{cert_subject}", NULL), e);
393864562Sgshapiro			s2 = macvalue(macid("{cert_issuer}", NULL), e);
393964562Sgshapiro			sm_syslog(LOG_INFO, NOQID,
394064562Sgshapiro				  "TLS: %s cert subject:%.128s, cert issuer=%.128s",
394164562Sgshapiro				  srv ? "client" : "server",
394264562Sgshapiro				  s1 == NULL ? "none" : s1,
394364562Sgshapiro				  s2 == NULL ? "none" : s2);
394464562Sgshapiro		}
394564562Sgshapiro	}
394664562Sgshapiro
394764562Sgshapiro	return r;
394864562Sgshapiro}
394964562Sgshapiro
395064562Sgshapiro# if !TLS_NO_RSA
395164562Sgshapiro/*
395264562Sgshapiro**  TMP_RSA_KEY -- return temporary RSA key
395364562Sgshapiro**
395464562Sgshapiro**	Parameters:
395564562Sgshapiro**		s -- SSL connection structure
395664562Sgshapiro**		export --
395764562Sgshapiro**		keylength --
395864562Sgshapiro**
395964562Sgshapiro**	Returns:
396064562Sgshapiro**		temporary RSA key.
396164562Sgshapiro*/
396264562Sgshapiro
396364562Sgshapiro/* ARGUSED0 */
396464562Sgshapirostatic RSA *
396564562Sgshapirotmp_rsa_key(s, export, keylength)
396664562Sgshapiro	SSL *s;
396764562Sgshapiro	int export;
396864562Sgshapiro	int keylength;
396964562Sgshapiro{
397064562Sgshapiro	return rsa_tmp;
397164562Sgshapiro}
397264562Sgshapiro# endif /* !TLS_NO_RSA */
397364562Sgshapiro/*
397464562Sgshapiro**  APPS_SSL_INFO_CB -- info callback for TLS connections
397564562Sgshapiro**
397664562Sgshapiro**	Parameters:
397764562Sgshapiro**		s -- SSL connection structure
397864562Sgshapiro**		where --
397964562Sgshapiro**		ret --
398064562Sgshapiro**
398164562Sgshapiro**	Returns:
398264562Sgshapiro**		none.
398364562Sgshapiro*/
398464562Sgshapiro
398564562Sgshapirovoid
398664562Sgshapiroapps_ssl_info_cb(s, where, ret)
398764562Sgshapiro	SSL *s;
398864562Sgshapiro	int where;
398964562Sgshapiro	int ret;
399064562Sgshapiro{
399164562Sgshapiro	char *str;
399264562Sgshapiro	int w;
399364562Sgshapiro	BIO *bio_err = NULL;
399464562Sgshapiro
399564562Sgshapiro	if (LogLevel > 14)
399664562Sgshapiro		sm_syslog(LOG_INFO, NOQID,
399764562Sgshapiro			  "info_callback where 0x%x ret %d", where, ret);
399864562Sgshapiro
399964562Sgshapiro	w = where & ~SSL_ST_MASK;
400064562Sgshapiro	if (bio_err == NULL)
400164562Sgshapiro		bio_err = BIO_new_fp(stderr, BIO_NOCLOSE);
400264562Sgshapiro
400364562Sgshapiro	if (w & SSL_ST_CONNECT)
400464562Sgshapiro		str = "SSL_connect";
400564562Sgshapiro	else if (w & SSL_ST_ACCEPT)
400664562Sgshapiro		str = "SSL_accept";
400764562Sgshapiro	else
400864562Sgshapiro		str = "undefined";
400964562Sgshapiro
401064562Sgshapiro	if (where & SSL_CB_LOOP)
401164562Sgshapiro	{
401264562Sgshapiro		if (LogLevel > 12)
401364562Sgshapiro			sm_syslog(LOG_NOTICE, NOQID,
401464562Sgshapiro			"%s:%s\n", str, SSL_state_string_long(s));
401564562Sgshapiro	}
401664562Sgshapiro	else if (where & SSL_CB_ALERT)
401764562Sgshapiro	{
401864562Sgshapiro		str = (where & SSL_CB_READ) ? "read" : "write";
401964562Sgshapiro		if (LogLevel > 12)
402064562Sgshapiro			sm_syslog(LOG_NOTICE, NOQID,
402164562Sgshapiro		"SSL3 alert %s:%s:%s\n",
402264562Sgshapiro			   str, SSL_alert_type_string_long(ret),
402364562Sgshapiro			   SSL_alert_desc_string_long(ret));
402464562Sgshapiro	}
402564562Sgshapiro	else if (where & SSL_CB_EXIT)
402664562Sgshapiro	{
402764562Sgshapiro		if (ret == 0)
402864562Sgshapiro		{
402964562Sgshapiro			if (LogLevel > 7)
403064562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
403164562Sgshapiro					"%s:failed in %s\n",
403264562Sgshapiro					str, SSL_state_string_long(s));
403364562Sgshapiro		}
403464562Sgshapiro		else if (ret < 0)
403564562Sgshapiro		{
403664562Sgshapiro			if (LogLevel > 7)
403764562Sgshapiro				sm_syslog(LOG_WARNING, NOQID,
403864562Sgshapiro					"%s:error in %s\n",
403964562Sgshapiro					str, SSL_state_string_long(s));
404064562Sgshapiro		}
404164562Sgshapiro	}
404264562Sgshapiro}
404364562Sgshapiro/*
404464562Sgshapiro**  TLS_VERIFY_LOG -- log verify error for TLS certificates
404564562Sgshapiro**
404664562Sgshapiro**	Parameters:
404764562Sgshapiro**		ok -- verify ok?
404864562Sgshapiro**		ctx -- x509 context
404964562Sgshapiro**
405064562Sgshapiro**	Returns:
405164562Sgshapiro**		0 -- fatal error
405264562Sgshapiro**		1 -- ok
405364562Sgshapiro*/
405464562Sgshapiro
405564562Sgshapirostatic int
405664562Sgshapirotls_verify_log(ok, ctx)
405764562Sgshapiro	int ok;
405864562Sgshapiro	X509_STORE_CTX *ctx;
405964562Sgshapiro{
406064562Sgshapiro	SSL *ssl;
406164562Sgshapiro	X509 *cert;
406264562Sgshapiro	int reason, depth;
406364562Sgshapiro	char buf[512];
406464562Sgshapiro
406564562Sgshapiro	cert = X509_STORE_CTX_get_current_cert(ctx);
406664562Sgshapiro	reason = X509_STORE_CTX_get_error(ctx);
406764562Sgshapiro	depth = X509_STORE_CTX_get_error_depth(ctx);
406864562Sgshapiro	ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx,
406964562Sgshapiro			SSL_get_ex_data_X509_STORE_CTX_idx());
407064562Sgshapiro
407164562Sgshapiro	if (ssl == NULL)
407264562Sgshapiro	{
407364562Sgshapiro		/* internal error */
407464562Sgshapiro		sm_syslog(LOG_ERR, NOQID,
407564562Sgshapiro			  "TLS: internal error: tls_verify_cb: ssl == NULL");
407664562Sgshapiro		return 0;
407764562Sgshapiro	}
407864562Sgshapiro
407964562Sgshapiro	X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof buf);
408064562Sgshapiro	sm_syslog(LOG_INFO, NOQID,
408164562Sgshapiro		  "TLS cert verify: depth=%d %s, state=%d, reason=%s\n",
408264562Sgshapiro		  depth, buf, ok, X509_verify_cert_error_string(reason));
408364562Sgshapiro	return 1;
408464562Sgshapiro}
408564562Sgshapiro
408664562Sgshapiro/*
408764562Sgshapiro**  TLS_VERIFY_CB -- verify callback for TLS certificates
408864562Sgshapiro**
408964562Sgshapiro**	Parameters:
409064562Sgshapiro**		ctx -- x509 context
409164562Sgshapiro**
409264562Sgshapiro**	Returns:
409364562Sgshapiro**		accept connection?
409464562Sgshapiro**		currently: always yes.
409564562Sgshapiro*/
409664562Sgshapiro
409764562Sgshapirostatic int
409864562Sgshapirotls_verify_cb(ctx)
409964562Sgshapiro	X509_STORE_CTX *ctx;
410064562Sgshapiro{
410164562Sgshapiro	int ok;
410264562Sgshapiro
410364562Sgshapiro	ok = X509_verify_cert(ctx);
410464562Sgshapiro	if (ok == 0)
410564562Sgshapiro	{
410664562Sgshapiro		if (LogLevel > 13)
410764562Sgshapiro			return tls_verify_log(ok, ctx);
410864562Sgshapiro		return 1;	/* override it */
410964562Sgshapiro	}
411064562Sgshapiro	return ok;
411164562Sgshapiro}
411264562Sgshapiro
411364562Sgshapiro
411464562Sgshapiro/*
411564562Sgshapiro**  TLSLOGERR -- log the errors from the TLS error stack
411664562Sgshapiro**
411764562Sgshapiro**	Parameters:
411864562Sgshapiro**		none.
411964562Sgshapiro**
412064562Sgshapiro**	Returns:
412164562Sgshapiro**		none.
412264562Sgshapiro*/
412364562Sgshapiro
412464562Sgshapirovoid
412564562Sgshapirotlslogerr()
412664562Sgshapiro{
412764562Sgshapiro	unsigned long l;
412864562Sgshapiro	int line, flags;
412964562Sgshapiro	unsigned long es;
413064562Sgshapiro	char *file, *data;
413164562Sgshapiro	char buf[256];
413264562Sgshapiro#define CP (const char **)
413364562Sgshapiro
413464562Sgshapiro	es = CRYPTO_thread_id();
413564562Sgshapiro	while ((l = ERR_get_error_line_data(CP &file, &line, CP &data, &flags))
413664562Sgshapiro		!= 0)
413764562Sgshapiro	{
413864562Sgshapiro		sm_syslog(LOG_WARNING, NOQID,
413964562Sgshapiro			 "TLS: %lu:%s:%s:%d:%s\n", es, ERR_error_string(l, buf),
414064562Sgshapiro			 file, line, (flags & ERR_TXT_STRING) ? data : "");
414164562Sgshapiro	}
414264562Sgshapiro}
414364562Sgshapiro
414464562Sgshapiro# endif /* STARTTLS */
414564562Sgshapiro#endif /* SMTP */
414664562Sgshapiro/*
414738032Speter**  HELP -- implement the HELP command.
414838032Speter**
414938032Speter**	Parameters:
415038032Speter**		topic -- the topic we want help for.
415164562Sgshapiro**		e -- envelope
415238032Speter**
415338032Speter**	Returns:
415438032Speter**		none.
415538032Speter**
415638032Speter**	Side Effects:
415738032Speter**		outputs the help file to message output.
415838032Speter*/
415964562Sgshapiro#define HELPVSTR	"#vers	"
416064562Sgshapiro#define HELPVERSION	2
416138032Speter
416238032Spetervoid
416364562Sgshapirohelp(topic, e)
416438032Speter	char *topic;
416564562Sgshapiro	ENVELOPE *e;
416638032Speter{
416738032Speter	register FILE *hf;
416864562Sgshapiro	register char *p;
416938032Speter	int len;
417038032Speter	bool noinfo;
417164562Sgshapiro	bool first = TRUE;
417264562Sgshapiro	long sff = SFF_OPENASROOT|SFF_REGONLY;
417338032Speter	char buf[MAXLINE];
417464562Sgshapiro	char inp[MAXLINE];
417564562Sgshapiro	static int foundvers = -1;
417638032Speter	extern char Version[];
417738032Speter
417838032Speter	if (DontLockReadFiles)
417938032Speter		sff |= SFF_NOLOCK;
418064562Sgshapiro	if (!bitnset(DBS_HELPFILEINUNSAFEDIRPATH, DontBlameSendmail))
418138032Speter		sff |= SFF_SAFEDIRPATH;
418238032Speter
418338032Speter	if (HelpFile == NULL ||
418438032Speter	    (hf = safefopen(HelpFile, O_RDONLY, 0444, sff)) == NULL)
418538032Speter	{
418638032Speter		/* no help */
418738032Speter		errno = 0;
418864562Sgshapiro		message("502 5.3.0 Sendmail %s -- HELP not implemented",
418964562Sgshapiro			Version);
419038032Speter		return;
419138032Speter	}
419238032Speter
419338032Speter	if (topic == NULL || *topic == '\0')
419438032Speter	{
419538032Speter		topic = "smtp";
419638032Speter		noinfo = FALSE;
419738032Speter	}
419838032Speter	else
419938032Speter	{
420038032Speter		makelower(topic);
420138032Speter		noinfo = TRUE;
420238032Speter	}
420338032Speter
420438032Speter	len = strlen(topic);
420538032Speter
420638032Speter	while (fgets(buf, sizeof buf, hf) != NULL)
420738032Speter	{
420864562Sgshapiro		if (buf[0] == '#')
420964562Sgshapiro		{
421064562Sgshapiro			if (foundvers < 0 &&
421164562Sgshapiro			    strncmp(buf, HELPVSTR, strlen(HELPVSTR)) == 0)
421264562Sgshapiro			{
421364562Sgshapiro				int h;
421464562Sgshapiro
421564562Sgshapiro				if (sscanf(buf + strlen(HELPVSTR), "%d",
421664562Sgshapiro					   &h) == 1)
421764562Sgshapiro					foundvers = h;
421864562Sgshapiro			}
421964562Sgshapiro			continue;
422064562Sgshapiro		}
422138032Speter		if (strncmp(buf, topic, len) == 0)
422238032Speter		{
422364562Sgshapiro			if (first)
422464562Sgshapiro			{
422564562Sgshapiro				first = FALSE;
422638032Speter
422764562Sgshapiro				/* print version if no/old vers# in file */
422864562Sgshapiro				if (foundvers < 2 && !noinfo)
422964562Sgshapiro					message("214-2.0.0 This is Sendmail version %s", Version);
423064562Sgshapiro			}
423164562Sgshapiro			p = strpbrk(buf, " \t");
423238032Speter			if (p == NULL)
423364562Sgshapiro				p = buf + strlen(buf) - 1;
423438032Speter			else
423538032Speter				p++;
423638032Speter			fixcrlf(p, TRUE);
423764562Sgshapiro			if (foundvers >= 2)
423864562Sgshapiro			{
423964562Sgshapiro				translate_dollars(p);
424064562Sgshapiro				expand(p, inp, sizeof inp, e);
424164562Sgshapiro				p = inp;
424264562Sgshapiro			}
424364562Sgshapiro			message("214-2.0.0 %s", p);
424438032Speter			noinfo = FALSE;
424538032Speter		}
424638032Speter	}
424738032Speter
424838032Speter	if (noinfo)
424964562Sgshapiro		message("504 5.3.0 HELP topic \"%.10s\" unknown", topic);
425038032Speter	else
425164562Sgshapiro		message("214 2.0.0 End of HELP info");
425264562Sgshapiro
425364562Sgshapiro	if (foundvers != 0 && foundvers < HELPVERSION)
425464562Sgshapiro	{
425564562Sgshapiro		if (LogLevel > 1)
425664562Sgshapiro			sm_syslog(LOG_WARNING, e->e_id,
425764562Sgshapiro				  "%s too old (require version %d)",
425864562Sgshapiro				  HelpFile, HELPVERSION);
425964562Sgshapiro
426064562Sgshapiro		/* avoid log next time */
426164562Sgshapiro		foundvers = 0;
426264562Sgshapiro	}
426364562Sgshapiro
426438032Speter	(void) fclose(hf);
426538032Speter}
4266