xref: /openbsd-src/usr.sbin/smtpd/smtp_session.c (revision 4768c36c49be8e901b3f58f0411358a458e95e6a)
1*4768c36cSeric /*	$OpenBSD: smtp_session.c,v 1.329 2018/05/04 10:49:49 eric Exp $	*/
21f3c2bcfSsobrado 
33ef9cbf7Sgilles /*
465c4fdfbSgilles  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
53ef9cbf7Sgilles  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6b8989c65Sjacekm  * Copyright (c) 2008-2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
7b04e6f27Seric  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
83ef9cbf7Sgilles  *
93ef9cbf7Sgilles  * Permission to use, copy, modify, and distribute this software for any
103ef9cbf7Sgilles  * purpose with or without fee is hereby granted, provided that the above
113ef9cbf7Sgilles  * copyright notice and this permission notice appear in all copies.
123ef9cbf7Sgilles  *
133ef9cbf7Sgilles  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
143ef9cbf7Sgilles  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
153ef9cbf7Sgilles  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
163ef9cbf7Sgilles  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
173ef9cbf7Sgilles  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
183ef9cbf7Sgilles  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
193ef9cbf7Sgilles  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
203ef9cbf7Sgilles  */
213ef9cbf7Sgilles 
223ef9cbf7Sgilles #include <sys/types.h>
233ef9cbf7Sgilles #include <sys/queue.h>
243ef9cbf7Sgilles #include <sys/tree.h>
253ef9cbf7Sgilles #include <sys/socket.h>
2665c4fdfbSgilles #include <sys/uio.h>
273ef9cbf7Sgilles 
283ef9cbf7Sgilles #include <netinet/in.h>
299e096db0Sgilles 
303ef9cbf7Sgilles #include <ctype.h>
3182614934Seric #include <errno.h>
323ef9cbf7Sgilles #include <event.h>
335eb8dddaSgilles #include <imsg.h>
34b9fc9a72Sderaadt #include <limits.h>
3582614934Seric #include <inttypes.h>
3665c4fdfbSgilles #include <openssl/ssl.h>
370767a313Sgilles #include <resolv.h>
383ef9cbf7Sgilles #include <stdio.h>
393ef9cbf7Sgilles #include <stdlib.h>
40a2bd1240Schl #include <string.h>
413ef9cbf7Sgilles #include <unistd.h>
4282614934Seric #include <vis.h>
433ef9cbf7Sgilles 
443ef9cbf7Sgilles #include "smtpd.h"
455eb8dddaSgilles #include "log.h"
4665c4fdfbSgilles #include "ssl.h"
473ef9cbf7Sgilles 
486162865dSeric #define	SMTP_LINE_MAX			16384
49b34f8648Sgilles #define	DATA_HIWAT			65535
506162865dSeric #define	APPEND_DOMAIN_BUFFER_SIZE	SMTP_LINE_MAX
5127b32957Sgilles 
5265c4fdfbSgilles enum smtp_state {
5365c4fdfbSgilles 	STATE_NEW = 0,
5465c4fdfbSgilles 	STATE_CONNECTED,
5565c4fdfbSgilles 	STATE_TLS,
5665c4fdfbSgilles 	STATE_HELO,
5765c4fdfbSgilles 	STATE_AUTH_INIT,
5865c4fdfbSgilles 	STATE_AUTH_USERNAME,
5965c4fdfbSgilles 	STATE_AUTH_PASSWORD,
6065c4fdfbSgilles 	STATE_AUTH_FINALIZE,
6165c4fdfbSgilles 	STATE_BODY,
6265c4fdfbSgilles 	STATE_QUIT,
6365c4fdfbSgilles };
6465c4fdfbSgilles 
6565c4fdfbSgilles enum session_flags {
6665c4fdfbSgilles 	SF_EHLO			= 0x0001,
6765c4fdfbSgilles 	SF_8BITMIME		= 0x0002,
6865c4fdfbSgilles 	SF_SECURE		= 0x0004,
6965c4fdfbSgilles 	SF_AUTHENTICATED	= 0x0008,
7065c4fdfbSgilles 	SF_BOUNCE		= 0x0010,
71b78cbe77Sgilles 	SF_VERIFIED		= 0x0020,
72b78cbe77Sgilles 	SF_BADINPUT		= 0x0080,
7365c4fdfbSgilles };
7465c4fdfbSgilles 
75f299ff90Seric enum {
76f299ff90Seric 	TX_OK = 0,
77f299ff90Seric 	TX_ERROR_ENVELOPE,
78f299ff90Seric 	TX_ERROR_SIZE,
79f299ff90Seric 	TX_ERROR_IO,
80f299ff90Seric 	TX_ERROR_LOOP,
81f299ff90Seric 	TX_ERROR_MALFORMED,
82f299ff90Seric 	TX_ERROR_RESOURCES
8365c4fdfbSgilles };
8465c4fdfbSgilles 
8565c4fdfbSgilles enum smtp_command {
8665c4fdfbSgilles 	CMD_HELO = 0,
8765c4fdfbSgilles 	CMD_EHLO,
8865c4fdfbSgilles 	CMD_STARTTLS,
8965c4fdfbSgilles 	CMD_AUTH,
9065c4fdfbSgilles 	CMD_MAIL_FROM,
9165c4fdfbSgilles 	CMD_RCPT_TO,
9265c4fdfbSgilles 	CMD_DATA,
9365c4fdfbSgilles 	CMD_RSET,
9465c4fdfbSgilles 	CMD_QUIT,
9565c4fdfbSgilles 	CMD_HELP,
96fe95d8d0Seric 	CMD_WIZ,
9765c4fdfbSgilles 	CMD_NOOP,
9865c4fdfbSgilles };
9965c4fdfbSgilles 
100ee3e6bebSeric struct smtp_rcpt {
101ee3e6bebSeric 	TAILQ_ENTRY(smtp_rcpt)	 entry;
102ee3e6bebSeric  	struct mailaddr		 maddr;
103ee3e6bebSeric 	size_t			 destcount;
104ee3e6bebSeric };
105ee3e6bebSeric 
1060a80c58aSeric struct smtp_tx {
1070a80c58aSeric 	struct smtp_session	*session;
108763ff2e3Seric 	uint32_t		 msgid;
1090a80c58aSeric 
1100a80c58aSeric 	struct envelope		 evp;
1110a80c58aSeric 	size_t			 rcptcount;
1120a80c58aSeric 	size_t			 destcount;
1130a80c58aSeric 	TAILQ_HEAD(, smtp_rcpt)	 rcpts;
1140a80c58aSeric 
115d7b42ce8Seric 	time_t			 time;
116f299ff90Seric 	int			 error;
117f71caa29Seric 	size_t			 datain;
1180a80c58aSeric 	size_t			 odatalen;
11984639b21Seric 	FILE			*ofile;
1200a80c58aSeric 	int			 hdrdone;
1210a80c58aSeric 	int			 rcvcount;
1220a80c58aSeric 	int			 skiphdr;
1230a80c58aSeric 	struct rfc2822_parser	 rfc2822_parser;
1240a80c58aSeric };
1250a80c58aSeric 
12665c4fdfbSgilles struct smtp_session {
12765c4fdfbSgilles 	uint64_t		 id;
1288d3f7f0dSeric 	struct io		*io;
12965c4fdfbSgilles 	struct listener		*listener;
1301c3ac238Seric 	void			*ssl_ctx;
13165c4fdfbSgilles 	struct sockaddr_storage	 ss;
132953aae25Sderaadt 	char			 hostname[HOST_NAME_MAX+1];
133953aae25Sderaadt 	char			 smtpname[HOST_NAME_MAX+1];
13465c4fdfbSgilles 
13565c4fdfbSgilles 	int			 flags;
13665c4fdfbSgilles 	enum smtp_state		 state;
13765c4fdfbSgilles 
138953aae25Sderaadt 	char			 helo[LINE_MAX];
139953aae25Sderaadt 	char			 cmd[LINE_MAX];
1406dfe9135Sgilles 	char			 username[SMTPD_MAXMAILADDRSIZE];
14165c4fdfbSgilles 
14265c4fdfbSgilles 	size_t			 mailcount;
143299c4efeSeric 	struct event		 pause;
1446bc0eb6bSgilles 
1450a80c58aSeric 	struct smtp_tx		*tx;
14665c4fdfbSgilles };
1472a4d7b1bSeric 
148f4807aa6Seric #define ADVERTISE_TLS(s) \
14965c4fdfbSgilles 	((s)->listener->flags & F_STARTTLS && !((s)->flags & SF_SECURE))
150f4807aa6Seric 
151f4807aa6Seric #define ADVERTISE_AUTH(s) \
15265c4fdfbSgilles 	((s)->listener->flags & F_AUTH && (s)->flags & SF_SECURE && \
15365c4fdfbSgilles 	 !((s)->flags & SF_AUTHENTICATED))
154f4807aa6Seric 
155ad286364Seric #define ADVERTISE_EXT_DSN(s) \
156ad286364Seric 	((s)->listener->flags & F_EXT_DSN)
157ad286364Seric 
158cc81b7c6Seric static int smtp_mailaddr(struct mailaddr *, char *, int, char **, const char *);
15965c4fdfbSgilles static void smtp_session_init(void);
1601c3ac238Seric static int smtp_lookup_servername(struct smtp_session *);
16165c4fdfbSgilles static void smtp_connected(struct smtp_session *);
162cc81b7c6Seric static void smtp_send_banner(struct smtp_session *);
1631ddc3febSeric static void smtp_tls_verified(struct smtp_session *);
164b556a8d3Seric static void smtp_io(struct io *, int, void *);
16565c4fdfbSgilles static void smtp_enter_state(struct smtp_session *, int);
16665c4fdfbSgilles static void smtp_reply(struct smtp_session *, char *, ...);
16765c4fdfbSgilles static void smtp_command(struct smtp_session *, char *);
16865c4fdfbSgilles static int smtp_parse_mail_args(struct smtp_session *, char *);
169fe95d8d0Seric static int smtp_parse_rcpt_args(struct smtp_session *, char *);
17065c4fdfbSgilles static void smtp_rfc4954_auth_plain(struct smtp_session *, char *);
17165c4fdfbSgilles static void smtp_rfc4954_auth_login(struct smtp_session *, char *);
17265c4fdfbSgilles static void smtp_free(struct smtp_session *, const char *);
17365c4fdfbSgilles static const char *smtp_strstate(int);
1747627c963Seric static void smtp_tls_init(struct smtp_session *);
17565c4fdfbSgilles static int smtp_verify_certificate(struct smtp_session *);
176299c4efeSeric static void smtp_auth_failure_pause(struct smtp_session *);
177299c4efeSeric static void smtp_auth_failure_resume(int, short, void *);
178b04e6f27Seric 
1790a80c58aSeric static int  smtp_tx(struct smtp_session *);
1800a80c58aSeric static void smtp_tx_free(struct smtp_tx *);
181008f3f72Seric static void smtp_tx_create_message(struct smtp_tx *);
18272cde3cbSeric static void smtp_tx_mail_from(struct smtp_tx *, char *);
18372cde3cbSeric static void smtp_tx_rcpt_to(struct smtp_tx *, char *);
184008f3f72Seric static void smtp_tx_open_message(struct smtp_tx *);
185008f3f72Seric static void smtp_tx_commit(struct smtp_tx *);
186008f3f72Seric static void smtp_tx_rollback(struct smtp_tx *);
187008f3f72Seric static int  smtp_tx_dataline(struct smtp_tx *, const char *);
18855042ccbSeric static void smtp_message_fd(struct smtp_tx *, int);
18955042ccbSeric static void smtp_message_end(struct smtp_tx *);
19055042ccbSeric static int  smtp_message_printf(struct smtp_tx *, const char *, ...);
19108becfa3Seric 
19265c4fdfbSgilles static struct { int code; const char *cmd; } commands[] = {
19365c4fdfbSgilles 	{ CMD_HELO,		"HELO" },
19465c4fdfbSgilles 	{ CMD_EHLO,		"EHLO" },
19565c4fdfbSgilles 	{ CMD_STARTTLS,		"STARTTLS" },
19665c4fdfbSgilles 	{ CMD_AUTH,		"AUTH" },
19765c4fdfbSgilles 	{ CMD_MAIL_FROM,	"MAIL FROM" },
19865c4fdfbSgilles 	{ CMD_RCPT_TO,		"RCPT TO" },
19965c4fdfbSgilles 	{ CMD_DATA,		"DATA" },
20065c4fdfbSgilles 	{ CMD_RSET,		"RSET" },
20165c4fdfbSgilles 	{ CMD_QUIT,		"QUIT" },
20265c4fdfbSgilles 	{ CMD_HELP,		"HELP" },
203fe95d8d0Seric 	{ CMD_WIZ,		"WIZ" },
20465c4fdfbSgilles 	{ CMD_NOOP,		"NOOP" },
20565c4fdfbSgilles 	{ -1, NULL },
2063ef9cbf7Sgilles };
2073ef9cbf7Sgilles 
20865c4fdfbSgilles static struct tree wait_lka_ptr;
209cc81b7c6Seric static struct tree wait_lka_helo;
210a1bd4550Sgilles static struct tree wait_lka_mail;
21165c4fdfbSgilles static struct tree wait_lka_rcpt;
21265c4fdfbSgilles static struct tree wait_parent_auth;
21365c4fdfbSgilles static struct tree wait_queue_msg;
21465c4fdfbSgilles static struct tree wait_queue_fd;
21565c4fdfbSgilles static struct tree wait_queue_commit;
21665c4fdfbSgilles static struct tree wait_ssl_init;
21765c4fdfbSgilles static struct tree wait_ssl_verify;
2183ef9cbf7Sgilles 
21965c4fdfbSgilles static void
2206bc0eb6bSgilles header_default_callback(const struct rfc2822_header *hdr, void *arg)
2216bc0eb6bSgilles {
222eb95ffd1Seric 	struct smtp_tx         *tx = arg;
2236bc0eb6bSgilles 	struct rfc2822_line    *l;
2246bc0eb6bSgilles 
22555042ccbSeric 	if (smtp_message_printf(tx, "%s:", hdr->name) == -1)
2266bc0eb6bSgilles 		return;
2276bc0eb6bSgilles 
2281b33e826Seric 	TAILQ_FOREACH(l, &hdr->lines, next)
22955042ccbSeric 		if (smtp_message_printf(tx, "%s\n", l->buffer) == -1)
2306bc0eb6bSgilles 			return;
2316bc0eb6bSgilles }
2326bc0eb6bSgilles 
2336bc0eb6bSgilles static void
2346bc0eb6bSgilles dataline_callback(const char *line, void *arg)
2356bc0eb6bSgilles {
236eb95ffd1Seric 	struct smtp_tx *tx = arg;
2376bc0eb6bSgilles 
238eb95ffd1Seric 	smtp_message_printf(tx, "%s\n", line);
2396bc0eb6bSgilles }
2406bc0eb6bSgilles 
2416bc0eb6bSgilles static void
2426bc0eb6bSgilles header_bcc_callback(const struct rfc2822_header *hdr, void *arg)
2436bc0eb6bSgilles {
2446bc0eb6bSgilles }
2456bc0eb6bSgilles 
2466bc0eb6bSgilles static void
24727b32957Sgilles header_append_domain_buffer(char *buffer, char *domain, size_t len)
24827b32957Sgilles {
24927b32957Sgilles 	size_t	i;
25027b32957Sgilles 	int	escape, quote, comment, bracket;
25127b32957Sgilles 	int	has_domain, has_bracket, has_group;
25227b32957Sgilles 	int	pos_bracket, pos_component, pos_insert;
25327b32957Sgilles 	char	copy[APPEND_DOMAIN_BUFFER_SIZE];
25427b32957Sgilles 
25527b32957Sgilles 	i = 0;
25627b32957Sgilles 	escape = quote = comment = bracket = 0;
25727b32957Sgilles 	has_domain = has_bracket = has_group = 0;
25827b32957Sgilles 	pos_bracket = pos_insert = pos_component = 0;
25927b32957Sgilles 	for (i = 0; buffer[i]; ++i) {
26027b32957Sgilles 		if (buffer[i] == '(' && !escape && !quote)
26127b32957Sgilles 			comment++;
26227b32957Sgilles 		if (buffer[i] == '"' && !escape && !comment)
26327b32957Sgilles 			quote = !quote;
26427b32957Sgilles 		if (buffer[i] == ')' && !escape && !quote && comment)
26527b32957Sgilles 			comment--;
26627b32957Sgilles 		if (buffer[i] == '\\' && !escape && !comment && !quote)
26727b32957Sgilles 			escape = 1;
26827b32957Sgilles 		else
26927b32957Sgilles 			escape = 0;
27027b32957Sgilles 		if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) {
27127b32957Sgilles 			bracket++;
27227b32957Sgilles 			has_bracket = 1;
27327b32957Sgilles 		}
27427b32957Sgilles 		if (buffer[i] == '>' && !escape && !comment && !quote && bracket) {
27527b32957Sgilles 			bracket--;
27627b32957Sgilles 			pos_bracket = i;
27727b32957Sgilles 		}
27827b32957Sgilles 		if (buffer[i] == '@' && !escape && !comment && !quote)
27927b32957Sgilles 			has_domain = 1;
28027b32957Sgilles 		if (buffer[i] == ':' && !escape && !comment && !quote)
28127b32957Sgilles 			has_group = 1;
282cb2a19a6Sgilles 
283cb2a19a6Sgilles 		/* update insert point if not in comment and not on a whitespace */
284cb2a19a6Sgilles 		if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i]))
28527b32957Sgilles 			pos_component = i;
28627b32957Sgilles 	}
28727b32957Sgilles 
28827b32957Sgilles 	/* parse error, do not attempt to modify */
28927b32957Sgilles 	if (escape || quote || comment || bracket)
29027b32957Sgilles 		return;
29127b32957Sgilles 
29227b32957Sgilles 	/* domain already present, no need to modify */
29327b32957Sgilles 	if (has_domain)
29427b32957Sgilles 		return;
29527b32957Sgilles 
29627b32957Sgilles 	/* address is group, skip */
29727b32957Sgilles 	if (has_group)
29827b32957Sgilles 		return;
29927b32957Sgilles 
30027b32957Sgilles 	/* there's an address between brackets, just append domain */
30127b32957Sgilles 	if (has_bracket) {
30227b32957Sgilles 		pos_bracket--;
303cb2a19a6Sgilles 		while (isspace((unsigned char)buffer[pos_bracket]))
30427b32957Sgilles 			pos_bracket--;
30527b32957Sgilles 		if (buffer[pos_bracket] == '<')
30627b32957Sgilles 			return;
30727b32957Sgilles 		pos_insert = pos_bracket + 1;
30827b32957Sgilles 	}
30927b32957Sgilles 	else {
31027b32957Sgilles 		/* otherwise append address to last component */
31127b32957Sgilles 		pos_insert = pos_component + 1;
31227b32957Sgilles 
31327b32957Sgilles 		/* empty address */
314cb2a19a6Sgilles                 if (buffer[pos_component] == '\0' ||
315cb2a19a6Sgilles 		    isspace((unsigned char)buffer[pos_component]))
31627b32957Sgilles                         return;
31727b32957Sgilles 	}
31827b32957Sgilles 
31927b32957Sgilles 	if (snprintf(copy, sizeof copy, "%.*s@%s%s",
32027b32957Sgilles 		(int)pos_insert, buffer,
32127b32957Sgilles 		domain,
32227b32957Sgilles 		buffer+pos_insert) >= (int)sizeof copy)
32327b32957Sgilles 		return;
32427b32957Sgilles 
32527b32957Sgilles 	memcpy(buffer, copy, len);
32627b32957Sgilles }
32727b32957Sgilles 
32827b32957Sgilles static void
3294d531edbSgilles header_address_rewrite_buffer(char *buffer, const char *address, size_t len)
3304d531edbSgilles {
3314d531edbSgilles 	size_t	i;
3324d531edbSgilles 	int	address_len;
3334d531edbSgilles 	int	escape, quote, comment, bracket;
3344d531edbSgilles 	int	has_bracket, has_group;
3354d531edbSgilles 	int	pos_bracket_beg, pos_bracket_end, pos_component_beg, pos_component_end;
3364d531edbSgilles 	int	insert_beg, insert_end;
3374d531edbSgilles 	char	copy[APPEND_DOMAIN_BUFFER_SIZE];
3384d531edbSgilles 
3394d531edbSgilles 	escape = quote = comment = bracket = 0;
3404d531edbSgilles 	has_bracket = has_group = 0;
3414d531edbSgilles 	pos_bracket_beg = pos_bracket_end = pos_component_beg = pos_component_end = 0;
3424d531edbSgilles 	for (i = 0; buffer[i]; ++i) {
3434d531edbSgilles 		if (buffer[i] == '(' && !escape && !quote)
3444d531edbSgilles 			comment++;
3454d531edbSgilles 		if (buffer[i] == '"' && !escape && !comment)
3464d531edbSgilles 			quote = !quote;
3474d531edbSgilles 		if (buffer[i] == ')' && !escape && !quote && comment)
3484d531edbSgilles 			comment--;
3494d531edbSgilles 		if (buffer[i] == '\\' && !escape && !comment && !quote)
3504d531edbSgilles 			escape = 1;
3514d531edbSgilles 		else
3524d531edbSgilles 			escape = 0;
3534d531edbSgilles 		if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) {
3544d531edbSgilles 			bracket++;
3554d531edbSgilles 			has_bracket = 1;
3564d531edbSgilles 			pos_bracket_beg = i+1;
3574d531edbSgilles 		}
3584d531edbSgilles 		if (buffer[i] == '>' && !escape && !comment && !quote && bracket) {
3594d531edbSgilles 			bracket--;
3604d531edbSgilles 			pos_bracket_end = i;
3614d531edbSgilles 		}
3624d531edbSgilles 		if (buffer[i] == ':' && !escape && !comment && !quote)
3634d531edbSgilles 			has_group = 1;
3644d531edbSgilles 
3654d531edbSgilles 		/* update insert point if not in comment and not on a whitespace */
3664d531edbSgilles 		if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i]))
3674d531edbSgilles 			pos_component_end = i;
3684d531edbSgilles 	}
3694d531edbSgilles 
3704d531edbSgilles 	/* parse error, do not attempt to modify */
3714d531edbSgilles 	if (escape || quote || comment || bracket)
3724d531edbSgilles 		return;
3734d531edbSgilles 
3744d531edbSgilles 	/* address is group, skip */
3754d531edbSgilles 	if (has_group)
3764d531edbSgilles 		return;
3774d531edbSgilles 
3784d531edbSgilles 	/* there's an address between brackets, just replace everything brackets */
3794d531edbSgilles 	if (has_bracket) {
3804d531edbSgilles 		insert_beg = pos_bracket_beg;
3814d531edbSgilles 		insert_end = pos_bracket_end;
3824d531edbSgilles 	}
3834d531edbSgilles 	else {
3844d531edbSgilles 		if (pos_component_end == 0)
3854d531edbSgilles 			pos_component_beg = 0;
3864d531edbSgilles 		else {
3874d531edbSgilles 			for (pos_component_beg = pos_component_end; pos_component_beg >= 0; --pos_component_beg)
3884d531edbSgilles 				if (buffer[pos_component_beg] == ')' || isspace(buffer[pos_component_beg]))
3894d531edbSgilles 					break;
3904d531edbSgilles 			pos_component_beg += 1;
3914d531edbSgilles 			pos_component_end += 1;
3924d531edbSgilles 		}
3934d531edbSgilles 		insert_beg = pos_component_beg;
3944d531edbSgilles 		insert_end = pos_component_end;
3954d531edbSgilles 	}
3964d531edbSgilles 
3974d531edbSgilles 	/* check that masquerade won' t overflow */
3984d531edbSgilles 	address_len = strlen(address);
3994d531edbSgilles 	if (strlen(buffer) - (insert_end - insert_beg) + address_len >= len)
4004d531edbSgilles 		return;
4014d531edbSgilles 
4024d531edbSgilles 	(void)strlcpy(copy, buffer, sizeof copy);
4034d531edbSgilles 	(void)strlcpy(copy+insert_beg, address, sizeof (copy) - insert_beg);
4044d531edbSgilles 	(void)strlcat(copy, buffer+insert_end, sizeof (copy));
4054d531edbSgilles 	memcpy(buffer, copy, len);
4064d531edbSgilles }
4074d531edbSgilles 
4084d531edbSgilles static void
4094ea1d675Seric header_domain_append_callback(const struct rfc2822_header *hdr, void *arg)
4104d531edbSgilles {
411eb95ffd1Seric 	struct smtp_session    *s;
412eb95ffd1Seric 	struct smtp_tx         *tx = arg;
4134d531edbSgilles 	struct rfc2822_line    *l;
4144d531edbSgilles 	size_t			i, j;
4154d531edbSgilles 	int			escape, quote, comment, skip;
4164d531edbSgilles 	char			buffer[APPEND_DOMAIN_BUFFER_SIZE];
4174d531edbSgilles 
418eb95ffd1Seric 	s = tx->session;
41955042ccbSeric 
42055042ccbSeric 	if (smtp_message_printf(tx, "%s:", hdr->name) == -1)
4214d531edbSgilles 		return;
4224d531edbSgilles 
4234d531edbSgilles 	j = 0;
4244d531edbSgilles 	escape = quote = comment = skip = 0;
4254d531edbSgilles 	memset(buffer, 0, sizeof buffer);
4264d531edbSgilles 
4274d531edbSgilles 	TAILQ_FOREACH(l, &hdr->lines, next) {
4284d531edbSgilles 		for (i = 0; i < strlen(l->buffer); ++i) {
4294d531edbSgilles 			if (l->buffer[i] == '(' && !escape && !quote)
4304d531edbSgilles 				comment++;
4314d531edbSgilles 			if (l->buffer[i] == '"' && !escape && !comment)
4324d531edbSgilles 				quote = !quote;
4334d531edbSgilles 			if (l->buffer[i] == ')' && !escape && !quote && comment)
4344d531edbSgilles 				comment--;
4354d531edbSgilles 			if (l->buffer[i] == '\\' && !escape && !comment && !quote)
4364d531edbSgilles 				escape = 1;
4374d531edbSgilles 			else
4384d531edbSgilles 				escape = 0;
4394d531edbSgilles 
4404d531edbSgilles 			/* found a separator, buffer contains a full address */
4414d531edbSgilles 			if (l->buffer[i] == ',' && !escape && !quote && !comment) {
4424d531edbSgilles 				if (!skip && j + strlen(s->listener->hostname) + 1 < sizeof buffer) {
4434d531edbSgilles 					header_append_domain_buffer(buffer, s->listener->hostname, sizeof buffer);
4444ea1d675Seric 					if (s->flags & SF_AUTHENTICATED &&
4454ea1d675Seric 					    s->listener->sendertable[0] &&
4464ea1d675Seric 					    s->listener->flags & F_MASQUERADE &&
4474ea1d675Seric 					    !(strcasecmp(hdr->name, "From")))
44855042ccbSeric 						header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender),
4494d531edbSgilles 						    sizeof buffer);
4504d531edbSgilles 				}
45155042ccbSeric 				if (smtp_message_printf(tx, "%s,", buffer) == -1)
4524d531edbSgilles 					return;
4534d531edbSgilles 				j = 0;
4544d531edbSgilles 				skip = 0;
4554d531edbSgilles 				memset(buffer, 0, sizeof buffer);
4564d531edbSgilles 			}
4574d531edbSgilles 			else {
4584d531edbSgilles 				if (skip) {
45955042ccbSeric 					if (smtp_message_printf(tx, "%c", l->buffer[i]) == -1)
4604d531edbSgilles 						return;
4614d531edbSgilles 				}
4624d531edbSgilles 				else {
4634d531edbSgilles 					buffer[j++] = l->buffer[i];
4644d531edbSgilles 					if (j == sizeof (buffer) - 1) {
46555042ccbSeric 						if (smtp_message_printf(tx, "%s", buffer) == -1)
4664d531edbSgilles 							return;
4674d531edbSgilles 						skip = 1;
4684d531edbSgilles 						j = 0;
4694d531edbSgilles 						memset(buffer, 0, sizeof buffer);
4704d531edbSgilles 					}
4714d531edbSgilles 				}
4724d531edbSgilles 			}
4734d531edbSgilles 		}
4744d531edbSgilles 		if (skip) {
47555042ccbSeric 			if (smtp_message_printf(tx, "\n") == -1)
4764d531edbSgilles 				return;
4774d531edbSgilles 		}
4784d531edbSgilles 		else {
4794d531edbSgilles 			buffer[j++] = '\n';
4804d531edbSgilles 			if (j == sizeof (buffer) - 1) {
48155042ccbSeric 				if (smtp_message_printf(tx, "%s", buffer) == -1)
4824d531edbSgilles 					return;
4834d531edbSgilles 				skip = 1;
4844d531edbSgilles 				j = 0;
4854d531edbSgilles 				memset(buffer, 0, sizeof buffer);
4864d531edbSgilles 			}
4874d531edbSgilles 		}
4884d531edbSgilles 	}
4894d531edbSgilles 
4904d531edbSgilles 	/* end of header, if buffer is not empty we'll process it */
4914d531edbSgilles 	if (buffer[0]) {
4924d531edbSgilles 		if (j + strlen(s->listener->hostname) + 1 < sizeof buffer) {
4934d531edbSgilles 			header_append_domain_buffer(buffer, s->listener->hostname, sizeof buffer);
4944ea1d675Seric 			if (s->flags & SF_AUTHENTICATED &&
4954ea1d675Seric 			    s->listener->sendertable[0] &&
4964ea1d675Seric 			    s->listener->flags & F_MASQUERADE &&
4974ea1d675Seric 			    !(strcasecmp(hdr->name, "From")))
49855042ccbSeric 				header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender),
4994d531edbSgilles 				    sizeof buffer);
5004d531edbSgilles 		}
50155042ccbSeric 		smtp_message_printf(tx, "%s", buffer);
5024d531edbSgilles 	}
5034d531edbSgilles }
5044d531edbSgilles 
5054d531edbSgilles static void
5062e02d062Sgilles header_missing_callback(const char *header, void *arg)
5072e02d062Sgilles {
508eb95ffd1Seric 	struct smtp_tx *tx = arg;
5092e02d062Sgilles 
5101b33e826Seric 	if (strcasecmp(header, "message-id") == 0)
51155042ccbSeric 		smtp_message_printf(tx, "Message-Id: <%016"PRIx64"@%s>\n",
512eb95ffd1Seric 		    generate_uid(), tx->session->listener->hostname);
5137fc7f1aaSgilles 
5141b33e826Seric 	if (strcasecmp(header, "date") == 0)
51555042ccbSeric 		smtp_message_printf(tx, "Date: %s\n", time_to_text(tx->time));
5162e02d062Sgilles }
5172e02d062Sgilles 
5182e02d062Sgilles static void
51965c4fdfbSgilles smtp_session_init(void)
5203ef9cbf7Sgilles {
52165c4fdfbSgilles 	static int	init = 0;
522ef9f3596Sjacekm 
52365c4fdfbSgilles 	if (!init) {
52465c4fdfbSgilles 		tree_init(&wait_lka_ptr);
525cc81b7c6Seric 		tree_init(&wait_lka_helo);
526a1bd4550Sgilles 		tree_init(&wait_lka_mail);
52765c4fdfbSgilles 		tree_init(&wait_lka_rcpt);
52865c4fdfbSgilles 		tree_init(&wait_parent_auth);
52965c4fdfbSgilles 		tree_init(&wait_queue_msg);
53065c4fdfbSgilles 		tree_init(&wait_queue_fd);
53165c4fdfbSgilles 		tree_init(&wait_queue_commit);
53265c4fdfbSgilles 		tree_init(&wait_ssl_init);
53365c4fdfbSgilles 		tree_init(&wait_ssl_verify);
53465c4fdfbSgilles 		init = 1;
53565c4fdfbSgilles 	}
5363ef9cbf7Sgilles }
5373ef9cbf7Sgilles 
53865c4fdfbSgilles int
53965c4fdfbSgilles smtp_session(struct listener *listener, int sock,
54065c4fdfbSgilles     const struct sockaddr_storage *ss, const char *hostname)
5413ef9cbf7Sgilles {
54265c4fdfbSgilles 	struct smtp_session	*s;
5433ef9cbf7Sgilles 
54465c4fdfbSgilles 	log_debug("debug: smtp: new client on listener: %p", listener);
54565c4fdfbSgilles 
54665c4fdfbSgilles 	smtp_session_init();
54765c4fdfbSgilles 
54865c4fdfbSgilles 	if ((s = calloc(1, sizeof(*s))) == NULL)
54965c4fdfbSgilles 		return (-1);
5500a80c58aSeric 
55165c4fdfbSgilles 	s->id = generate_uid();
55265c4fdfbSgilles 	s->listener = listener;
55365c4fdfbSgilles 	memmove(&s->ss, ss, sizeof(*ss));
5548d3f7f0dSeric 	s->io = io_new();
5558d3f7f0dSeric 	io_set_callback(s->io, smtp_io, s);
5568d3f7f0dSeric 	io_set_fd(s->io, sock);
5578d3f7f0dSeric 	io_set_timeout(s->io, SMTPD_SESSION_TIMEOUT * 1000);
5588d3f7f0dSeric 	io_set_write(s->io);
55965c4fdfbSgilles 
56065c4fdfbSgilles 	s->state = STATE_NEW;
56165c4fdfbSgilles 
562c51a7fb7Sgilles 	(void)strlcpy(s->smtpname, listener->hostname, sizeof(s->smtpname));
563cc81b7c6Seric 
56465195c54Sgilles 	log_trace(TRACE_SMTP, "smtp: %p: connected to listener %p "
56565195c54Sgilles 	    "[hostname=%s, port=%d, tag=%s]", s, listener,
56665195c54Sgilles 	    listener->hostname, ntohs(listener->port), listener->tag);
56765195c54Sgilles 
56865c4fdfbSgilles 	/* For local enqueueing, the hostname is already set */
56965c4fdfbSgilles 	if (hostname) {
57065c4fdfbSgilles 		s->flags |= SF_AUTHENTICATED;
57165c4fdfbSgilles 		/* A bit of a hack */
57265c4fdfbSgilles 		if (!strcmp(hostname, "localhost"))
57365c4fdfbSgilles 			s->flags |= SF_BOUNCE;
574c51a7fb7Sgilles 		(void)strlcpy(s->hostname, hostname, sizeof(s->hostname));
5751c3ac238Seric 		if (smtp_lookup_servername(s))
57665c4fdfbSgilles 			smtp_connected(s);
57765c4fdfbSgilles 	} else {
578aa1d5973Seric 		m_create(p_lka,  IMSG_SMTP_DNS_PTR, 0, 0, -1);
579aa1d5973Seric 		m_add_id(p_lka, s->id);
580aa1d5973Seric 		m_add_sockaddr(p_lka, (struct sockaddr *)&s->ss);
581aa1d5973Seric 		m_close(p_lka);
58265c4fdfbSgilles 		tree_xset(&wait_lka_ptr, s->id, s);
5837219d653Sjacekm 	}
584ef9f3596Sjacekm 
585cb2a19a6Sgilles 	/* session may have been freed by now */
5866bc0eb6bSgilles 
58765c4fdfbSgilles 	return (0);
5883ef9cbf7Sgilles }
5893ef9cbf7Sgilles 
59065c4fdfbSgilles void
59165c4fdfbSgilles smtp_session_imsg(struct mproc *p, struct imsg *imsg)
59265c4fdfbSgilles {
59365c4fdfbSgilles 	struct ca_cert_resp_msg       	*resp_ca_cert;
59465c4fdfbSgilles 	struct ca_vrfy_resp_msg       	*resp_ca_vrfy;
59565c4fdfbSgilles 	struct smtp_session		*s;
596ee3e6bebSeric 	struct smtp_rcpt		*rcpt;
59765c4fdfbSgilles 	void				*ssl;
598953aae25Sderaadt 	char				 user[LOGIN_NAME_MAX];
59965c4fdfbSgilles 	struct msg			 m;
600cc81b7c6Seric 	const char			*line, *helo;
60165c4fdfbSgilles 	uint64_t			 reqid, evpid;
60208becfa3Seric 	uint32_t			 msgid;
60365c4fdfbSgilles 	int				 status, success, dnserror;
6041c3ac238Seric 	void				*ssl_ctx;
60565c4fdfbSgilles 
60665c4fdfbSgilles 	switch (imsg->hdr.type) {
607aa1d5973Seric 	case IMSG_SMTP_DNS_PTR:
60865c4fdfbSgilles 		m_msg(&m, imsg);
60965c4fdfbSgilles 		m_get_id(&m, &reqid);
61065c4fdfbSgilles 		m_get_int(&m, &dnserror);
61165c4fdfbSgilles 		if (dnserror)
61265c4fdfbSgilles 			line = "<unknown>";
61365c4fdfbSgilles 		else
61465c4fdfbSgilles 			m_get_string(&m, &line);
61565c4fdfbSgilles 		m_end(&m);
61665c4fdfbSgilles 		s = tree_xpop(&wait_lka_ptr, reqid);
6175dc5ee61Sgilles 		(void)strlcpy(s->hostname, line, sizeof s->hostname);
6181c3ac238Seric 		if (smtp_lookup_servername(s))
61965c4fdfbSgilles 			smtp_connected(s);
62065c4fdfbSgilles 		return;
62165c4fdfbSgilles 
62206405042Ssunil 	case IMSG_SMTP_CHECK_SENDER:
62306405042Ssunil 		m_msg(&m, imsg);
62406405042Ssunil 		m_get_id(&m, &reqid);
62506405042Ssunil 		m_get_int(&m, &status);
62606405042Ssunil 		m_end(&m);
62706405042Ssunil 		s = tree_xpop(&wait_lka_mail, reqid);
62806405042Ssunil 		switch (status) {
62906405042Ssunil 		case LKA_OK:
630008f3f72Seric 			smtp_tx_create_message(s->tx);
63106405042Ssunil 			break;
63206405042Ssunil 
63306405042Ssunil 		case LKA_PERMFAIL:
6340490754bSeric 			smtp_tx_free(s->tx);
63506405042Ssunil 			smtp_reply(s, "%d %s", 530, "Sender rejected");
63606405042Ssunil 			break;
63706405042Ssunil 		case LKA_TEMPFAIL:
6380490754bSeric 			smtp_tx_free(s->tx);
63906405042Ssunil 			smtp_reply(s, "421 %s: Temporary Error",
64006405042Ssunil 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
64106405042Ssunil 			break;
64206405042Ssunil 		}
64306405042Ssunil 		return;
64406405042Ssunil 
645aa1d5973Seric 	case IMSG_SMTP_EXPAND_RCPT:
64665c4fdfbSgilles 		m_msg(&m, imsg);
64765c4fdfbSgilles 		m_get_id(&m, &reqid);
64865c4fdfbSgilles 		m_get_int(&m, &status);
649299c4efeSeric 		m_get_string(&m, &line);
65065c4fdfbSgilles 		m_end(&m);
65165c4fdfbSgilles 		s = tree_xpop(&wait_lka_rcpt, reqid);
65265c4fdfbSgilles 		switch (status) {
65365c4fdfbSgilles 		case LKA_OK:
65465c4fdfbSgilles 			fatalx("unexpected ok");
65565c4fdfbSgilles 		case LKA_PERMFAIL:
656299c4efeSeric 			smtp_reply(s, "%s", line);
65765c4fdfbSgilles 			break;
65865c4fdfbSgilles 		case LKA_TEMPFAIL:
659299c4efeSeric 			smtp_reply(s, "%s", line);
66065c4fdfbSgilles 		}
66165c4fdfbSgilles 		return;
66265c4fdfbSgilles 
663aa1d5973Seric 	case IMSG_SMTP_LOOKUP_HELO:
664cc81b7c6Seric 		m_msg(&m, imsg);
665cc81b7c6Seric 		m_get_id(&m, &reqid);
666cc81b7c6Seric 		s = tree_xpop(&wait_lka_helo, reqid);
667cc81b7c6Seric 		m_get_int(&m, &status);
668cc81b7c6Seric 		if (status == LKA_OK) {
669cc81b7c6Seric 			m_get_string(&m, &helo);
670d429e01dSgilles 			(void)strlcpy(s->smtpname, helo, sizeof(s->smtpname));
671cc81b7c6Seric 		}
672cc81b7c6Seric 		m_end(&m);
6731c3ac238Seric 		smtp_connected(s);
674cc81b7c6Seric 		return;
675cc81b7c6Seric 
676aa1d5973Seric 	case IMSG_SMTP_MESSAGE_CREATE:
67765c4fdfbSgilles 		m_msg(&m, imsg);
67865c4fdfbSgilles 		m_get_id(&m, &reqid);
67965c4fdfbSgilles 		m_get_int(&m, &success);
68065c4fdfbSgilles 		s = tree_xpop(&wait_queue_msg, reqid);
68165c4fdfbSgilles 		if (success) {
68265c4fdfbSgilles 			m_get_msgid(&m, &msgid);
683763ff2e3Seric 			s->tx->msgid = msgid;
6840a80c58aSeric 			s->tx->evp.id = msgid_to_evpid(msgid);
6850a80c58aSeric 			s->tx->rcptcount = 0;
686fe95d8d0Seric 			smtp_reply(s, "250 %s: Ok",
687fe95d8d0Seric 			    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
68865c4fdfbSgilles 		} else {
6890490754bSeric 			smtp_tx_free(s->tx);
690fe95d8d0Seric 			smtp_reply(s, "421 %s: Temporary Error",
691fe95d8d0Seric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
692299c4efeSeric 			smtp_enter_state(s, STATE_QUIT);
69365c4fdfbSgilles 		}
69465c4fdfbSgilles 		m_end(&m);
69565c4fdfbSgilles 		return;
69665c4fdfbSgilles 
697aa1d5973Seric 	case IMSG_SMTP_MESSAGE_OPEN:
69865c4fdfbSgilles 		m_msg(&m, imsg);
69965c4fdfbSgilles 		m_get_id(&m, &reqid);
70065c4fdfbSgilles 		m_get_int(&m, &success);
70165c4fdfbSgilles 		m_end(&m);
70265c4fdfbSgilles 
70365c4fdfbSgilles 		s = tree_xpop(&wait_queue_fd, reqid);
704f7c2eb5aSgilles 		if (!success || imsg->fd == -1) {
70565c4fdfbSgilles 			if (imsg->fd != -1)
70665c4fdfbSgilles 				close(imsg->fd);
707fe95d8d0Seric 			smtp_reply(s, "421 %s: Temporary Error",
708fe95d8d0Seric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
70965c4fdfbSgilles 			smtp_enter_state(s, STATE_QUIT);
71065c4fdfbSgilles 			return;
71165c4fdfbSgilles 		}
71265c4fdfbSgilles 
713f7c2eb5aSgilles 		log_debug("smtp: %p: fd %d from queue", s, imsg->fd);
71465c4fdfbSgilles 
71555042ccbSeric 		smtp_message_fd(s->tx, imsg->fd);
71665c4fdfbSgilles 		return;
71765c4fdfbSgilles 
718aa1d5973Seric 	case IMSG_QUEUE_ENVELOPE_SUBMIT:
71965c4fdfbSgilles 		m_msg(&m, imsg);
72065c4fdfbSgilles 		m_get_id(&m, &reqid);
72165c4fdfbSgilles 		m_get_int(&m, &success);
72265c4fdfbSgilles 		s = tree_xget(&wait_lka_rcpt, reqid);
72365c4fdfbSgilles 		if (success) {
72465c4fdfbSgilles 			m_get_evpid(&m, &evpid);
7250a80c58aSeric 			s->tx->destcount++;
72665c4fdfbSgilles 		}
72765c4fdfbSgilles 		else
728f299ff90Seric 			s->tx->error = TX_ERROR_ENVELOPE;
72965c4fdfbSgilles 		m_end(&m);
73065c4fdfbSgilles 		return;
73165c4fdfbSgilles 
732aa1d5973Seric 	case IMSG_QUEUE_ENVELOPE_COMMIT:
73365c4fdfbSgilles 		m_msg(&m, imsg);
73465c4fdfbSgilles 		m_get_id(&m, &reqid);
73565c4fdfbSgilles 		m_get_int(&m, &success);
73665c4fdfbSgilles 		m_end(&m);
73765c4fdfbSgilles 		if (!success)
73865c4fdfbSgilles 			fatalx("commit evp failed: not supposed to happen");
73965c4fdfbSgilles 		s = tree_xpop(&wait_lka_rcpt, reqid);
740f299ff90Seric 		if (s->tx->error) {
74165c4fdfbSgilles 			/*
74265c4fdfbSgilles 			 * If an envelope failed, we can't cancel the last
74365c4fdfbSgilles 			 * RCPT only so we must cancel the whole transaction
74465c4fdfbSgilles 			 * and close the connection.
74565c4fdfbSgilles 			 */
746fe95d8d0Seric 			smtp_reply(s, "421 %s: Temporary failure",
747fe95d8d0Seric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
74865c4fdfbSgilles 			smtp_enter_state(s, STATE_QUIT);
74965c4fdfbSgilles 		}
75065c4fdfbSgilles 		else {
751ee3e6bebSeric 			rcpt = xcalloc(1, sizeof(*rcpt), "smtp_rcpt");
7520a80c58aSeric 			rcpt->destcount = s->tx->destcount;
7530a80c58aSeric 			rcpt->maddr = s->tx->evp.rcpt;
7540a80c58aSeric 			TAILQ_INSERT_TAIL(&s->tx->rcpts, rcpt, entry);
755ee3e6bebSeric 
7560a80c58aSeric 			s->tx->destcount = 0;
7570a80c58aSeric 			s->tx->rcptcount++;
758fe95d8d0Seric 			smtp_reply(s, "250 %s %s: Recipient ok",
759fe95d8d0Seric 			    esc_code(ESC_STATUS_OK, ESC_DESTINATION_ADDRESS_VALID),
760fe95d8d0Seric 			    esc_description(ESC_DESTINATION_ADDRESS_VALID));
76165c4fdfbSgilles 		}
76265c4fdfbSgilles 		return;
76365c4fdfbSgilles 
764aa1d5973Seric 	case IMSG_SMTP_MESSAGE_COMMIT:
76565c4fdfbSgilles 		m_msg(&m, imsg);
76665c4fdfbSgilles 		m_get_id(&m, &reqid);
76765c4fdfbSgilles 		m_get_int(&m, &success);
76865c4fdfbSgilles 		m_end(&m);
76965c4fdfbSgilles 		s = tree_xpop(&wait_queue_commit, reqid);
77065c4fdfbSgilles 		if (!success) {
7710490754bSeric 			smtp_tx_free(s->tx);
772fe95d8d0Seric 			smtp_reply(s, "421 %s: Temporary failure",
773fe95d8d0Seric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
77465c4fdfbSgilles 			smtp_enter_state(s, STATE_QUIT);
77565c4fdfbSgilles 			return;
77665c4fdfbSgilles 		}
77765c4fdfbSgilles 
778fe95d8d0Seric 		smtp_reply(s, "250 %s: %08x Message accepted for delivery",
779fe95d8d0Seric 		    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS),
780763ff2e3Seric 		    s->tx->msgid);
78165c4fdfbSgilles 
7820a80c58aSeric 		TAILQ_FOREACH(rcpt, &s->tx->rcpts, entry) {
783a206140dSgiovanni 			log_info("%016"PRIx64" smtp event=message address=%s host=%s "
784a206140dSgiovanni 			    "msgid=%08x from=<%s%s%s> to=<%s%s%s> size=%zu ndest=%zu proto=%s",
78565c4fdfbSgilles 			    s->id,
786a206140dSgiovanni 			    ss_to_text(&s->ss), s->hostname,
787763ff2e3Seric 			    s->tx->msgid,
7880a80c58aSeric 			    s->tx->evp.sender.user,
7890a80c58aSeric 			    s->tx->evp.sender.user[0] == '\0' ? "" : "@",
7900a80c58aSeric 			    s->tx->evp.sender.domain,
791ee3e6bebSeric 			    rcpt->maddr.user,
792ee3e6bebSeric 			    rcpt->maddr.user[0] == '\0' ? "" : "@",
793ee3e6bebSeric 			    rcpt->maddr.domain,
7940a80c58aSeric 			    s->tx->odatalen,
795ee3e6bebSeric 			    rcpt->destcount,
79665c4fdfbSgilles 			    s->flags & SF_EHLO ? "ESMTP" : "SMTP");
797ee3e6bebSeric 		}
7980490754bSeric 		smtp_tx_free(s->tx);
79965c4fdfbSgilles 		s->mailcount++;
80065c4fdfbSgilles 		smtp_enter_state(s, STATE_HELO);
80165c4fdfbSgilles 		return;
80265c4fdfbSgilles 
803aa1d5973Seric 	case IMSG_SMTP_AUTHENTICATE:
80465c4fdfbSgilles 		m_msg(&m, imsg);
80565c4fdfbSgilles 		m_get_id(&m, &reqid);
80665c4fdfbSgilles 		m_get_int(&m, &success);
80765c4fdfbSgilles 		m_end(&m);
80865c4fdfbSgilles 
80965c4fdfbSgilles 		s = tree_xpop(&wait_parent_auth, reqid);
81065c4fdfbSgilles 		strnvis(user, s->username, sizeof user, VIS_WHITE | VIS_SAFE);
81165c4fdfbSgilles 		if (success == LKA_OK) {
8127a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
813a9c4e50dSgiovanni 			    "event=authentication user=%s address=%s "
814a9c4e50dSgiovanni 			    "host=%s result=ok",
815a9c4e50dSgiovanni 			    s->id, user, ss_to_text(&s->ss), s->hostname);
81665c4fdfbSgilles 			s->flags |= SF_AUTHENTICATED;
817fe95d8d0Seric 			smtp_reply(s, "235 %s: Authentication succeeded",
818fe95d8d0Seric 			    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
81965c4fdfbSgilles 		}
82065c4fdfbSgilles 		else if (success == LKA_PERMFAIL) {
8217a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
822a9c4e50dSgiovanni 			    "event=authentication user=%s address=%s "
823a9c4e50dSgiovanni 			    "host=%s result=permfail",
824a9c4e50dSgiovanni 			    s->id, user, ss_to_text(&s->ss), s->hostname);
825299c4efeSeric 			smtp_auth_failure_pause(s);
826299c4efeSeric 			return;
82765c4fdfbSgilles 		}
82865c4fdfbSgilles 		else if (success == LKA_TEMPFAIL) {
8297a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
830a9c4e50dSgiovanni 			    "event=authentication user=%s address=%s "
831a9c4e50dSgiovanni 			    "host=%s result=tempfail",
832a9c4e50dSgiovanni 			    s->id, user, ss_to_text(&s->ss), s->hostname);
833fe95d8d0Seric 			smtp_reply(s, "421 %s: Temporary failure",
834fe95d8d0Seric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
83565c4fdfbSgilles 		}
83665c4fdfbSgilles 		else
83765c4fdfbSgilles 			fatalx("bad lka response");
838299c4efeSeric 
83965c4fdfbSgilles 		smtp_enter_state(s, STATE_HELO);
84065c4fdfbSgilles 		return;
84165c4fdfbSgilles 
842f80060acSgilles 	case IMSG_SMTP_TLS_INIT:
84365c4fdfbSgilles 		resp_ca_cert = imsg->data;
84465c4fdfbSgilles 		s = tree_xpop(&wait_ssl_init, resp_ca_cert->reqid);
84565c4fdfbSgilles 
84665c4fdfbSgilles 		if (resp_ca_cert->status == CA_FAIL) {
847a206140dSgiovanni 			log_info("%016"PRIx64" smtp event=closed address=%s host=%s "
848a206140dSgiovanni 			    "reason=ca-failure",
849a206140dSgiovanni 			    s->id, ss_to_text(&s->ss), s->hostname);
85065c4fdfbSgilles 			smtp_free(s, "CA failure");
85165c4fdfbSgilles 			return;
85265c4fdfbSgilles 		}
85365c4fdfbSgilles 
85465c4fdfbSgilles 		resp_ca_cert = xmemdup(imsg->data, sizeof *resp_ca_cert, "smtp:ca_cert");
85565c4fdfbSgilles 		resp_ca_cert->cert = xstrdup((char *)imsg->data +
85665c4fdfbSgilles 		    sizeof *resp_ca_cert, "smtp:ca_cert");
857f7aa1c30Sgilles 		ssl_ctx = dict_get(env->sc_ssl_dict, resp_ca_cert->name);
858c3e2e87dSgilles 		ssl = ssl_smtp_init(ssl_ctx, s->listener->flags & F_TLS_VERIFY);
8598d3f7f0dSeric 		io_set_read(s->io);
8608d3f7f0dSeric 		io_start_tls(s->io, ssl);
86165c4fdfbSgilles 
862d914c05cSderaadt 		freezero(resp_ca_cert->cert, resp_ca_cert->cert_len);
86365c4fdfbSgilles 		free(resp_ca_cert);
86465c4fdfbSgilles 		return;
86565c4fdfbSgilles 
866f80060acSgilles 	case IMSG_SMTP_TLS_VERIFY:
86765c4fdfbSgilles 		resp_ca_vrfy = imsg->data;
86865c4fdfbSgilles 		s = tree_xpop(&wait_ssl_verify, resp_ca_vrfy->reqid);
86965c4fdfbSgilles 
87065c4fdfbSgilles 		if (resp_ca_vrfy->status == CA_OK)
87165c4fdfbSgilles 			s->flags |= SF_VERIFIED;
872cc81b7c6Seric 		else if (s->listener->flags & F_TLS_VERIFY) {
8737a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
874a206140dSgiovanni 			    "event=closed address=%s host=%s reason=cert-check-failed",
875a206140dSgiovanni 			    s->id, ss_to_text(&s->ss), s->hostname);
876cc81b7c6Seric 			smtp_free(s, "SSL certificate check failed");
877cc81b7c6Seric 			return;
878cc81b7c6Seric 		}
8791ddc3febSeric 		smtp_tls_verified(s);
880f80a744fSeric 		io_resume(s->io, IO_IN);
8811ddc3febSeric 		return;
8821ddc3febSeric 	}
8831ddc3febSeric 
8841ddc3febSeric 	log_warnx("smtp_session_imsg: unexpected %s imsg",
8851ddc3febSeric 	    imsg_to_str(imsg->hdr.type));
8861ddc3febSeric 	fatalx(NULL);
8871ddc3febSeric }
8881ddc3febSeric 
8891ddc3febSeric static void
8901ddc3febSeric smtp_tls_verified(struct smtp_session *s)
8911ddc3febSeric {
8921ddc3febSeric 	X509 *x;
8934b9ec75fSeric 
8948d3f7f0dSeric 	x = SSL_get_peer_certificate(io_ssl(s->io));
8954b9ec75fSeric 	if (x) {
8964b9ec75fSeric 		log_info("%016"PRIx64" smtp "
8974b9ec75fSeric 		    "event=client-cert-check address=%s host=%s result=\"%s\"",
8984b9ec75fSeric 		    s->id, ss_to_text(&s->ss), s->hostname,
8994b9ec75fSeric 		    (s->flags & SF_VERIFIED) ? "success" : "failure");
9004b9ec75fSeric 		X509_free(x);
9014b9ec75fSeric 	}
9024b9ec75fSeric 
9034b9ec75fSeric 	if (s->listener->flags & F_SMTPS) {
9044b9ec75fSeric 		stat_increment("smtp.smtps", 1);
9058d3f7f0dSeric 		io_set_write(s->io);
9064b9ec75fSeric 		smtp_send_banner(s);
9074b9ec75fSeric 	}
9084b9ec75fSeric 	else {
9094b9ec75fSeric 		stat_increment("smtp.tls", 1);
9104b9ec75fSeric 		smtp_enter_state(s, STATE_HELO);
9114b9ec75fSeric 	}
91265c4fdfbSgilles }
91365c4fdfbSgilles 
91465c4fdfbSgilles static void
915b556a8d3Seric smtp_io(struct io *io, int evt, void *arg)
91665c4fdfbSgilles {
917b556a8d3Seric 	struct smtp_session    *s = arg;
91865c4fdfbSgilles 	char		       *line;
919b251880aSgilles 	size_t			len;
9205bfc3fefSeric 	int			eom;
92165c4fdfbSgilles 
92265c4fdfbSgilles 	log_trace(TRACE_IO, "smtp: %p: %s %s", s, io_strevent(evt),
92365c4fdfbSgilles 	    io_strio(io));
92465c4fdfbSgilles 
92565c4fdfbSgilles 	switch (evt) {
92665c4fdfbSgilles 
92765c4fdfbSgilles 	case IO_TLSREADY:
928a206140dSgiovanni 		log_info("%016"PRIx64" smtp event=starttls address=%s host=%s ciphers=\"%s\"",
9298d3f7f0dSeric 		    s->id, ss_to_text(&s->ss), s->hostname, ssl_to_text(io_ssl(s->io)));
93065c4fdfbSgilles 
93165c4fdfbSgilles 		s->flags |= SF_SECURE;
932b937aab1Seric 		s->helo[0] = '\0';
93365c4fdfbSgilles 
93465c4fdfbSgilles 		if (smtp_verify_certificate(s)) {
935f80a744fSeric 			io_pause(s->io, IO_IN);
93665c4fdfbSgilles 			break;
93765c4fdfbSgilles 		}
93865c4fdfbSgilles 
939cc81b7c6Seric 		if (s->listener->flags & F_TLS_VERIFY) {
9407a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
941a206140dSgiovanni 			    "event=closed address=%s host=%s reason=no-client-cert",
942a206140dSgiovanni 			    s->id, ss_to_text(&s->ss), s->hostname);
943cc81b7c6Seric 			smtp_free(s, "client did not present certificate");
944cc81b7c6Seric 			return;
945cc81b7c6Seric 		}
946cc81b7c6Seric 
9471ddc3febSeric 		smtp_tls_verified(s);
9481ddc3febSeric 		break;
94965c4fdfbSgilles 
95065c4fdfbSgilles 	case IO_DATAIN:
95165c4fdfbSgilles 	    nextline:
9528d3f7f0dSeric 		line = io_getline(s->io, &len);
9536162865dSeric 		if ((line == NULL && io_datalen(s->io) >= SMTP_LINE_MAX) ||
9546162865dSeric 		    (line && len >= SMTP_LINE_MAX)) {
955299c4efeSeric 			s->flags |= SF_BADINPUT;
956fe95d8d0Seric 			smtp_reply(s, "500 %s: Line too long",
957fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS));
95865c4fdfbSgilles 			smtp_enter_state(s, STATE_QUIT);
95965c4fdfbSgilles 			io_set_write(io);
96065c4fdfbSgilles 			return;
96165c4fdfbSgilles 		}
96265c4fdfbSgilles 
96365c4fdfbSgilles 		/* No complete line received */
9640e198904Seric 		if (line == NULL)
96565c4fdfbSgilles 			return;
96665c4fdfbSgilles 
96765c4fdfbSgilles 		/* Message body */
9685bfc3fefSeric 		eom = 0;
9695bfc3fefSeric 		if (s->state == STATE_BODY) {
970008f3f72Seric 			eom = smtp_tx_dataline(s->tx, line);
9715bfc3fefSeric 			if (eom == 0)
97265c4fdfbSgilles 				goto nextline;
97365c4fdfbSgilles 		}
97465c4fdfbSgilles 
97565c4fdfbSgilles 		/* Pipelining not supported */
9768d3f7f0dSeric 		if (io_datalen(s->io)) {
977299c4efeSeric 			s->flags |= SF_BADINPUT;
978fe95d8d0Seric 			smtp_reply(s, "500 %s %s: Pipelining not supported",
979fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
980fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
98165c4fdfbSgilles 			smtp_enter_state(s, STATE_QUIT);
98265c4fdfbSgilles 			io_set_write(io);
98365c4fdfbSgilles 			return;
98465c4fdfbSgilles 		}
98565c4fdfbSgilles 
9865bfc3fefSeric 		if (eom) {
98755042ccbSeric 			smtp_message_end(s->tx);
9885bfc3fefSeric 			io_set_write(io);
98965c4fdfbSgilles 			return;
99065c4fdfbSgilles 		}
99165c4fdfbSgilles 
99265c4fdfbSgilles 		/* Must be a command */
9936162865dSeric 		if (strlcpy(s->cmd, line, sizeof(s->cmd)) >= sizeof(s->cmd)) {
9946162865dSeric 			s->flags |= SF_BADINPUT;
9956162865dSeric 			smtp_reply(s, "500 %s: Command line too long",
9966162865dSeric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS));
9976162865dSeric 			smtp_enter_state(s, STATE_QUIT);
9986162865dSeric 			io_set_write(io);
9996162865dSeric 			return;
10006162865dSeric 		}
100165c4fdfbSgilles 		io_set_write(io);
100265c4fdfbSgilles 		smtp_command(s, line);
100365c4fdfbSgilles 		break;
100465c4fdfbSgilles 
100565c4fdfbSgilles 	case IO_LOWAT:
100665c4fdfbSgilles 		if (s->state == STATE_QUIT) {
1007a206140dSgiovanni 			log_info("%016"PRIx64" smtp event=closed address=%s host=%s "
1008a206140dSgiovanni 			    "reason=quit",
1009a206140dSgiovanni 			    s->id, ss_to_text(&s->ss), s->hostname);
101065c4fdfbSgilles 			smtp_free(s, "done");
101165c4fdfbSgilles 			break;
101265c4fdfbSgilles 		}
101365c4fdfbSgilles 
101465c4fdfbSgilles 		/* Wait for the client to start tls */
101565c4fdfbSgilles 		if (s->state == STATE_TLS) {
10167627c963Seric 			smtp_tls_init(s);
101765c4fdfbSgilles 			break;
101865c4fdfbSgilles 		}
101965c4fdfbSgilles 
102065c4fdfbSgilles 		io_set_read(io);
102165c4fdfbSgilles 		break;
102265c4fdfbSgilles 
102365c4fdfbSgilles 	case IO_TIMEOUT:
1024a206140dSgiovanni 		log_info("%016"PRIx64" smtp event=closed address=%s host=%s "
1025a206140dSgiovanni 		    "reason=timeout",
1026a206140dSgiovanni 		    s->id, ss_to_text(&s->ss), s->hostname);
102765c4fdfbSgilles 		smtp_free(s, "timeout");
102865c4fdfbSgilles 		break;
102965c4fdfbSgilles 
103065c4fdfbSgilles 	case IO_DISCONNECTED:
1031a206140dSgiovanni 		log_info("%016"PRIx64" smtp event=closed address=%s host=%s "
1032a206140dSgiovanni 		    "reason=disconnect",
1033a206140dSgiovanni 		    s->id, ss_to_text(&s->ss), s->hostname);
103465c4fdfbSgilles 		smtp_free(s, "disconnected");
103565c4fdfbSgilles 		break;
103665c4fdfbSgilles 
103765c4fdfbSgilles 	case IO_ERROR:
1038a206140dSgiovanni 		log_info("%016"PRIx64" smtp event=closed address=%s host=%s "
1039a206140dSgiovanni 		    "reason=\"io-error: %s\"",
1040219e2fd6Seric 		    s->id, ss_to_text(&s->ss), s->hostname, io_error(io));
104165c4fdfbSgilles 		smtp_free(s, "IO error");
104265c4fdfbSgilles 		break;
104365c4fdfbSgilles 
104465c4fdfbSgilles 	default:
104565c4fdfbSgilles 		fatalx("smtp_io()");
104665c4fdfbSgilles 	}
104765c4fdfbSgilles }
104865c4fdfbSgilles 
104965c4fdfbSgilles static void
105065c4fdfbSgilles smtp_command(struct smtp_session *s, char *line)
105165c4fdfbSgilles {
105265c4fdfbSgilles 	char			       *args, *eom, *method;
105365c4fdfbSgilles 	int				cmd, i;
105465c4fdfbSgilles 
105565c4fdfbSgilles 	log_trace(TRACE_SMTP, "smtp: %p: <<< %s", s, line);
105665c4fdfbSgilles 
105765c4fdfbSgilles 	/*
105865c4fdfbSgilles 	 * These states are special.
105965c4fdfbSgilles 	 */
106065c4fdfbSgilles 	if (s->state == STATE_AUTH_INIT) {
106165c4fdfbSgilles 		smtp_rfc4954_auth_plain(s, line);
106265c4fdfbSgilles 		return;
106365c4fdfbSgilles 	}
106465c4fdfbSgilles 	if (s->state == STATE_AUTH_USERNAME || s->state == STATE_AUTH_PASSWORD) {
106565c4fdfbSgilles 		smtp_rfc4954_auth_login(s, line);
106665c4fdfbSgilles 		return;
106765c4fdfbSgilles 	}
106865c4fdfbSgilles 
106965c4fdfbSgilles 	/*
107065c4fdfbSgilles 	 * Unlike other commands, "mail from" and "rcpt to" contain a
107165c4fdfbSgilles 	 * space in the command name.
107265c4fdfbSgilles 	 */
107365c4fdfbSgilles 	if (strncasecmp("mail from:", line, 10) == 0 ||
107465c4fdfbSgilles 	    strncasecmp("rcpt to:", line, 8) == 0)
107565c4fdfbSgilles 		args = strchr(line, ':');
107665c4fdfbSgilles 	else
107765c4fdfbSgilles 		args = strchr(line, ' ');
107865c4fdfbSgilles 
107965c4fdfbSgilles 	if (args) {
108065c4fdfbSgilles 		*args++ = '\0';
1081fc3a8311Seric 		while (isspace((unsigned char)*args))
108265c4fdfbSgilles 			args++;
108365c4fdfbSgilles 	}
108465c4fdfbSgilles 
108565c4fdfbSgilles 	cmd = -1;
108665c4fdfbSgilles 	for (i = 0; commands[i].code != -1; i++)
108765c4fdfbSgilles 		if (!strcasecmp(line, commands[i].cmd)) {
108865c4fdfbSgilles 			cmd = commands[i].code;
108965c4fdfbSgilles 			break;
109065c4fdfbSgilles 		}
109165c4fdfbSgilles 
109265c4fdfbSgilles 	switch (cmd) {
109365c4fdfbSgilles 	/*
109465c4fdfbSgilles 	 * INIT
109565c4fdfbSgilles 	 */
109665c4fdfbSgilles 	case CMD_HELO:
109765c4fdfbSgilles 	case CMD_EHLO:
1098b937aab1Seric 		if (s->helo[0]) {
1099af0dba2aSsobrado 			smtp_reply(s, "503 %s %s: Already identified",
1100fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1101fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
110265c4fdfbSgilles 			break;
1103b8ff4170Sgilles 		}
1104b8ff4170Sgilles 
11053ef9cbf7Sgilles 		if (args == NULL) {
1106fe95d8d0Seric 			smtp_reply(s, "501 %s %s: %s requires domain name",
1107fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1108fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND),
110965c4fdfbSgilles 			    (cmd == CMD_HELO) ? "HELO" : "EHLO");
1110fe95d8d0Seric 
111165c4fdfbSgilles 			break;
111265c4fdfbSgilles 		}
111365c4fdfbSgilles 
111465c4fdfbSgilles 		if (!valid_domainpart(args)) {
1115fe95d8d0Seric 			smtp_reply(s, "501 %s %s: Invalid domain name",
1116fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1117fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
111865c4fdfbSgilles 			break;
111965c4fdfbSgilles 		}
1120c51a7fb7Sgilles 		(void)strlcpy(s->helo, args, sizeof(s->helo));
112184639b21Seric 		s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED;
112265c4fdfbSgilles 		if (cmd == CMD_EHLO) {
112365c4fdfbSgilles 			s->flags |= SF_EHLO;
112465c4fdfbSgilles 			s->flags |= SF_8BITMIME;
112565c4fdfbSgilles 		}
112665c4fdfbSgilles 
1127c679e749Seric 		smtp_enter_state(s, STATE_HELO);
1128c679e749Seric 		smtp_reply(s, "250%c%s Hello %s [%s], pleased to meet you",
1129c679e749Seric 		    (s->flags & SF_EHLO) ? '-' : ' ',
1130c679e749Seric 		    s->smtpname,
1131c679e749Seric 		    s->helo,
1132c679e749Seric 		    ss_to_text(&s->ss));
1133c679e749Seric 
1134c679e749Seric 		if (s->flags & SF_EHLO) {
1135c679e749Seric 			smtp_reply(s, "250-8BITMIME");
1136c679e749Seric 			smtp_reply(s, "250-ENHANCEDSTATUSCODES");
1137c679e749Seric 			smtp_reply(s, "250-SIZE %zu", env->sc_maxsize);
1138c679e749Seric 			if (ADVERTISE_EXT_DSN(s))
1139c679e749Seric 				smtp_reply(s, "250-DSN");
1140c679e749Seric 			if (ADVERTISE_TLS(s))
1141c679e749Seric 				smtp_reply(s, "250-STARTTLS");
1142c679e749Seric 			if (ADVERTISE_AUTH(s))
1143c679e749Seric 				smtp_reply(s, "250-AUTH PLAIN LOGIN");
1144c679e749Seric 			smtp_reply(s, "250 HELP");
1145c679e749Seric 		}
114665c4fdfbSgilles 		break;
114765c4fdfbSgilles 	/*
114865c4fdfbSgilles 	 * SETUP
114965c4fdfbSgilles 	 */
115065c4fdfbSgilles 	case CMD_STARTTLS:
1151b937aab1Seric 		if (s->helo[0] == '\0' || s->tx) {
1152fe95d8d0Seric 			smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1153fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1154fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
115565c4fdfbSgilles 			break;
115665c4fdfbSgilles 		}
115765c4fdfbSgilles 
115865c4fdfbSgilles 		if (!(s->listener->flags & F_STARTTLS)) {
1159fe95d8d0Seric 			smtp_reply(s, "503 %s %s: Command not supported",
1160fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1161fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
116265c4fdfbSgilles 			break;
116365c4fdfbSgilles 		}
116465c4fdfbSgilles 
116565c4fdfbSgilles 		if (s->flags & SF_SECURE) {
1166fe95d8d0Seric 			smtp_reply(s, "503 %s %s: Channel already secured",
1167fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1168fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
116965c4fdfbSgilles 			break;
117065c4fdfbSgilles 		}
117165c4fdfbSgilles 		if (args != NULL) {
1172fe95d8d0Seric 			smtp_reply(s, "501 %s %s: No parameters allowed",
1173fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1174fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
117565c4fdfbSgilles 			break;
117665c4fdfbSgilles 		}
1177fe95d8d0Seric 		smtp_reply(s, "220 %s: Ready to start TLS",
1178fe95d8d0Seric 		    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
117965c4fdfbSgilles 		smtp_enter_state(s, STATE_TLS);
118065c4fdfbSgilles 		break;
118165c4fdfbSgilles 
118265c4fdfbSgilles 	case CMD_AUTH:
1183b937aab1Seric 		if (s->helo[0] == '\0' || s->tx) {
1184fe95d8d0Seric 			smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1185fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1186fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
118765c4fdfbSgilles 			break;
118865c4fdfbSgilles 		}
118965c4fdfbSgilles 
119065c4fdfbSgilles 		if (s->flags & SF_AUTHENTICATED) {
1191fe95d8d0Seric 			smtp_reply(s, "503 %s %s: Already authenticated",
1192fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1193fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
119465c4fdfbSgilles 			break;
119565c4fdfbSgilles 		}
119665c4fdfbSgilles 
119765c4fdfbSgilles 		if (!ADVERTISE_AUTH(s)) {
1198fe95d8d0Seric 			smtp_reply(s, "503 %s %s: Command not supported",
1199fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1200fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
120165c4fdfbSgilles 			break;
120265c4fdfbSgilles 		}
120365c4fdfbSgilles 
120465c4fdfbSgilles 		if (args == NULL) {
1205fe95d8d0Seric 			smtp_reply(s, "501 %s %s: No parameters given",
1206fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1207fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
120865c4fdfbSgilles 			break;
12093ef9cbf7Sgilles 		}
12103ef9cbf7Sgilles 
12113ef9cbf7Sgilles 		method = args;
12123ef9cbf7Sgilles 		eom = strchr(args, ' ');
12133ef9cbf7Sgilles 		if (eom == NULL)
12143ef9cbf7Sgilles 			eom = strchr(args, '\t');
12153ef9cbf7Sgilles 		if (eom != NULL)
12163ef9cbf7Sgilles 			*eom++ = '\0';
121785e1f4f6Sgilles 		if (strcasecmp(method, "PLAIN") == 0)
121865c4fdfbSgilles 			smtp_rfc4954_auth_plain(s, eom);
121985e1f4f6Sgilles 		else if (strcasecmp(method, "LOGIN") == 0)
122065c4fdfbSgilles 			smtp_rfc4954_auth_login(s, eom);
1221e8fa9864Sjacekm 		else
1222fe95d8d0Seric 			smtp_reply(s, "504 %s %s: AUTH method \"%s\" not supported",
1223fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_SECURITY_FEATURES_NOT_SUPPORTED),
1224fe95d8d0Seric 			    esc_description(ESC_SECURITY_FEATURES_NOT_SUPPORTED),
122565c4fdfbSgilles 			    method);
122665c4fdfbSgilles 		break;
122785e1f4f6Sgilles 
122865c4fdfbSgilles 	case CMD_MAIL_FROM:
1229b937aab1Seric 		if (s->helo[0] == '\0' || s->tx) {
1230fe95d8d0Seric 			smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1231fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1232fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
1233fe95d8d0Seric 
123465c4fdfbSgilles 			break;
123565c4fdfbSgilles 		}
123665c4fdfbSgilles 
123765c4fdfbSgilles 		if (s->listener->flags & F_STARTTLS_REQUIRE &&
123865c4fdfbSgilles 		    !(s->flags & SF_SECURE)) {
123965c4fdfbSgilles 			smtp_reply(s,
1240fe95d8d0Seric 			    "530 %s %s: Must issue a STARTTLS command first",
1241fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1242fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
124365c4fdfbSgilles 			break;
124465c4fdfbSgilles 		}
124565c4fdfbSgilles 
124665c4fdfbSgilles 		if (s->listener->flags & F_AUTH_REQUIRE &&
124765c4fdfbSgilles 		    !(s->flags & SF_AUTHENTICATED)) {
124865c4fdfbSgilles 			smtp_reply(s,
1249fe95d8d0Seric 			    "530 %s %s: Must issue an AUTH command first",
1250fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1251fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
125265c4fdfbSgilles 			break;
125365c4fdfbSgilles 		}
125465c4fdfbSgilles 
125552f1cdb1Sjung 		if (s->mailcount >= env->sc_session_max_mails) {
1256fe95d8d0Seric 			/* we can pretend we had too many recipients */
1257fe95d8d0Seric 			smtp_reply(s, "452 %s %s: Too many messages sent",
1258fe95d8d0Seric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
1259fe95d8d0Seric 			    esc_description(ESC_TOO_MANY_RECIPIENTS));
126065c4fdfbSgilles 			break;
126165c4fdfbSgilles 		}
126265c4fdfbSgilles 
12630490754bSeric 		if (!smtp_tx(s)) {
12640490754bSeric 			smtp_reply(s, "421 %s: Temporary Error",
12650490754bSeric 			    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
12660490754bSeric 			smtp_enter_state(s, STATE_QUIT);
12670490754bSeric 			break;
12680490754bSeric 		}
126965c4fdfbSgilles 
127072cde3cbSeric 		smtp_tx_mail_from(s->tx, args);
127165c4fdfbSgilles 		break;
127265c4fdfbSgilles 	/*
127365c4fdfbSgilles 	 * TRANSACTION
127465c4fdfbSgilles 	 */
127565c4fdfbSgilles 	case CMD_RCPT_TO:
1276b937aab1Seric 		if (s->tx == NULL) {
1277fe95d8d0Seric 			smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1278fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1279fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
128065c4fdfbSgilles 			break;
128165c4fdfbSgilles 		}
128265c4fdfbSgilles 
128372cde3cbSeric 		smtp_tx_rcpt_to(s->tx, args);
128465c4fdfbSgilles 		break;
128565c4fdfbSgilles 
128665c4fdfbSgilles 	case CMD_RSET:
1287b937aab1Seric 		if (s->helo[0] == '\0') {
1288fe95d8d0Seric 			smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1289fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1290fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
129165c4fdfbSgilles 			break;
129265c4fdfbSgilles 		}
129365c4fdfbSgilles 
12940490754bSeric 		if (s->tx) {
1295763ff2e3Seric 			if (s->tx->msgid)
1296008f3f72Seric 				smtp_tx_rollback(s->tx);
12970490754bSeric 			smtp_tx_free(s->tx);
12980490754bSeric 		}
129965c4fdfbSgilles 
1300fe95d8d0Seric 		smtp_reply(s, "250 %s: Reset state",
1301fe95d8d0Seric 		    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
130265c4fdfbSgilles 		break;
130365c4fdfbSgilles 
130465c4fdfbSgilles 	case CMD_DATA:
1305b937aab1Seric 		if (s->tx == NULL) {
1306fe95d8d0Seric 			smtp_reply(s, "503 %s %s: Command not allowed at this point.",
1307fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1308fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
130965c4fdfbSgilles 			break;
131065c4fdfbSgilles 		}
13110a80c58aSeric 		if (s->tx->rcptcount == 0) {
1312fe95d8d0Seric 			smtp_reply(s, "503 %s %s: No recipient specified",
1313fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1314fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
131565c4fdfbSgilles 			break;
131665c4fdfbSgilles 		}
131765c4fdfbSgilles 
1318008f3f72Seric 		smtp_tx_open_message(s->tx);
131965c4fdfbSgilles 		break;
132065c4fdfbSgilles 	/*
132165c4fdfbSgilles 	 * ANY
132265c4fdfbSgilles 	 */
132365c4fdfbSgilles 	case CMD_QUIT:
1324fe95d8d0Seric 		smtp_reply(s, "221 %s: Bye",
1325fe95d8d0Seric 		    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
132665c4fdfbSgilles 		smtp_enter_state(s, STATE_QUIT);
132765c4fdfbSgilles 		break;
132865c4fdfbSgilles 
132965c4fdfbSgilles 	case CMD_NOOP:
1330fe95d8d0Seric 		smtp_reply(s, "250 %s: Ok",
1331fe95d8d0Seric 		    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
133265c4fdfbSgilles 		break;
133365c4fdfbSgilles 
133465c4fdfbSgilles 	case CMD_HELP:
133598cf77ddSjung 		smtp_reply(s, "214- This is " SMTPD_NAME);
133665c4fdfbSgilles 		smtp_reply(s, "214- To report bugs in the implementation, "
133765c4fdfbSgilles 		    "please contact bugs@openbsd.org");
133865c4fdfbSgilles 		smtp_reply(s, "214- with full details");
1339fe95d8d0Seric 		smtp_reply(s, "214 %s: End of HELP info",
1340fe95d8d0Seric 		    esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
1341fe95d8d0Seric 		break;
1342fe95d8d0Seric 
1343fe95d8d0Seric 	case CMD_WIZ:
1344fe95d8d0Seric 		smtp_reply(s, "500 %s %s: this feature is not supported yet ;-)",
1345fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1346fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
134765c4fdfbSgilles 		break;
134865c4fdfbSgilles 
134965c4fdfbSgilles 	default:
1350fe95d8d0Seric 		smtp_reply(s, "500 %s %s: Command unrecognized",
1351fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
1352fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND));
135365c4fdfbSgilles 		break;
135465c4fdfbSgilles 	}
135585e1f4f6Sgilles }
135685e1f4f6Sgilles 
1357be925435Sgilles static void
135865c4fdfbSgilles smtp_rfc4954_auth_plain(struct smtp_session *s, char *arg)
135985e1f4f6Sgilles {
1360e8fa9864Sjacekm 	char		 buf[1024], *user, *pass;
1361e8fa9864Sjacekm 	int		 len;
1362e8fa9864Sjacekm 
136365c4fdfbSgilles 	switch (s->state) {
136465c4fdfbSgilles 	case STATE_HELO:
136585e1f4f6Sgilles 		if (arg == NULL) {
136665c4fdfbSgilles 			smtp_enter_state(s, STATE_AUTH_INIT);
136765c4fdfbSgilles 			smtp_reply(s, "334 ");
1368e8fa9864Sjacekm 			return;
136985e1f4f6Sgilles 		}
137065c4fdfbSgilles 		smtp_enter_state(s, STATE_AUTH_INIT);
1371e8fa9864Sjacekm 		/* FALLTHROUGH */
137285e1f4f6Sgilles 
137365c4fdfbSgilles 	case STATE_AUTH_INIT:
1374e8fa9864Sjacekm 		/* String is not NUL terminated, leave room. */
1375254aed36Seric 		if ((len = base64_decode(arg, (unsigned char *)buf,
13767bdbba2fSeric 			    sizeof(buf) - 1)) == -1)
1377e8fa9864Sjacekm 			goto abort;
1378e8fa9864Sjacekm 		/* buf is a byte string, NUL terminate. */
1379e8fa9864Sjacekm 		buf[len] = '\0';
1380e8fa9864Sjacekm 
1381e8fa9864Sjacekm 		/*
1382e8fa9864Sjacekm 		 * Skip "foo" in "foo\0user\0pass", if present.
1383e8fa9864Sjacekm 		 */
1384e8fa9864Sjacekm 		user = memchr(buf, '\0', len);
1385e8fa9864Sjacekm 		if (user == NULL || user >= buf + len - 2)
1386e8fa9864Sjacekm 			goto abort;
1387e8fa9864Sjacekm 		user++; /* skip NUL */
138865c4fdfbSgilles 		if (strlcpy(s->username, user, sizeof(s->username))
138965c4fdfbSgilles 		    >= sizeof(s->username))
1390e8fa9864Sjacekm 			goto abort;
1391e8fa9864Sjacekm 
1392e8fa9864Sjacekm 		pass = memchr(user, '\0', len - (user - buf));
1393e8fa9864Sjacekm 		if (pass == NULL || pass >= buf + len - 2)
1394e8fa9864Sjacekm 			goto abort;
1395e8fa9864Sjacekm 		pass++; /* skip NUL */
13963ef9cbf7Sgilles 
1397aa1d5973Seric 		m_create(p_lka,  IMSG_SMTP_AUTHENTICATE, 0, 0, -1);
139865c4fdfbSgilles 		m_add_id(p_lka, s->id);
139965c4fdfbSgilles 		m_add_string(p_lka, s->listener->authtable);
140065c4fdfbSgilles 		m_add_string(p_lka, user);
140165c4fdfbSgilles 		m_add_string(p_lka, pass);
140265c4fdfbSgilles 		m_close(p_lka);
140365c4fdfbSgilles 		tree_xset(&wait_parent_auth, s->id, s);
1404e8fa9864Sjacekm 		return;
1405e8fa9864Sjacekm 
1406e8fa9864Sjacekm 	default:
140765c4fdfbSgilles 		fatal("smtp_rfc4954_auth_plain: unknown state");
14083ef9cbf7Sgilles 	}
14093ef9cbf7Sgilles 
1410e8fa9864Sjacekm abort:
1411fe95d8d0Seric 	smtp_reply(s, "501 %s %s: Syntax error",
1412fe95d8d0Seric 	    esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR),
1413fe95d8d0Seric 	    esc_description(ESC_SYNTAX_ERROR));
141465c4fdfbSgilles 	smtp_enter_state(s, STATE_HELO);
1415e8fa9864Sjacekm }
1416e8fa9864Sjacekm 
1417be925435Sgilles static void
141865c4fdfbSgilles smtp_rfc4954_auth_login(struct smtp_session *s, char *arg)
141985e1f4f6Sgilles {
1420953aae25Sderaadt 	char		buf[LINE_MAX];
142185e1f4f6Sgilles 
142265c4fdfbSgilles 	switch (s->state) {
142365c4fdfbSgilles 	case STATE_HELO:
142465c4fdfbSgilles 		smtp_enter_state(s, STATE_AUTH_USERNAME);
1425da29cb6eSgilles 		if (arg != NULL && *arg != '\0') {
1426da29cb6eSgilles 			smtp_rfc4954_auth_login(s, arg);
1427da29cb6eSgilles 			return;
1428da29cb6eSgilles 		}
142965c4fdfbSgilles 		smtp_reply(s, "334 VXNlcm5hbWU6");
1430e8fa9864Sjacekm 		return;
143185e1f4f6Sgilles 
143265c4fdfbSgilles 	case STATE_AUTH_USERNAME:
1433c1392a69Seric 		memset(s->username, 0, sizeof(s->username));
1434254aed36Seric 		if (base64_decode(arg, (unsigned char *)s->username,
143565c4fdfbSgilles 				  sizeof(s->username) - 1) == -1)
1436e8fa9864Sjacekm 			goto abort;
143785e1f4f6Sgilles 
143865c4fdfbSgilles 		smtp_enter_state(s, STATE_AUTH_PASSWORD);
143965c4fdfbSgilles 		smtp_reply(s, "334 UGFzc3dvcmQ6");
1440e8fa9864Sjacekm 		return;
144185e1f4f6Sgilles 
144265c4fdfbSgilles 	case STATE_AUTH_PASSWORD:
1443c1392a69Seric 		memset(buf, 0, sizeof(buf));
1444254aed36Seric 		if (base64_decode(arg, (unsigned char *)buf,
1445254aed36Seric 				  sizeof(buf)-1) == -1)
1446e8fa9864Sjacekm 			goto abort;
144785e1f4f6Sgilles 
1448aa1d5973Seric 		m_create(p_lka,  IMSG_SMTP_AUTHENTICATE, 0, 0, -1);
144965c4fdfbSgilles 		m_add_id(p_lka, s->id);
145065c4fdfbSgilles 		m_add_string(p_lka, s->listener->authtable);
145165c4fdfbSgilles 		m_add_string(p_lka, s->username);
145265c4fdfbSgilles 		m_add_string(p_lka, buf);
145365c4fdfbSgilles 		m_close(p_lka);
145465c4fdfbSgilles 		tree_xset(&wait_parent_auth, s->id, s);
1455e8fa9864Sjacekm 		return;
1456e8fa9864Sjacekm 
145785e1f4f6Sgilles 	default:
145865c4fdfbSgilles 		fatal("smtp_rfc4954_auth_login: unknown state");
145985e1f4f6Sgilles 	}
146085e1f4f6Sgilles 
1461e8fa9864Sjacekm abort:
1462fe95d8d0Seric 	smtp_reply(s, "501 %s %s: Syntax error",
1463fe95d8d0Seric 	    esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR),
1464fe95d8d0Seric 	    esc_description(ESC_SYNTAX_ERROR));
146565c4fdfbSgilles 	smtp_enter_state(s, STATE_HELO);
146685e1f4f6Sgilles }
146785e1f4f6Sgilles 
1468fe95d8d0Seric static int
1469fe95d8d0Seric smtp_parse_rcpt_args(struct smtp_session *s, char *args)
1470fe95d8d0Seric {
1471fe95d8d0Seric 	char *b, *p;
1472fe95d8d0Seric 
1473fe95d8d0Seric 	while ((b = strsep(&args, " "))) {
1474fe95d8d0Seric 		if (*b == '\0')
1475fe95d8d0Seric 			continue;
1476fe95d8d0Seric 
1477ad286364Seric 		if (ADVERTISE_EXT_DSN(s) && strncasecmp(b, "NOTIFY=", 7) == 0) {
1478fe95d8d0Seric 			b += 7;
1479fe95d8d0Seric 			while ((p = strsep(&b, ","))) {
1480fb24b76bSeric 				if (strcasecmp(p, "SUCCESS") == 0)
1481fb24b76bSeric 					s->tx->evp.dsn_notify |= DSN_SUCCESS;
1482fb24b76bSeric 				else if (strcasecmp(p, "FAILURE") == 0)
1483fb24b76bSeric 					s->tx->evp.dsn_notify |= DSN_FAILURE;
1484fb24b76bSeric 				else if (strcasecmp(p, "DELAY") == 0)
1485fb24b76bSeric 					s->tx->evp.dsn_notify |= DSN_DELAY;
1486fb24b76bSeric 				else if (strcasecmp(p, "NEVER") == 0)
1487fb24b76bSeric 					s->tx->evp.dsn_notify |= DSN_NEVER;
1488fe95d8d0Seric 			}
1489fb24b76bSeric 
14900a80c58aSeric 			if (s->tx->evp.dsn_notify & DSN_NEVER &&
14910a80c58aSeric 			    s->tx->evp.dsn_notify & (DSN_SUCCESS | DSN_FAILURE |
1492fe95d8d0Seric 			    DSN_DELAY)) {
1493fe95d8d0Seric 				smtp_reply(s,
1494fe95d8d0Seric 				    "553 NOTIFY option NEVER cannot be \
1495fe95d8d0Seric 				    combined with other options");
1496fe95d8d0Seric 				return (-1);
1497fe95d8d0Seric 			}
1498ad286364Seric 		} else if (ADVERTISE_EXT_DSN(s) && strncasecmp(b, "ORCPT=", 6) == 0) {
1499fe95d8d0Seric 			b += 6;
15000a80c58aSeric 			if (!text_to_mailaddr(&s->tx->evp.dsn_orcpt, b)) {
1501fe95d8d0Seric 				smtp_reply(s, "553 ORCPT address syntax error");
1502fe95d8d0Seric 				return (-1);
1503fe95d8d0Seric 			}
1504fe95d8d0Seric 		} else {
1505fe95d8d0Seric 			smtp_reply(s, "503 Unsupported option %s", b);
1506fe95d8d0Seric 			return (-1);
1507fe95d8d0Seric 		}
1508fe95d8d0Seric 	}
1509fe95d8d0Seric 
1510fe95d8d0Seric 	return (0);
1511fe95d8d0Seric }
1512fe95d8d0Seric 
1513be925435Sgilles static int
151465c4fdfbSgilles smtp_parse_mail_args(struct smtp_session *s, char *args)
15153ef9cbf7Sgilles {
151665c4fdfbSgilles 	char *b;
15173ef9cbf7Sgilles 
151865c4fdfbSgilles 	while ((b = strsep(&args, " "))) {
151965c4fdfbSgilles 		if (*b == '\0')
15205739f5ceSjacekm 			continue;
15213ef9cbf7Sgilles 
152265c4fdfbSgilles 		if (strncasecmp(b, "AUTH=", 5) == 0)
152365c4fdfbSgilles 			log_debug("debug: smtp: AUTH in MAIL FROM command");
152465c4fdfbSgilles 		else if (strncasecmp(b, "SIZE=", 5) == 0)
152565c4fdfbSgilles 			log_debug("debug: smtp: SIZE in MAIL FROM command");
1526fe95d8d0Seric 		else if (strcasecmp(b, "BODY=7BIT") == 0)
152765c4fdfbSgilles 			/* XXX only for this transaction */
152865c4fdfbSgilles 			s->flags &= ~SF_8BITMIME;
152965c4fdfbSgilles 		else if (strcasecmp(b, "BODY=8BITMIME") == 0)
153065c4fdfbSgilles 			;
1531ad286364Seric 		else if (ADVERTISE_EXT_DSN(s) && strncasecmp(b, "RET=", 4) == 0) {
1532fe95d8d0Seric 			b += 4;
1533fe95d8d0Seric 			if (strcasecmp(b, "HDRS") == 0)
15340a80c58aSeric 				s->tx->evp.dsn_ret = DSN_RETHDRS;
1535fe95d8d0Seric 			else if (strcasecmp(b, "FULL") == 0)
15360a80c58aSeric 				s->tx->evp.dsn_ret = DSN_RETFULL;
1537ad286364Seric 		} else if (ADVERTISE_EXT_DSN(s) && strncasecmp(b, "ENVID=", 6) == 0) {
1538fe95d8d0Seric 			b += 6;
15390a80c58aSeric 			if (strlcpy(s->tx->evp.dsn_envid, b, sizeof(s->tx->evp.dsn_envid))
15400a80c58aSeric 			    >= sizeof(s->tx->evp.dsn_envid)) {
1541351a2600Sgilles 				smtp_reply(s, "503 %s %s: option too large, truncated: %s",
1542351a2600Sgilles 				    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1543351a2600Sgilles 				    esc_description(ESC_INVALID_COMMAND_ARGUMENTS), b);
1544351a2600Sgilles 				return (-1);
1545351a2600Sgilles 			}
1546fe95d8d0Seric 		} else {
1547fe95d8d0Seric 			smtp_reply(s, "503 %s %s: Unsupported option %s",
1548fe95d8d0Seric 			    esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
1549fe95d8d0Seric 			    esc_description(ESC_INVALID_COMMAND_ARGUMENTS), b);
15503ef9cbf7Sgilles 			return (-1);
155165c4fdfbSgilles 		}
155265c4fdfbSgilles 	}
15533ef9cbf7Sgilles 
15543ef9cbf7Sgilles 	return (0);
15553ef9cbf7Sgilles }
15563ef9cbf7Sgilles 
15571c3ac238Seric static int
15581c3ac238Seric smtp_lookup_servername(struct smtp_session *s)
15591c3ac238Seric {
15601c3ac238Seric 	struct sockaddr		*sa;
15611c3ac238Seric 	socklen_t		 sa_len;
15621c3ac238Seric 	struct sockaddr_storage	 ss;
15631c3ac238Seric 
15641c3ac238Seric 	if (s->listener->hostnametable[0]) {
15651c3ac238Seric 		sa_len = sizeof(ss);
15661c3ac238Seric 		sa = (struct sockaddr *)&ss;
15678d3f7f0dSeric 		if (getsockname(io_fileno(s->io), sa, &sa_len) == -1) {
15681c3ac238Seric 			log_warn("warn: getsockname()");
15691c3ac238Seric 		}
15701c3ac238Seric 		else {
1571aa1d5973Seric 			m_create(p_lka, IMSG_SMTP_LOOKUP_HELO, 0, 0, -1);
15721c3ac238Seric 			m_add_id(p_lka, s->id);
15731c3ac238Seric 			m_add_string(p_lka, s->listener->hostnametable);
15741c3ac238Seric 			m_add_sockaddr(p_lka, sa);
15751c3ac238Seric 			m_close(p_lka);
15761c3ac238Seric 			tree_xset(&wait_lka_helo, s->id, s);
15771c3ac238Seric 			return 0;
15781c3ac238Seric 		}
15791c3ac238Seric 	}
15801c3ac238Seric 	return 1;
15811c3ac238Seric }
15821c3ac238Seric 
158365c4fdfbSgilles static void
158465c4fdfbSgilles smtp_connected(struct smtp_session *s)
15853ef9cbf7Sgilles {
158665c4fdfbSgilles 	struct sockaddr_storage	ss;
158765c4fdfbSgilles 	socklen_t		sl;
15883ef9cbf7Sgilles 
158965c4fdfbSgilles 	smtp_enter_state(s, STATE_CONNECTED);
15903ef9cbf7Sgilles 
15917a93bdaaSgilles 	log_info("%016"PRIx64" smtp event=connected address=%s host=%s",
15927a93bdaaSgilles 	    s->id, ss_to_text(&s->ss), s->hostname);
159365c4fdfbSgilles 
159465c4fdfbSgilles 	sl = sizeof(ss);
15958d3f7f0dSeric 	if (getsockname(io_fileno(s->io), (struct sockaddr*)&ss, &sl) == -1) {
1596316175faSeric 		smtp_free(s, strerror(errno));
1597316175faSeric 		return;
1598316175faSeric 	}
159965c4fdfbSgilles 
1600c679e749Seric 	if (s->listener->flags & F_SMTPS) {
16017627c963Seric 		smtp_tls_init(s);
1602c679e749Seric 		return;
1603c679e749Seric 	}
1604c679e749Seric 
1605c679e749Seric 	smtp_send_banner(s);
16063ef9cbf7Sgilles }
16073ef9cbf7Sgilles 
1608cc81b7c6Seric static void
1609cc81b7c6Seric smtp_send_banner(struct smtp_session *s)
1610cc81b7c6Seric {
161148b77194Seric 	smtp_reply(s, "220 %s ESMTP %s", s->smtpname, SMTPD_NAME);
1612cc81b7c6Seric }
1613cc81b7c6Seric 
16143ef9cbf7Sgilles void
161565c4fdfbSgilles smtp_enter_state(struct smtp_session *s, int newstate)
161665c4fdfbSgilles {
161765c4fdfbSgilles 	log_trace(TRACE_SMTP, "smtp: %p: %s -> %s", s,
161865c4fdfbSgilles 	    smtp_strstate(s->state),
161965c4fdfbSgilles 	    smtp_strstate(newstate));
162065c4fdfbSgilles 
162165c4fdfbSgilles 	s->state = newstate;
162265c4fdfbSgilles }
162365c4fdfbSgilles 
162465c4fdfbSgilles static void
162565c4fdfbSgilles smtp_reply(struct smtp_session *s, char *fmt, ...)
1626a6ca2cc5Sjacekm {
1627a6ca2cc5Sjacekm 	va_list	 ap;
162865c4fdfbSgilles 	int	 n;
1629953aae25Sderaadt 	char	 buf[LINE_MAX], tmp[LINE_MAX];
1630a6ca2cc5Sjacekm 
1631a6ca2cc5Sjacekm 	va_start(ap, fmt);
1632b04e6f27Seric 	n = vsnprintf(buf, sizeof buf, fmt, ap);
1633a6ca2cc5Sjacekm 	va_end(ap);
1634953aae25Sderaadt 	if (n == -1 || n >= LINE_MAX)
163565c4fdfbSgilles 		fatalx("smtp_reply: line too long");
1636b04e6f27Seric 	if (n < 4)
163765c4fdfbSgilles 		fatalx("smtp_reply: response too short");
1638a6ca2cc5Sjacekm 
1639b04e6f27Seric 	log_trace(TRACE_SMTP, "smtp: %p: >>> %s", s, buf);
1640b04e6f27Seric 
16418d3f7f0dSeric 	io_xprintf(s->io, "%s\r\n", buf);
16425711eb67Sjacekm 
1643b04e6f27Seric 	switch (buf[0]) {
16441ef63b12Sjacekm 	case '5':
16451ef63b12Sjacekm 	case '4':
1646299c4efeSeric 		if (s->flags & SF_BADINPUT) {
16477a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
1648a206140dSgiovanni 			    "event=bad-input address=%s host=%s result=\"%.*s\"",
1649a206140dSgiovanni 			    s->id, ss_to_text(&s->ss), s->hostname, n, buf);
1650299c4efeSeric 		}
16516f197337Sgilles 		else if (s->state == STATE_AUTH_INIT) {
1652a206140dSgiovanni 			log_info("%016"PRIx64" smtp "
1653a206140dSgiovanni 			    "event=failed-command address=%s host=%s "
1654a206140dSgiovanni 			    "command=\"AUTH PLAIN (...)\" result=\"%.*s\"",
1655a206140dSgiovanni 			    s->id, ss_to_text(&s->ss), s->hostname, n, buf);
16566f197337Sgilles 		}
16576f197337Sgilles 		else if (s->state == STATE_AUTH_USERNAME) {
1658a206140dSgiovanni 			log_info("%016"PRIx64" smtp "
1659a206140dSgiovanni 			    "event=failed-command address=%s host=%s "
1660a206140dSgiovanni 			    "command=\"AUTH LOGIN (username)\" result=\"%.*s\"",
1661a206140dSgiovanni 			    s->id, ss_to_text(&s->ss), s->hostname, n, buf);
16626f197337Sgilles 		}
16636f197337Sgilles 		else if (s->state == STATE_AUTH_PASSWORD) {
1664a206140dSgiovanni 			log_info("%016"PRIx64" smtp "
1665a206140dSgiovanni 			    "event=failed-command address=%s host=%s "
1666a206140dSgiovanni 			    "command=\"AUTH LOGIN (password)\" result=\"%.*s\"",
1667a206140dSgiovanni 			    s->id, ss_to_text(&s->ss), s->hostname, n, buf);
1668299c4efeSeric 		}
1669299c4efeSeric 		else {
167082614934Seric 			strnvis(tmp, s->cmd, sizeof tmp, VIS_SAFE | VIS_CSTYLE);
16717a93bdaaSgilles 			log_info("%016"PRIx64" smtp "
1672a206140dSgiovanni 			    "event=failed-command address=%s host=%s command=\"%s\" "
1673a206140dSgiovanni 			    "result=\"%.*s\"",
1674a206140dSgiovanni 			    s->id, ss_to_text(&s->ss), s->hostname, tmp, n, buf);
1675299c4efeSeric 		}
16761ef63b12Sjacekm 		break;
16771ef63b12Sjacekm 	}
16785711eb67Sjacekm }
16795711eb67Sjacekm 
1680be925435Sgilles static void
168165c4fdfbSgilles smtp_free(struct smtp_session *s, const char * reason)
16828107cc2fSjacekm {
168365c4fdfbSgilles 	log_debug("debug: smtp: %p: deleting session: %s", s, reason);
1684b04e6f27Seric 
16850490754bSeric 	if (s->tx) {
16868d3f7f0dSeric 		if (s->tx->msgid)
1687008f3f72Seric 			smtp_tx_rollback(s->tx);
16880490754bSeric 		smtp_tx_free(s->tx);
16890490754bSeric 	}
1690dcdb4581Seric 
169165c4fdfbSgilles 	if (s->flags & SF_SECURE && s->listener->flags & F_SMTPS)
169265c4fdfbSgilles 		stat_decrement("smtp.smtps", 1);
169365c4fdfbSgilles 	if (s->flags & SF_SECURE && s->listener->flags & F_STARTTLS)
169465c4fdfbSgilles 		stat_decrement("smtp.tls", 1);
169565c4fdfbSgilles 
16968d3f7f0dSeric 	io_free(s->io);
169765c4fdfbSgilles 	free(s);
169865c4fdfbSgilles 
169965c4fdfbSgilles 	smtp_collect();
170065c4fdfbSgilles }
170165c4fdfbSgilles 
170265c4fdfbSgilles static int
1703cc81b7c6Seric smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args,
1704cc81b7c6Seric     const char *domain)
170565c4fdfbSgilles {
170665c4fdfbSgilles 	char   *p, *e;
170765c4fdfbSgilles 
170865c4fdfbSgilles 	if (line == NULL)
170965c4fdfbSgilles 		return (0);
171065c4fdfbSgilles 
171165c4fdfbSgilles 	if (*line != '<')
171265c4fdfbSgilles 		return (0);
171365c4fdfbSgilles 
171465c4fdfbSgilles 	e = strchr(line, '>');
171565c4fdfbSgilles 	if (e == NULL)
171665c4fdfbSgilles 		return (0);
171765c4fdfbSgilles 	*e++ = '\0';
171865c4fdfbSgilles 	while (*e == ' ')
171965c4fdfbSgilles 		e++;
172065c4fdfbSgilles 	*args = e;
172165c4fdfbSgilles 
172265c4fdfbSgilles 	if (!text_to_mailaddr(maddr, line + 1))
172365c4fdfbSgilles 		return (0);
172465c4fdfbSgilles 
172565c4fdfbSgilles 	p = strchr(maddr->user, ':');
172665c4fdfbSgilles 	if (p != NULL) {
172765c4fdfbSgilles 		p++;
172865c4fdfbSgilles 		memmove(maddr->user, p, strlen(p) + 1);
172965c4fdfbSgilles 	}
173065c4fdfbSgilles 
173165c4fdfbSgilles 	if (!valid_localpart(maddr->user) ||
1732299c4efeSeric 	    !valid_domainpart(maddr->domain)) {
1733f3fad8caSgilles 		/* accept empty return-path in MAIL FROM, required for bounces */
1734f3fad8caSgilles 		if (mailfrom && maddr->user[0] == '\0' && maddr->domain[0] == '\0')
173565c4fdfbSgilles 			return (1);
1736cc81b7c6Seric 
1737f3fad8caSgilles 		/* no user-part, reject */
1738f3fad8caSgilles 		if (maddr->user[0] == '\0')
1739f3fad8caSgilles 			return (0);
1740f3fad8caSgilles 
1741f3fad8caSgilles 		/* no domain, local user */
1742f3fad8caSgilles 		if (maddr->domain[0] == '\0') {
1743cc81b7c6Seric 			(void)strlcpy(maddr->domain, domain,
1744cc81b7c6Seric 			    sizeof(maddr->domain));
1745cc81b7c6Seric 			return (1);
1746cc81b7c6Seric 		}
174765c4fdfbSgilles 		return (0);
174865c4fdfbSgilles 	}
174965c4fdfbSgilles 
175065c4fdfbSgilles 	return (1);
175165c4fdfbSgilles }
175265c4fdfbSgilles 
17537627c963Seric static void
17547627c963Seric smtp_tls_init(struct smtp_session *s)
17557627c963Seric {
17567627c963Seric 	struct ca_cert_req_msg req_ca_cert;
17577627c963Seric 
17587627c963Seric 	req_ca_cert.reqid = s->id;
17597627c963Seric 	if (s->listener->pki_name[0]) {
17607627c963Seric 		(void)strlcpy(req_ca_cert.name, s->listener->pki_name,
17617627c963Seric 		    sizeof req_ca_cert.name);
17627627c963Seric 		req_ca_cert.fallback = 0;
17637627c963Seric 	}
17647627c963Seric 	else {
17657627c963Seric 		(void)strlcpy(req_ca_cert.name, s->smtpname,
17667627c963Seric 		    sizeof req_ca_cert.name);
17677627c963Seric 		req_ca_cert.fallback = 1;
17687627c963Seric 	}
17697627c963Seric 	m_compose(p_lka, IMSG_SMTP_TLS_INIT, 0, 0, -1,
17707627c963Seric 	    &req_ca_cert, sizeof(req_ca_cert));
17717627c963Seric 	tree_xset(&wait_ssl_init, s->id, s);
17727627c963Seric }
17737627c963Seric 
177465c4fdfbSgilles static int
177565c4fdfbSgilles smtp_verify_certificate(struct smtp_session *s)
177665c4fdfbSgilles {
1777290f027cSgilles #define MAX_CERTS	16
1778290f027cSgilles #define MAX_CERT_LEN	(MAX_IMSGSIZE - (IMSG_HEADER_SIZE + sizeof(req_ca_vrfy)))
177965c4fdfbSgilles 	struct ca_vrfy_req_msg	req_ca_vrfy;
178065c4fdfbSgilles 	struct iovec		iov[2];
178165c4fdfbSgilles 	X509		       *x;
178265c4fdfbSgilles 	STACK_OF(X509)	       *xchain;
1783f7aa1c30Sgilles 	const char	       *name;
1784290f027cSgilles 	unsigned char	       *cert_der[MAX_CERTS];
1785290f027cSgilles 	int			cert_len[MAX_CERTS];
1786290f027cSgilles 	int			i, cert_count, res;
1787290f027cSgilles 
1788290f027cSgilles 	res = 0;
1789290f027cSgilles 	memset(cert_der, 0, sizeof(cert_der));
1790290f027cSgilles 	memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy);
1791290f027cSgilles 
1792f7aa1c30Sgilles 	/* Send the client certificate */
1793f7aa1c30Sgilles 	if (s->listener->ca_name[0]) {
1794f7aa1c30Sgilles 		name = s->listener->ca_name;
1795f7aa1c30Sgilles 		req_ca_vrfy.fallback = 0;
1796f7aa1c30Sgilles 	}
1797f7aa1c30Sgilles 	else {
1798f7aa1c30Sgilles 		name = s->smtpname;
1799f7aa1c30Sgilles 		req_ca_vrfy.fallback = 1;
1800f7aa1c30Sgilles 	}
1801f7aa1c30Sgilles 
1802f7aa1c30Sgilles 	if (strlcpy(req_ca_vrfy.name, name, sizeof req_ca_vrfy.name)
1803bc2251d2Sgilles 	    >= sizeof req_ca_vrfy.name)
1804290f027cSgilles 		return 0;
180565c4fdfbSgilles 
18068d3f7f0dSeric 	x = SSL_get_peer_certificate(io_ssl(s->io));
180765c4fdfbSgilles 	if (x == NULL)
180865c4fdfbSgilles 		return 0;
18098d3f7f0dSeric 	xchain = SSL_get_peer_cert_chain(io_ssl(s->io));
181065c4fdfbSgilles 
181165c4fdfbSgilles 	/*
181265c4fdfbSgilles 	 * Client provided a certificate and possibly a certificate chain.
181365c4fdfbSgilles 	 * SMTP can't verify because it does not have the information that
181465c4fdfbSgilles 	 * it needs, instead it will pass the certificate and chain to the
181565c4fdfbSgilles 	 * lookup process and wait for a reply.
181665c4fdfbSgilles 	 *
181765c4fdfbSgilles 	 */
181865c4fdfbSgilles 
1819290f027cSgilles 	cert_len[0] = i2d_X509(x, &cert_der[0]);
1820290f027cSgilles 	X509_free(x);
1821290f027cSgilles 
1822290f027cSgilles 	if (cert_len[0] < 0) {
1823290f027cSgilles 		log_warnx("warn: failed to encode certificate");
1824290f027cSgilles 		goto end;
1825290f027cSgilles 	}
1826290f027cSgilles 	log_debug("debug: certificate 0: len=%d", cert_len[0]);
1827290f027cSgilles 	if (cert_len[0] > (int)MAX_CERT_LEN) {
1828290f027cSgilles 		log_warnx("warn: certificate too long");
1829290f027cSgilles 		goto end;
1830290f027cSgilles 	}
1831290f027cSgilles 
1832290f027cSgilles 	if (xchain) {
1833290f027cSgilles 		cert_count = sk_X509_num(xchain);
1834290f027cSgilles 		log_debug("debug: certificate chain len: %d", cert_count);
1835290f027cSgilles 		if (cert_count >= MAX_CERTS) {
1836290f027cSgilles 			log_warnx("warn: certificate chain too long");
1837290f027cSgilles 			goto end;
1838290f027cSgilles 		}
1839290f027cSgilles 	}
1840290f027cSgilles 	else
1841290f027cSgilles 		cert_count = 0;
1842290f027cSgilles 
1843290f027cSgilles 	for (i = 0; i < cert_count; ++i) {
1844290f027cSgilles 		x = sk_X509_value(xchain, i);
1845290f027cSgilles 		cert_len[i+1] = i2d_X509(x, &cert_der[i+1]);
1846290f027cSgilles 		if (cert_len[i+1] < 0) {
1847290f027cSgilles 			log_warnx("warn: failed to encode certificate");
1848290f027cSgilles 			goto end;
1849290f027cSgilles 		}
1850290f027cSgilles 		log_debug("debug: certificate %i: len=%d", i+1, cert_len[i+1]);
1851290f027cSgilles 		if (cert_len[i+1] > (int)MAX_CERT_LEN) {
1852290f027cSgilles 			log_warnx("warn: certificate too long");
1853290f027cSgilles 			goto end;
1854290f027cSgilles 		}
1855290f027cSgilles 	}
1856290f027cSgilles 
185765c4fdfbSgilles 	tree_xset(&wait_ssl_verify, s->id, s);
185865c4fdfbSgilles 
185965c4fdfbSgilles 	/* Send the client certificate */
186065c4fdfbSgilles 	req_ca_vrfy.reqid = s->id;
1861290f027cSgilles 	req_ca_vrfy.cert_len = cert_len[0];
1862290f027cSgilles 	req_ca_vrfy.n_chain = cert_count;
186365c4fdfbSgilles 	iov[0].iov_base = &req_ca_vrfy;
186465c4fdfbSgilles 	iov[0].iov_len = sizeof(req_ca_vrfy);
1865290f027cSgilles 	iov[1].iov_base = cert_der[0];
1866290f027cSgilles 	iov[1].iov_len = cert_len[0];
1867f80060acSgilles 	m_composev(p_lka, IMSG_SMTP_TLS_VERIFY_CERT, 0, 0, -1,
186865c4fdfbSgilles 	    iov, nitems(iov));
186965c4fdfbSgilles 
1870c1392a69Seric 	memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy);
187165c4fdfbSgilles 	req_ca_vrfy.reqid = s->id;
1872290f027cSgilles 
1873290f027cSgilles 	/* Send the chain, one cert at a time */
1874290f027cSgilles 	for (i = 0; i < cert_count; ++i) {
1875290f027cSgilles 		req_ca_vrfy.cert_len = cert_len[i+1];
1876290f027cSgilles 		iov[1].iov_base = cert_der[i+1];
1877290f027cSgilles 		iov[1].iov_len  = cert_len[i+1];
1878f80060acSgilles 		m_composev(p_lka, IMSG_SMTP_TLS_VERIFY_CHAIN, 0, 0, -1,
187965c4fdfbSgilles 		    iov, nitems(iov));
188065c4fdfbSgilles 	}
188165c4fdfbSgilles 
188265c4fdfbSgilles 	/* Tell lookup process that it can start verifying, we're done */
1883c1392a69Seric 	memset(&req_ca_vrfy, 0, sizeof req_ca_vrfy);
188465c4fdfbSgilles 	req_ca_vrfy.reqid = s->id;
1885f80060acSgilles 	m_compose(p_lka, IMSG_SMTP_TLS_VERIFY, 0, 0, -1,
188665c4fdfbSgilles 	    &req_ca_vrfy, sizeof req_ca_vrfy);
188765c4fdfbSgilles 
1888290f027cSgilles 	res = 1;
1889290f027cSgilles 
1890290f027cSgilles     end:
1891290f027cSgilles 	for (i = 0; i < MAX_CERTS; ++i)
1892290f027cSgilles 		free(cert_der[i]);
1893290f027cSgilles 
1894290f027cSgilles 	return res;
189565c4fdfbSgilles }
1896b6d81129Seric 
1897299c4efeSeric static void
1898299c4efeSeric smtp_auth_failure_resume(int fd, short event, void *p)
1899299c4efeSeric {
1900299c4efeSeric 	struct smtp_session *s = p;
1901299c4efeSeric 
1902299c4efeSeric 	smtp_reply(s, "535 Authentication failed");
1903299c4efeSeric 	smtp_enter_state(s, STATE_HELO);
1904299c4efeSeric }
1905299c4efeSeric 
1906299c4efeSeric static void
1907299c4efeSeric smtp_auth_failure_pause(struct smtp_session *s)
1908299c4efeSeric {
1909299c4efeSeric 	struct timeval	tv;
1910299c4efeSeric 
1911299c4efeSeric 	tv.tv_sec = 0;
1912299c4efeSeric 	tv.tv_usec = arc4random_uniform(1000000);
1913299c4efeSeric 	log_trace(TRACE_SMTP, "smtp: timing-attack protection triggered, "
1914299c4efeSeric 	    "will defer answer for %lu microseconds", tv.tv_usec);
1915299c4efeSeric 	evtimer_set(&s->pause, smtp_auth_failure_resume, s);
1916299c4efeSeric 	evtimer_add(&s->pause, &tv);
1917299c4efeSeric }
1918299c4efeSeric 
19195e3ff182Seric static int
19205e3ff182Seric smtp_tx(struct smtp_session *s)
19215e3ff182Seric {
19225e3ff182Seric 	struct smtp_tx *tx;
19235e3ff182Seric 
19245e3ff182Seric 	tx = calloc(1, sizeof(*tx));
19255e3ff182Seric 	if (tx == NULL)
19265e3ff182Seric 		return 0;
19275e3ff182Seric 
19285e3ff182Seric 	TAILQ_INIT(&tx->rcpts);
19295e3ff182Seric 
19305e3ff182Seric 	s->tx = tx;
19315e3ff182Seric 	tx->session = s;
19325e3ff182Seric 
19335e3ff182Seric 	/* setup the envelope */
193422135240Seric 	tx->evp.ss = s->ss;
193522135240Seric 	(void)strlcpy(tx->evp.tag, s->listener->tag, sizeof(tx->evp.tag));
193622135240Seric 	(void)strlcpy(tx->evp.smtpname, s->smtpname, sizeof(tx->evp.smtpname));
193722135240Seric 	(void)strlcpy(tx->evp.hostname, s->hostname, sizeof tx->evp.hostname);
193822135240Seric 	(void)strlcpy(tx->evp.helo, s->helo, sizeof(tx->evp.helo));
19395e3ff182Seric 
19405e3ff182Seric 	if (s->flags & SF_BOUNCE)
194122135240Seric 		tx->evp.flags |= EF_BOUNCE;
19425e3ff182Seric 	if (s->flags & SF_AUTHENTICATED)
194322135240Seric 		tx->evp.flags |= EF_AUTHENTICATED;
19445e3ff182Seric 
19455e3ff182Seric 	/* Setup parser and callbacks */
19465e3ff182Seric 	rfc2822_parser_init(&tx->rfc2822_parser);
19475e3ff182Seric 	rfc2822_header_default_callback(&tx->rfc2822_parser,
1948eb95ffd1Seric 	    header_default_callback, tx);
19495e3ff182Seric 	rfc2822_header_callback(&tx->rfc2822_parser, "bcc",
1950eb95ffd1Seric 	    header_bcc_callback, tx);
19515e3ff182Seric 	rfc2822_header_callback(&tx->rfc2822_parser, "from",
1952eb95ffd1Seric 	    header_domain_append_callback, tx);
19535e3ff182Seric 	rfc2822_header_callback(&tx->rfc2822_parser, "to",
1954eb95ffd1Seric 	    header_domain_append_callback, tx);
19555e3ff182Seric 	rfc2822_header_callback(&tx->rfc2822_parser, "cc",
1956eb95ffd1Seric 	    header_domain_append_callback, tx);
19575e3ff182Seric 	rfc2822_body_callback(&tx->rfc2822_parser,
1958eb95ffd1Seric 	    dataline_callback, tx);
19595e3ff182Seric 
19605e3ff182Seric 	if (s->listener->local || s->listener->port == htons(587)) {
19615e3ff182Seric 		rfc2822_missing_header_callback(&tx->rfc2822_parser, "date",
1962eb95ffd1Seric 		    header_missing_callback, tx);
19635e3ff182Seric 		rfc2822_missing_header_callback(&tx->rfc2822_parser, "message-id",
1964eb95ffd1Seric 		    header_missing_callback, tx);
19655e3ff182Seric 	}
19665e3ff182Seric 
19675e3ff182Seric 	return 1;
19685e3ff182Seric }
19695e3ff182Seric 
19705e3ff182Seric static void
19715e3ff182Seric smtp_tx_free(struct smtp_tx *tx)
19725e3ff182Seric {
19735e3ff182Seric 	struct smtp_rcpt *rcpt;
19745e3ff182Seric 
19755e3ff182Seric 	rfc2822_parser_release(&tx->rfc2822_parser);
19765e3ff182Seric 
19775e3ff182Seric 	while ((rcpt = TAILQ_FIRST(&tx->rcpts))) {
19785e3ff182Seric 		TAILQ_REMOVE(&tx->rcpts, rcpt, entry);
19795e3ff182Seric 		free(rcpt);
19805e3ff182Seric 	}
19815e3ff182Seric 
19825e3ff182Seric 	if (tx->ofile)
19835e3ff182Seric 		fclose(tx->ofile);
19845e3ff182Seric 
19855e3ff182Seric 	tx->session->tx = NULL;
19865e3ff182Seric 
19875e3ff182Seric 	free(tx);
19885e3ff182Seric }
19895e3ff182Seric 
199008becfa3Seric static void
199172cde3cbSeric smtp_tx_mail_from(struct smtp_tx *tx, char *line)
199272cde3cbSeric {
199372cde3cbSeric 	if (smtp_mailaddr(&tx->evp.sender, line, 1, &line,
199472cde3cbSeric 		tx->session->smtpname) == 0) {
199572cde3cbSeric 		smtp_reply(tx->session, "553 %s: Sender address syntax error",
199672cde3cbSeric 		    esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS));
1997*4768c36cSeric 		smtp_tx_free(tx);
199872cde3cbSeric 		return;
199972cde3cbSeric 	}
200072cde3cbSeric 
200172cde3cbSeric 	if (line && smtp_parse_mail_args(tx->session, line) == -1) {
200272cde3cbSeric 		smtp_tx_free(tx);
200372cde3cbSeric 		return;
200472cde3cbSeric 	}
200572cde3cbSeric 
200672cde3cbSeric 	/* only check sendertable if defined and user has authenticated */
200772cde3cbSeric 	if (tx->session->flags & SF_AUTHENTICATED &&
200872cde3cbSeric 	    tx->session->listener->sendertable[0]) {
200972cde3cbSeric 		m_create(p_lka, IMSG_SMTP_CHECK_SENDER, 0, 0, -1);
201072cde3cbSeric 		m_add_id(p_lka, tx->session->id);
201172cde3cbSeric 		m_add_string(p_lka, tx->session->listener->sendertable);
201272cde3cbSeric 		m_add_string(p_lka, tx->session->username);
201372cde3cbSeric 		m_add_mailaddr(p_lka, &tx->evp.sender);
201472cde3cbSeric 		m_close(p_lka);
201572cde3cbSeric 		tree_xset(&wait_lka_mail, tx->session->id, tx->session);
201672cde3cbSeric 	}
201772cde3cbSeric 	else
201872cde3cbSeric 		smtp_tx_create_message(tx);
201972cde3cbSeric }
202072cde3cbSeric 
202172cde3cbSeric static void
2022008f3f72Seric smtp_tx_create_message(struct smtp_tx *tx)
2023a2e141b1Seric {
2024a2e141b1Seric 	m_create(p_queue, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1);
2025008f3f72Seric 	m_add_id(p_queue, tx->session->id);
2026a2e141b1Seric 	m_close(p_queue);
2027008f3f72Seric 	tree_xset(&wait_queue_msg, tx->session->id, tx->session);
2028a2e141b1Seric }
2029a2e141b1Seric 
2030a2e141b1Seric static void
203172cde3cbSeric smtp_tx_rcpt_to(struct smtp_tx *tx, char *line)
203272cde3cbSeric {
203372cde3cbSeric 	if (tx->rcptcount >= env->sc_session_max_rcpt) {
203472cde3cbSeric 		smtp_reply(tx->session, "451 %s %s: Too many recipients",
203572cde3cbSeric 		    esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
203672cde3cbSeric 		    esc_description(ESC_TOO_MANY_RECIPIENTS));
203772cde3cbSeric 		return;
203872cde3cbSeric 	}
203972cde3cbSeric 
204072cde3cbSeric 	if (smtp_mailaddr(&tx->evp.rcpt, line, 0, &line,
204172cde3cbSeric 	    tx->session->smtpname) == 0) {
204272cde3cbSeric 		smtp_reply(tx->session,
204372cde3cbSeric 		    "501 %s: Recipient address syntax error",
204472cde3cbSeric 		    esc_code(ESC_STATUS_PERMFAIL,
204572cde3cbSeric 		        ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX));
204672cde3cbSeric 		return;
204772cde3cbSeric 	}
204872cde3cbSeric 
204972cde3cbSeric 	if (line && smtp_parse_rcpt_args(tx->session, line) == -1)
205072cde3cbSeric 		return;
205172cde3cbSeric 
205272cde3cbSeric 	m_create(p_lka, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1);
205372cde3cbSeric 	m_add_id(p_lka, tx->session->id);
205472cde3cbSeric 	m_add_envelope(p_lka, &tx->evp);
205572cde3cbSeric 	m_close(p_lka);
205672cde3cbSeric 	tree_xset(&wait_lka_rcpt, tx->session->id, tx->session);
205772cde3cbSeric }
205872cde3cbSeric 
205972cde3cbSeric static void
2060008f3f72Seric smtp_tx_open_message(struct smtp_tx *tx)
2061a2e141b1Seric {
2062a2e141b1Seric 	m_create(p_queue, IMSG_SMTP_MESSAGE_OPEN, 0, 0, -1);
2063008f3f72Seric 	m_add_id(p_queue, tx->session->id);
2064008f3f72Seric 	m_add_msgid(p_queue, tx->msgid);
2065a2e141b1Seric 	m_close(p_queue);
2066008f3f72Seric 	tree_xset(&wait_queue_fd, tx->session->id, tx->session);
2067a2e141b1Seric }
2068a2e141b1Seric 
2069a2e141b1Seric static void
2070008f3f72Seric smtp_tx_commit(struct smtp_tx *tx)
2071a2e141b1Seric {
2072a2e141b1Seric 	m_create(p_queue, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1);
2073008f3f72Seric 	m_add_id(p_queue, tx->session->id);
2074008f3f72Seric 	m_add_msgid(p_queue, tx->msgid);
2075a2e141b1Seric 	m_close(p_queue);
2076008f3f72Seric 	tree_xset(&wait_queue_commit, tx->session->id, tx->session);
2077a2e141b1Seric }
2078a2e141b1Seric 
2079a2e141b1Seric static void
2080008f3f72Seric smtp_tx_rollback(struct smtp_tx *tx)
2081a2e141b1Seric {
2082a2e141b1Seric 	m_create(p_queue, IMSG_SMTP_MESSAGE_ROLLBACK, 0, 0, -1);
2083008f3f72Seric 	m_add_msgid(p_queue, tx->msgid);
2084a2e141b1Seric 	m_close(p_queue);
2085a2e141b1Seric }
2086a2e141b1Seric 
20875bfc3fefSeric static int
2088008f3f72Seric smtp_tx_dataline(struct smtp_tx *tx, const char *line)
2089a1bd4550Sgilles {
2090f7c2eb5aSgilles 	int	ret;
2091f7c2eb5aSgilles 
2092ad48cc20Seric 	log_trace(TRACE_SMTP, "<<< [MSG] %s", line);
2093ad48cc20Seric 
20945bfc3fefSeric 	if (!strcmp(line, ".")) {
20955bfc3fefSeric 		log_trace(TRACE_SMTP, "<<< [EOM]");
2096008f3f72Seric 		if (!tx->error)
2097008f3f72Seric 			rfc2822_parser_flush(&tx->rfc2822_parser);
20985bfc3fefSeric 		return 1;
20995bfc3fefSeric 	}
21005bfc3fefSeric 
2101f299ff90Seric 	/* ignore data line if an error is set */
2102008f3f72Seric 	if (tx->error)
21035bfc3fefSeric 		return 0;
2104f7c2eb5aSgilles 
2105ad48cc20Seric 	/* escape lines starting with a '.' */
2106ad48cc20Seric 	if (line[0] == '.')
2107ad48cc20Seric 		line += 1;
2108f7c2eb5aSgilles 
2109f7c2eb5aSgilles 	/* account for newline */
2110008f3f72Seric 	tx->datain += strlen(line) + 1;
2111008f3f72Seric 	if (tx->datain > env->sc_maxsize) {
2112008f3f72Seric 		tx->error = TX_ERROR_SIZE;
21135bfc3fefSeric 		return 0;
2114f7c2eb5aSgilles 	}
2115f7c2eb5aSgilles 
2116008f3f72Seric 	if (!tx->hdrdone) {
2117ad48cc20Seric 
2118ad48cc20Seric 		/* folded header that must be skipped */
2119008f3f72Seric 		if (isspace((unsigned char)line[0]) && tx->skiphdr)
21205bfc3fefSeric 			return 0;
2121008f3f72Seric 		tx->skiphdr = 0;
2122ad48cc20Seric 
2123ad48cc20Seric 		/* BCC should be stripped from headers */
2124ad48cc20Seric 		if (strncasecmp("bcc:", line, 4) == 0) {
2125008f3f72Seric 			tx->skiphdr = 1;
21265bfc3fefSeric 			return 0;
2127ad48cc20Seric 		}
2128ad48cc20Seric 
2129ad48cc20Seric 		/* check for loop */
2130f7c2eb5aSgilles 		if (strncasecmp("Received: ", line, 10) == 0)
2131008f3f72Seric 			tx->rcvcount++;
2132008f3f72Seric 		if (tx->rcvcount == MAX_HOPS_COUNT) {
2133008f3f72Seric 			tx->error = TX_ERROR_LOOP;
213471130655Smillert 			log_warnx("warn: loop detected");
21355bfc3fefSeric 			return 0;
2136f7c2eb5aSgilles 		}
2137ad48cc20Seric 
2138ad48cc20Seric 		if (line[0] == '\0')
2139008f3f72Seric 			tx->hdrdone = 1;
2140f7c2eb5aSgilles 	}
2141f7c2eb5aSgilles 
2142008f3f72Seric 	ret = rfc2822_parser_feed(&tx->rfc2822_parser, line);
21435bfc3fefSeric 	if (ret == -1)
2144008f3f72Seric 		tx->error = TX_ERROR_RESOURCES;
2145f7c2eb5aSgilles 
21465bfc3fefSeric 	if (ret == 0)
2147008f3f72Seric 		tx->error = TX_ERROR_MALFORMED;
21485bfc3fefSeric 
21495bfc3fefSeric 	return 0;
2150a1bd4550Sgilles }
2151a1bd4550Sgilles 
215254355315Seric static void
215354355315Seric smtp_message_fd(struct smtp_tx *tx, int fd)
215454355315Seric {
215554355315Seric 	struct smtp_session *s;
215654355315Seric 	X509 *x;
215754355315Seric 
215854355315Seric 	s = tx->session;
215954355315Seric 
216054355315Seric 	log_debug("smtp: %p: message fd %d", s, fd);
216154355315Seric 
216222135240Seric 	if ((tx->ofile = fdopen(fd, "w")) == NULL) {
216354355315Seric 		close(fd);
216454355315Seric 		smtp_reply(s, "421 %s: Temporary Error",
216554355315Seric 		    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
216654355315Seric 		smtp_enter_state(s, STATE_QUIT);
216754355315Seric 		return;
216854355315Seric 	}
216954355315Seric 
217054355315Seric 	smtp_message_printf(tx, "Received: ");
217154355315Seric 	if (!(s->listener->flags & F_MASK_SOURCE)) {
217254355315Seric 		smtp_message_printf(tx, "from %s (%s [%s])",
217354355315Seric 		    s->helo,
217454355315Seric 		    s->hostname,
217554355315Seric 		    ss_to_text(&s->ss));
217654355315Seric 	}
217754355315Seric 	smtp_message_printf(tx, "\n\tby %s (%s) with %sSMTP%s%s id %08x",
217854355315Seric 	    s->smtpname,
217954355315Seric 	    SMTPD_NAME,
218054355315Seric 	    s->flags & SF_EHLO ? "E" : "",
218154355315Seric 	    s->flags & SF_SECURE ? "S" : "",
218254355315Seric 	    s->flags & SF_AUTHENTICATED ? "A" : "",
218322135240Seric 	    tx->msgid);
218454355315Seric 
218554355315Seric 	if (s->flags & SF_SECURE) {
218654355315Seric 		x = SSL_get_peer_certificate(io_ssl(s->io));
218754355315Seric 		smtp_message_printf(tx, " (%s:%s:%d:%s)",
218854355315Seric 		    SSL_get_version(io_ssl(s->io)),
218954355315Seric 		    SSL_get_cipher_name(io_ssl(s->io)),
219054355315Seric 		    SSL_get_cipher_bits(io_ssl(s->io), NULL),
219154355315Seric 		    (s->flags & SF_VERIFIED) ? "YES" : (x ? "FAIL" : "NO"));
219254355315Seric 		X509_free(x);
219354355315Seric 
219454355315Seric 		if (s->listener->flags & F_RECEIVEDAUTH) {
219554355315Seric 			smtp_message_printf(tx, " auth=%s",
219654355315Seric 			    s->username[0] ? "yes" : "no");
219754355315Seric 			if (s->username[0])
219854355315Seric 				smtp_message_printf(tx, " user=%s", s->username);
219954355315Seric 		}
220054355315Seric 	}
220154355315Seric 
220254355315Seric 	if (tx->rcptcount == 1) {
220354355315Seric 		smtp_message_printf(tx, "\n\tfor <%s@%s>",
220454355315Seric 		    tx->evp.rcpt.user,
220554355315Seric 		    tx->evp.rcpt.domain);
220654355315Seric 	}
220754355315Seric 
220854355315Seric 	smtp_message_printf(tx, ";\n\t%s\n", time_to_text(time(&tx->time)));
220954355315Seric 
221054355315Seric 	smtp_enter_state(s, STATE_BODY);
221154355315Seric 	smtp_reply(s, "354 Enter mail, end with \".\""
221254355315Seric 	    " on a line by itself");
221354355315Seric }
221454355315Seric 
221554355315Seric static void
221654355315Seric smtp_message_end(struct smtp_tx *tx)
221754355315Seric {
221854355315Seric 	struct smtp_session *s;
221954355315Seric 
222054355315Seric 	s = tx->session;
222154355315Seric 
222254355315Seric 	log_debug("debug: %p: end of message, error=%d", s, tx->error);
222354355315Seric 
222454355315Seric 	fclose(tx->ofile);
222554355315Seric 	tx->ofile = NULL;
222654355315Seric 
222754355315Seric 	switch(tx->error) {
222854355315Seric 	case TX_OK:
222954355315Seric 		smtp_tx_commit(tx);
223054355315Seric 		return;
223154355315Seric 
223254355315Seric 	case TX_ERROR_SIZE:
223354355315Seric 		smtp_reply(s, "554 %s %s: Transaction failed, message too big",
223454355315Seric 		    esc_code(ESC_STATUS_PERMFAIL, ESC_MESSAGE_TOO_BIG_FOR_SYSTEM),
223554355315Seric 		    esc_description(ESC_MESSAGE_TOO_BIG_FOR_SYSTEM));
223654355315Seric 		break;
223754355315Seric 
223854355315Seric 	case TX_ERROR_LOOP:
223954355315Seric 		smtp_reply(s, "500 %s %s: Loop detected",
224054355315Seric 		   esc_code(ESC_STATUS_PERMFAIL, ESC_ROUTING_LOOP_DETECTED),
224154355315Seric 		   esc_description(ESC_ROUTING_LOOP_DETECTED));
224254355315Seric 		break;
224354355315Seric 
224454355315Seric 	case TX_ERROR_MALFORMED:
224554355315Seric 		smtp_reply(s, "550 %s %s: Message is not RFC 2822 compliant",
224654355315Seric 		    esc_code(ESC_STATUS_PERMFAIL, ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED),
224754355315Seric 		    esc_description(ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED));
224854355315Seric 		break;
224954355315Seric 
225054355315Seric 	case TX_ERROR_IO:
225154355315Seric 	case TX_ERROR_RESOURCES:
225254355315Seric 		smtp_reply(s, "421 %s: Temporary Error",
225354355315Seric 		    esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
225454355315Seric 		break;
225554355315Seric 
225654355315Seric 	default:
225754355315Seric 		/* fatal? */
225854355315Seric 		smtp_reply(s, "421 Internal server error");
225954355315Seric 	}
226054355315Seric 
226154355315Seric 	smtp_tx_rollback(tx);
226254355315Seric 	smtp_tx_free(tx);
226354355315Seric 	smtp_enter_state(s, STATE_HELO);
226454355315Seric }
226554355315Seric 
226654355315Seric static int
226754355315Seric smtp_message_printf(struct smtp_tx *tx, const char *fmt, ...)
226854355315Seric {
226954355315Seric 	va_list	ap;
227054355315Seric 	int	len;
227154355315Seric 
227254355315Seric 	if (tx->error)
227354355315Seric 		return -1;
227454355315Seric 
227554355315Seric 	va_start(ap, fmt);
227654355315Seric 	len = vfprintf(tx->ofile, fmt, ap);
227754355315Seric 	va_end(ap);
227854355315Seric 
227954355315Seric 	if (len < 0) {
228054355315Seric 		log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id);
228154355315Seric 		tx->error = TX_ERROR_IO;
228254355315Seric 	}
228354355315Seric 	else
228454355315Seric 		tx->odatalen += len;
228554355315Seric 
228654355315Seric 	return len;
228754355315Seric }
228854355315Seric 
2289b6d81129Seric #define CASE(x) case x : return #x
2290b6d81129Seric 
2291b6d81129Seric const char *
229265c4fdfbSgilles smtp_strstate(int state)
2293b6d81129Seric {
2294b6d81129Seric 	static char	buf[32];
2295b6d81129Seric 
2296b6d81129Seric 	switch (state) {
229765c4fdfbSgilles 	CASE(STATE_NEW);
229865c4fdfbSgilles 	CASE(STATE_CONNECTED);
229965c4fdfbSgilles 	CASE(STATE_TLS);
230065c4fdfbSgilles 	CASE(STATE_HELO);
230165c4fdfbSgilles 	CASE(STATE_AUTH_INIT);
230265c4fdfbSgilles 	CASE(STATE_AUTH_USERNAME);
230365c4fdfbSgilles 	CASE(STATE_AUTH_PASSWORD);
230465c4fdfbSgilles 	CASE(STATE_AUTH_FINALIZE);
230565c4fdfbSgilles 	CASE(STATE_BODY);
230665c4fdfbSgilles 	CASE(STATE_QUIT);
2307b6d81129Seric 	default:
2308ef75c9feSgilles 		(void)snprintf(buf, sizeof(buf), "STATE_??? (%d)", state);
230965c4fdfbSgilles 		return (buf);
2310b6d81129Seric 	}
2311b6d81129Seric }
2312