aboutsummaryrefslogtreecommitdiffstats
path: root/smtp_session.c
diff options
context:
space:
mode:
Diffstat (limited to 'smtp_session.c')
-rw-r--r--smtp_session.c3213
1 files changed, 0 insertions, 3213 deletions
diff --git a/smtp_session.c b/smtp_session.c
deleted file mode 100644
index 5ea9f89e..00000000
--- a/smtp_session.c
+++ /dev/null
@@ -1,3213 +0,0 @@
-/* $OpenBSD: smtp_session.c,v 1.426 2020/04/24 11:34:07 eric Exp $ */
-
-/*
- * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
- * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
- * Copyright (c) 2008-2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
- * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/types.h>
-#include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/socket.h>
-#include <sys/uio.h>
-
-#include <netinet/in.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <limits.h>
-#include <inttypes.h>
-#include <openssl/ssl.h>
-#include <resolv.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <vis.h>
-
-#include "smtpd.h"
-#include "log.h"
-#include "ssl.h"
-#include "rfc5322.h"
-
-#define SMTP_LINE_MAX 65535
-#define DATA_HIWAT 65535
-#define APPEND_DOMAIN_BUFFER_SIZE SMTP_LINE_MAX
-
-enum smtp_state {
- STATE_NEW = 0,
- STATE_CONNECTED,
- STATE_TLS,
- STATE_HELO,
- STATE_AUTH_INIT,
- STATE_AUTH_USERNAME,
- STATE_AUTH_PASSWORD,
- STATE_AUTH_FINALIZE,
- STATE_BODY,
- STATE_QUIT,
-};
-
-enum session_flags {
- SF_EHLO = 0x0001,
- SF_8BITMIME = 0x0002,
- SF_SECURE = 0x0004,
- SF_AUTHENTICATED = 0x0008,
- SF_BOUNCE = 0x0010,
- SF_VERIFIED = 0x0020,
- SF_BADINPUT = 0x0080,
-};
-
-enum {
- TX_OK = 0,
- TX_ERROR_ENVELOPE,
- TX_ERROR_SIZE,
- TX_ERROR_IO,
- TX_ERROR_LOOP,
- TX_ERROR_MALFORMED,
- TX_ERROR_RESOURCES,
- TX_ERROR_INTERNAL,
-};
-
-enum smtp_command {
- CMD_HELO = 0,
- CMD_EHLO,
- CMD_STARTTLS,
- CMD_AUTH,
- CMD_MAIL_FROM,
- CMD_RCPT_TO,
- CMD_DATA,
- CMD_RSET,
- CMD_QUIT,
- CMD_HELP,
- CMD_WIZ,
- CMD_NOOP,
- CMD_COMMIT,
-};
-
-struct smtp_rcpt {
- TAILQ_ENTRY(smtp_rcpt) entry;
- uint64_t evpid;
- struct mailaddr maddr;
- size_t destcount;
-};
-
-struct smtp_tx {
- struct smtp_session *session;
- uint32_t msgid;
-
- struct envelope evp;
- size_t rcptcount;
- size_t destcount;
- TAILQ_HEAD(, smtp_rcpt) rcpts;
-
- time_t time;
- int error;
- size_t datain;
- size_t odatalen;
- FILE *ofile;
- struct io *filter;
- struct rfc5322_parser *parser;
- int rcvcount;
- int has_date;
- int has_message_id;
-
- uint8_t junk;
-};
-
-struct smtp_session {
- uint64_t id;
- struct io *io;
- struct listener *listener;
- void *ssl_ctx;
- struct sockaddr_storage ss;
- char rdns[HOST_NAME_MAX+1];
- char smtpname[HOST_NAME_MAX+1];
- int fcrdns;
-
- int flags;
- enum smtp_state state;
-
- uint8_t banner_sent;
- char helo[LINE_MAX];
- char cmd[LINE_MAX];
- char username[SMTPD_MAXMAILADDRSIZE];
-
- size_t mailcount;
- struct event pause;
-
- struct smtp_tx *tx;
-
- enum smtp_command last_cmd;
- enum filter_phase filter_phase;
- const char *filter_param;
-
- uint8_t junk;
-};
-
-#define ADVERTISE_TLS(s) \
- ((s)->listener->flags & F_STARTTLS && !((s)->flags & SF_SECURE))
-
-#define ADVERTISE_AUTH(s) \
- ((s)->listener->flags & F_AUTH && (s)->flags & SF_SECURE && \
- !((s)->flags & SF_AUTHENTICATED))
-
-#define ADVERTISE_EXT_DSN(s) \
- ((s)->listener->flags & F_EXT_DSN)
-
-#define SESSION_FILTERED(s) \
- ((s)->listener->flags & F_FILTERED)
-
-#define SESSION_DATA_FILTERED(s) \
- ((s)->listener->flags & F_FILTERED)
-
-
-static int smtp_mailaddr(struct mailaddr *, char *, int, char **, const char *);
-static void smtp_session_init(void);
-static void smtp_lookup_servername(struct smtp_session *);
-static void smtp_getnameinfo_cb(void *, int, const char *, const char *);
-static void smtp_getaddrinfo_cb(void *, int, struct addrinfo *);
-static void smtp_connected(struct smtp_session *);
-static void smtp_send_banner(struct smtp_session *);
-static void smtp_tls_verified(struct smtp_session *);
-static void smtp_io(struct io *, int, void *);
-static void smtp_enter_state(struct smtp_session *, int);
-static void smtp_reply(struct smtp_session *, char *, ...);
-static void smtp_command(struct smtp_session *, char *);
-static void smtp_rfc4954_auth_plain(struct smtp_session *, char *);
-static void smtp_rfc4954_auth_login(struct smtp_session *, char *);
-static void smtp_free(struct smtp_session *, const char *);
-static const char *smtp_strstate(int);
-static void smtp_cert_init(struct smtp_session *);
-static void smtp_cert_init_cb(void *, int, const char *, const void *, size_t);
-static void smtp_cert_verify(struct smtp_session *);
-static void smtp_cert_verify_cb(void *, int);
-static void smtp_auth_failure_pause(struct smtp_session *);
-static void smtp_auth_failure_resume(int, short, void *);
-
-static int smtp_tx(struct smtp_session *);
-static void smtp_tx_free(struct smtp_tx *);
-static void smtp_tx_create_message(struct smtp_tx *);
-static void smtp_tx_mail_from(struct smtp_tx *, const char *);
-static void smtp_tx_rcpt_to(struct smtp_tx *, const char *);
-static void smtp_tx_open_message(struct smtp_tx *);
-static void smtp_tx_commit(struct smtp_tx *);
-static void smtp_tx_rollback(struct smtp_tx *);
-static int smtp_tx_dataline(struct smtp_tx *, const char *);
-static int smtp_tx_filtered_dataline(struct smtp_tx *, const char *);
-static void smtp_tx_eom(struct smtp_tx *);
-static void smtp_filter_fd(struct smtp_tx *, int);
-static int smtp_message_fd(struct smtp_tx *, int);
-static void smtp_message_begin(struct smtp_tx *);
-static void smtp_message_end(struct smtp_tx *);
-static int smtp_filter_printf(struct smtp_tx *, const char *, ...)
- __attribute__((__format__ (printf, 2, 3)));
-static int smtp_message_printf(struct smtp_tx *, const char *, ...)
- __attribute__((__format__ (printf, 2, 3)));
-
-static int smtp_check_rset(struct smtp_session *, const char *);
-static int smtp_check_helo(struct smtp_session *, const char *);
-static int smtp_check_ehlo(struct smtp_session *, const char *);
-static int smtp_check_auth(struct smtp_session *s, const char *);
-static int smtp_check_starttls(struct smtp_session *, const char *);
-static int smtp_check_mail_from(struct smtp_session *, const char *);
-static int smtp_check_rcpt_to(struct smtp_session *, const char *);
-static int smtp_check_data(struct smtp_session *, const char *);
-static int smtp_check_noparam(struct smtp_session *, const char *);
-
-static void smtp_filter_phase(enum filter_phase, struct smtp_session *, const char *);
-
-static void smtp_proceed_connected(struct smtp_session *);
-static void smtp_proceed_rset(struct smtp_session *, const char *);
-static void smtp_proceed_helo(struct smtp_session *, const char *);
-static void smtp_proceed_ehlo(struct smtp_session *, const char *);
-static void smtp_proceed_auth(struct smtp_session *, const char *);
-static void smtp_proceed_starttls(struct smtp_session *, const char *);
-static void smtp_proceed_mail_from(struct smtp_session *, const char *);
-static void smtp_proceed_rcpt_to(struct smtp_session *, const char *);
-static void smtp_proceed_data(struct smtp_session *, const char *);
-static void smtp_proceed_noop(struct smtp_session *, const char *);
-static void smtp_proceed_help(struct smtp_session *, const char *);
-static void smtp_proceed_wiz(struct smtp_session *, const char *);
-static void smtp_proceed_quit(struct smtp_session *, const char *);
-static void smtp_proceed_commit(struct smtp_session *, const char *);
-static void smtp_proceed_rollback(struct smtp_session *, const char *);
-
-static void smtp_filter_begin(struct smtp_session *);
-static void smtp_filter_end(struct smtp_session *);
-static void smtp_filter_data_begin(struct smtp_session *);
-static void smtp_filter_data_end(struct smtp_session *);
-
-static void smtp_report_link_connect(struct smtp_session *, const char *, int,
- const struct sockaddr_storage *,
- const struct sockaddr_storage *);
-static void smtp_report_link_greeting(struct smtp_session *, const char *);
-static void smtp_report_link_identify(struct smtp_session *, const char *, const char *);
-static void smtp_report_link_tls(struct smtp_session *, const char *);
-static void smtp_report_link_disconnect(struct smtp_session *);
-static void smtp_report_link_auth(struct smtp_session *, const char *, const char *);
-static void smtp_report_tx_reset(struct smtp_session *, uint32_t);
-static void smtp_report_tx_begin(struct smtp_session *, uint32_t);
-static void smtp_report_tx_mail(struct smtp_session *, uint32_t, const char *, int);
-static void smtp_report_tx_rcpt(struct smtp_session *, uint32_t, const char *, int);
-static void smtp_report_tx_envelope(struct smtp_session *, uint32_t, uint64_t);
-static void smtp_report_tx_data(struct smtp_session *, uint32_t, int);
-static void smtp_report_tx_commit(struct smtp_session *, uint32_t, size_t);
-static void smtp_report_tx_rollback(struct smtp_session *, uint32_t);
-static void smtp_report_protocol_client(struct smtp_session *, const char *);
-static void smtp_report_protocol_server(struct smtp_session *, const char *);
-static void smtp_report_filter_response(struct smtp_session *, int, int, const char *);
-static void smtp_report_timeout(struct smtp_session *);
-
-
-static struct {
- int code;
- enum filter_phase filter_phase;
- const char *cmd;
-
- int (*check)(struct smtp_session *, const char *);
- void (*proceed)(struct smtp_session *, const char *);
-} commands[] = {
- { CMD_HELO, FILTER_HELO, "HELO", smtp_check_helo, smtp_proceed_helo },
- { CMD_EHLO, FILTER_EHLO, "EHLO", smtp_check_ehlo, smtp_proceed_ehlo },
- { CMD_STARTTLS, FILTER_STARTTLS, "STARTTLS", smtp_check_starttls, smtp_proceed_starttls },
- { CMD_AUTH, FILTER_AUTH, "AUTH", smtp_check_auth, smtp_proceed_auth },
- { CMD_MAIL_FROM, FILTER_MAIL_FROM, "MAIL FROM", smtp_check_mail_from, smtp_proceed_mail_from },
- { CMD_RCPT_TO, FILTER_RCPT_TO, "RCPT TO", smtp_check_rcpt_to, smtp_proceed_rcpt_to },
- { CMD_DATA, FILTER_DATA, "DATA", smtp_check_data, smtp_proceed_data },
- { CMD_RSET, FILTER_RSET, "RSET", smtp_check_rset, smtp_proceed_rset },
- { CMD_QUIT, FILTER_QUIT, "QUIT", smtp_check_noparam, smtp_proceed_quit },
- { CMD_NOOP, FILTER_NOOP, "NOOP", smtp_check_noparam, smtp_proceed_noop },
- { CMD_HELP, FILTER_HELP, "HELP", smtp_check_noparam, smtp_proceed_help },
- { CMD_WIZ, FILTER_WIZ, "WIZ", smtp_check_noparam, smtp_proceed_wiz },
- { CMD_COMMIT, FILTER_COMMIT, ".", smtp_check_noparam, smtp_proceed_commit },
- { -1, 0, NULL, NULL },
-};
-
-static struct tree wait_lka_helo;
-static struct tree wait_lka_mail;
-static struct tree wait_lka_rcpt;
-static struct tree wait_parent_auth;
-static struct tree wait_queue_msg;
-static struct tree wait_queue_fd;
-static struct tree wait_queue_commit;
-static struct tree wait_ssl_init;
-static struct tree wait_ssl_verify;
-static struct tree wait_filters;
-static struct tree wait_filter_fd;
-
-static void
-header_append_domain_buffer(char *buffer, char *domain, size_t len)
-{
- size_t i;
- int escape, quote, comment, bracket;
- int has_domain, has_bracket, has_group;
- int pos_bracket, pos_component, pos_insert;
- char copy[APPEND_DOMAIN_BUFFER_SIZE];
-
- escape = quote = comment = bracket = 0;
- has_domain = has_bracket = has_group = 0;
- pos_bracket = pos_insert = pos_component = 0;
- for (i = 0; buffer[i]; ++i) {
- if (buffer[i] == '(' && !escape && !quote)
- comment++;
- if (buffer[i] == '"' && !escape && !comment)
- quote = !quote;
- if (buffer[i] == ')' && !escape && !quote && comment)
- comment--;
- if (buffer[i] == '\\' && !escape && !comment && !quote)
- escape = 1;
- else
- escape = 0;
- if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) {
- bracket++;
- has_bracket = 1;
- }
- if (buffer[i] == '>' && !escape && !comment && !quote && bracket) {
- bracket--;
- pos_bracket = i;
- }
- if (buffer[i] == '@' && !escape && !comment && !quote)
- has_domain = 1;
- if (buffer[i] == ':' && !escape && !comment && !quote)
- has_group = 1;
-
- /* update insert point if not in comment and not on a whitespace */
- if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i]))
- pos_component = i;
- }
-
- /* parse error, do not attempt to modify */
- if (escape || quote || comment || bracket)
- return;
-
- /* domain already present, no need to modify */
- if (has_domain)
- return;
-
- /* address is group, skip */
- if (has_group)
- return;
-
- /* there's an address between brackets, just append domain */
- if (has_bracket) {
- pos_bracket--;
- while (isspace((unsigned char)buffer[pos_bracket]))
- pos_bracket--;
- if (buffer[pos_bracket] == '<')
- return;
- pos_insert = pos_bracket + 1;
- }
- else {
- /* otherwise append address to last component */
- pos_insert = pos_component + 1;
-
- /* empty address */
- if (buffer[pos_component] == '\0' ||
- isspace((unsigned char)buffer[pos_component]))
- return;
- }
-
- if (snprintf(copy, sizeof copy, "%.*s@%s%s",
- (int)pos_insert, buffer,
- domain,
- buffer+pos_insert) >= (int)sizeof copy)
- return;
-
- memcpy(buffer, copy, len);
-}
-
-static void
-header_address_rewrite_buffer(char *buffer, const char *address, size_t len)
-{
- size_t i;
- int address_len;
- int escape, quote, comment, bracket;
- int has_bracket, has_group;
- int pos_bracket_beg, pos_bracket_end, pos_component_beg, pos_component_end;
- int insert_beg, insert_end;
- char copy[APPEND_DOMAIN_BUFFER_SIZE];
-
- escape = quote = comment = bracket = 0;
- has_bracket = has_group = 0;
- pos_bracket_beg = pos_bracket_end = pos_component_beg = pos_component_end = 0;
- for (i = 0; buffer[i]; ++i) {
- if (buffer[i] == '(' && !escape && !quote)
- comment++;
- if (buffer[i] == '"' && !escape && !comment)
- quote = !quote;
- if (buffer[i] == ')' && !escape && !quote && comment)
- comment--;
- if (buffer[i] == '\\' && !escape && !comment && !quote)
- escape = 1;
- else
- escape = 0;
- if (buffer[i] == '<' && !escape && !comment && !quote && !bracket) {
- bracket++;
- has_bracket = 1;
- pos_bracket_beg = i+1;
- }
- if (buffer[i] == '>' && !escape && !comment && !quote && bracket) {
- bracket--;
- pos_bracket_end = i;
- }
- if (buffer[i] == ':' && !escape && !comment && !quote)
- has_group = 1;
-
- /* update insert point if not in comment and not on a whitespace */
- if (!comment && buffer[i] != ')' && !isspace((unsigned char)buffer[i]))
- pos_component_end = i;
- }
-
- /* parse error, do not attempt to modify */
- if (escape || quote || comment || bracket)
- return;
-
- /* address is group, skip */
- if (has_group)
- return;
-
- /* there's an address between brackets, just replace everything brackets */
- if (has_bracket) {
- insert_beg = pos_bracket_beg;
- insert_end = pos_bracket_end;
- }
- else {
- if (pos_component_end == 0)
- pos_component_beg = 0;
- else {
- for (pos_component_beg = pos_component_end; pos_component_beg >= 0; --pos_component_beg)
- if (buffer[pos_component_beg] == ')' || isspace((unsigned char)buffer[pos_component_beg]))
- break;
- pos_component_beg += 1;
- pos_component_end += 1;
- }
- insert_beg = pos_component_beg;
- insert_end = pos_component_end;
- }
-
- /* check that masquerade won' t overflow */
- address_len = strlen(address);
- if (strlen(buffer) - (insert_end - insert_beg) + address_len >= len)
- return;
-
- (void)strlcpy(copy, buffer, sizeof copy);
- (void)strlcpy(copy+insert_beg, address, sizeof (copy) - insert_beg);
- (void)strlcat(copy, buffer+insert_end, sizeof (copy));
- memcpy(buffer, copy, len);
-}
-
-static void
-header_domain_append_callback(struct smtp_tx *tx, const char *hdr,
- const char *val)
-{
- size_t i, j, linelen;
- int escape, quote, comment, skip;
- char buffer[APPEND_DOMAIN_BUFFER_SIZE];
- const char *line, *end;
-
- if (smtp_message_printf(tx, "%s:", hdr) == -1)
- return;
-
- j = 0;
- escape = quote = comment = skip = 0;
- memset(buffer, 0, sizeof buffer);
-
- for (line = val; line; line = end) {
- end = strchr(line, '\n');
- if (end) {
- linelen = end - line;
- end++;
- }
- else
- linelen = strlen(line);
-
- for (i = 0; i < linelen; ++i) {
- if (line[i] == '(' && !escape && !quote)
- comment++;
- if (line[i] == '"' && !escape && !comment)
- quote = !quote;
- if (line[i] == ')' && !escape && !quote && comment)
- comment--;
- if (line[i] == '\\' && !escape && !comment && !quote)
- escape = 1;
- else
- escape = 0;
-
- /* found a separator, buffer contains a full address */
- if (line[i] == ',' && !escape && !quote && !comment) {
- if (!skip && j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) {
- header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer);
- if (tx->session->flags & SF_AUTHENTICATED &&
- tx->session->listener->sendertable[0] &&
- tx->session->listener->flags & F_MASQUERADE &&
- !(strcasecmp(hdr, "From")))
- header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender),
- sizeof buffer);
- }
- if (smtp_message_printf(tx, "%s,", buffer) == -1)
- return;
- j = 0;
- skip = 0;
- memset(buffer, 0, sizeof buffer);
- }
- else {
- if (skip) {
- if (smtp_message_printf(tx, "%c", line[i]) == -1)
- return;
- }
- else {
- buffer[j++] = line[i];
- if (j == sizeof (buffer) - 1) {
- if (smtp_message_printf(tx, "%s", buffer) == -1)
- return;
- skip = 1;
- j = 0;
- memset(buffer, 0, sizeof buffer);
- }
- }
- }
- }
- if (skip) {
- if (smtp_message_printf(tx, "\n") == -1)
- return;
- }
- else {
- buffer[j++] = '\n';
- if (j == sizeof (buffer) - 1) {
- if (smtp_message_printf(tx, "%s", buffer) == -1)
- return;
- skip = 1;
- j = 0;
- memset(buffer, 0, sizeof buffer);
- }
- }
- }
-
- /* end of header, if buffer is not empty we'll process it */
- if (buffer[0]) {
- if (j + strlen(tx->session->listener->hostname) + 1 < sizeof buffer) {
- header_append_domain_buffer(buffer, tx->session->listener->hostname, sizeof buffer);
- if (tx->session->flags & SF_AUTHENTICATED &&
- tx->session->listener->sendertable[0] &&
- tx->session->listener->flags & F_MASQUERADE &&
- !(strcasecmp(hdr, "From")))
- header_address_rewrite_buffer(buffer, mailaddr_to_text(&tx->evp.sender),
- sizeof buffer);
- }
- smtp_message_printf(tx, "%s", buffer);
- }
-}
-
-static void
-smtp_session_init(void)
-{
- static int init = 0;
-
- if (!init) {
- tree_init(&wait_lka_helo);
- tree_init(&wait_lka_mail);
- tree_init(&wait_lka_rcpt);
- tree_init(&wait_parent_auth);
- tree_init(&wait_queue_msg);
- tree_init(&wait_queue_fd);
- tree_init(&wait_queue_commit);
- tree_init(&wait_ssl_init);
- tree_init(&wait_ssl_verify);
- tree_init(&wait_filters);
- tree_init(&wait_filter_fd);
- init = 1;
- }
-}
-
-int
-smtp_session(struct listener *listener, int sock,
- const struct sockaddr_storage *ss, const char *hostname, struct io *io)
-{
- struct smtp_session *s;
-
- smtp_session_init();
-
- if ((s = calloc(1, sizeof(*s))) == NULL)
- return (-1);
-
- s->id = generate_uid();
- s->listener = listener;
- memmove(&s->ss, ss, sizeof(*ss));
-
- if (io != NULL)
- s->io = io;
- else
- s->io = io_new();
-
- io_set_callback(s->io, smtp_io, s);
- io_set_fd(s->io, sock);
- io_set_timeout(s->io, SMTPD_SESSION_TIMEOUT * 1000);
- io_set_write(s->io);
- s->state = STATE_NEW;
-
- (void)strlcpy(s->smtpname, listener->hostname, sizeof(s->smtpname));
-
- log_trace(TRACE_SMTP, "smtp: %p: connected to listener %p "
- "[hostname=%s, port=%d, tag=%s]", s, listener,
- listener->hostname, ntohs(listener->port), listener->tag);
-
- /* For local enqueueing, the hostname is already set */
- if (hostname) {
- s->flags |= SF_AUTHENTICATED;
- /* A bit of a hack */
- if (!strcmp(hostname, "localhost"))
- s->flags |= SF_BOUNCE;
- (void)strlcpy(s->rdns, hostname, sizeof(s->rdns));
- s->fcrdns = 1;
- smtp_lookup_servername(s);
- } else {
- resolver_getnameinfo((struct sockaddr *)&s->ss, NI_NAMEREQD,
- smtp_getnameinfo_cb, s);
- }
-
- /* session may have been freed by now */
-
- return (0);
-}
-
-static void
-smtp_getnameinfo_cb(void *arg, int gaierrno, const char *host, const char *serv)
-{
- struct smtp_session *s = arg;
- struct addrinfo hints;
-
- if (gaierrno) {
- (void)strlcpy(s->rdns, "<unknown>", sizeof(s->rdns));
-
- if (gaierrno == EAI_NODATA || gaierrno == EAI_NONAME)
- s->fcrdns = 0;
- else {
- log_warnx("getnameinfo: %s: %s", ss_to_text(&s->ss),
- gai_strerror(gaierrno));
- s->fcrdns = -1;
- }
-
- smtp_lookup_servername(s);
- return;
- }
-
- (void)strlcpy(s->rdns, host, sizeof(s->rdns));
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = s->ss.ss_family;
- hints.ai_socktype = SOCK_STREAM;
- resolver_getaddrinfo(s->rdns, NULL, &hints, smtp_getaddrinfo_cb, s);
-}
-
-static void
-smtp_getaddrinfo_cb(void *arg, int gaierrno, struct addrinfo *ai0)
-{
- struct smtp_session *s = arg;
- struct addrinfo *ai;
- char fwd[64], rev[64];
-
- if (gaierrno) {
- if (gaierrno == EAI_NODATA || gaierrno == EAI_NONAME)
- s->fcrdns = 0;
- else {
- log_warnx("getaddrinfo: %s: %s", s->rdns,
- gai_strerror(gaierrno));
- s->fcrdns = -1;
- }
- }
- else {
- strlcpy(rev, ss_to_text(&s->ss), sizeof(rev));
- for (ai = ai0; ai; ai = ai->ai_next) {
- strlcpy(fwd, sa_to_text(ai->ai_addr), sizeof(fwd));
- if (!strcmp(fwd, rev)) {
- s->fcrdns = 1;
- break;
- }
- }
- freeaddrinfo(ai0);
- }
-
- smtp_lookup_servername(s);
-}
-
-void
-smtp_session_imsg(struct mproc *p, struct imsg *imsg)
-{
- struct smtp_session *s;
- struct smtp_rcpt *rcpt;
- char user[SMTPD_MAXMAILADDRSIZE];
- char tmp[SMTP_LINE_MAX];
- struct msg m;
- const char *line, *helo;
- uint64_t reqid, evpid;
- uint32_t msgid;
- int status, success;
- int filter_response;
- const char *filter_param;
- uint8_t i;
-
- switch (imsg->hdr.type) {
-
- case IMSG_SMTP_CHECK_SENDER:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &status);
- m_end(&m);
- s = tree_xpop(&wait_lka_mail, reqid);
- switch (status) {
- case LKA_OK:
- smtp_tx_create_message(s->tx);
- break;
-
- case LKA_PERMFAIL:
- smtp_tx_free(s->tx);
- smtp_reply(s, "%d %s", 530, "Sender rejected");
- break;
- case LKA_TEMPFAIL:
- smtp_tx_free(s->tx);
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- break;
- }
- return;
-
- case IMSG_SMTP_EXPAND_RCPT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &status);
- m_get_string(&m, &line);
- m_end(&m);
- s = tree_xpop(&wait_lka_rcpt, reqid);
-
- tmp[0] = '\0';
- if (s->tx->evp.rcpt.user[0]) {
- (void)strlcpy(tmp, s->tx->evp.rcpt.user, sizeof tmp);
- if (s->tx->evp.rcpt.domain[0]) {
- (void)strlcat(tmp, "@", sizeof tmp);
- (void)strlcat(tmp, s->tx->evp.rcpt.domain,
- sizeof tmp);
- }
- }
-
- switch (status) {
- case LKA_OK:
- fatalx("unexpected ok");
- case LKA_PERMFAIL:
- smtp_reply(s, "%s: <%s>", line, tmp);
- break;
- case LKA_TEMPFAIL:
- smtp_reply(s, "%s: <%s>", line, tmp);
- break;
- }
- return;
-
- case IMSG_SMTP_LOOKUP_HELO:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- s = tree_xpop(&wait_lka_helo, reqid);
- m_get_int(&m, &status);
- if (status == LKA_OK) {
- m_get_string(&m, &helo);
- (void)strlcpy(s->smtpname, helo, sizeof(s->smtpname));
- }
- m_end(&m);
- smtp_connected(s);
- return;
-
- case IMSG_SMTP_MESSAGE_CREATE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- s = tree_xpop(&wait_queue_msg, reqid);
- if (success) {
- m_get_msgid(&m, &msgid);
- s->tx->msgid = msgid;
- s->tx->evp.id = msgid_to_evpid(msgid);
- s->tx->rcptcount = 0;
- smtp_reply(s, "250 %s Ok",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
- } else {
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_tx_free(s->tx);
- smtp_enter_state(s, STATE_QUIT);
- }
- m_end(&m);
- return;
-
- case IMSG_SMTP_MESSAGE_OPEN:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- m_end(&m);
-
- s = tree_xpop(&wait_queue_fd, reqid);
- if (!success || imsg->fd == -1) {
- if (imsg->fd != -1)
- close(imsg->fd);
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- return;
- }
-
- log_debug("smtp: %p: fd %d from queue", s, imsg->fd);
-
- if (smtp_message_fd(s->tx, imsg->fd)) {
- if (!SESSION_DATA_FILTERED(s))
- smtp_message_begin(s->tx);
- else
- smtp_filter_data_begin(s);
- }
- return;
-
- case IMSG_FILTER_SMTP_DATA_BEGIN:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- m_end(&m);
-
- s = tree_xpop(&wait_filter_fd, reqid);
- if (!success || imsg->fd == -1) {
- if (imsg->fd != -1)
- close(imsg->fd);
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- return;
- }
-
- log_debug("smtp: %p: fd %d from lka", s, imsg->fd);
-
- smtp_filter_fd(s->tx, imsg->fd);
- smtp_message_begin(s->tx);
- return;
-
- case IMSG_QUEUE_ENVELOPE_SUBMIT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- s = tree_xget(&wait_lka_rcpt, reqid);
- if (success) {
- m_get_evpid(&m, &evpid);
- s->tx->evp.id = evpid;
- s->tx->destcount++;
- smtp_report_tx_envelope(s, s->tx->msgid, evpid);
- }
- else
- s->tx->error = TX_ERROR_ENVELOPE;
- m_end(&m);
- return;
-
- case IMSG_QUEUE_ENVELOPE_COMMIT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- m_end(&m);
- if (!success)
- fatalx("commit evp failed: not supposed to happen");
- s = tree_xpop(&wait_lka_rcpt, reqid);
- if (s->tx->error) {
- /*
- * If an envelope failed, we can't cancel the last
- * RCPT only so we must cancel the whole transaction
- * and close the connection.
- */
- smtp_reply(s, "421 %s Temporary failure",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- }
- else {
- rcpt = xcalloc(1, sizeof(*rcpt));
- rcpt->evpid = s->tx->evp.id;
- rcpt->destcount = s->tx->destcount;
- rcpt->maddr = s->tx->evp.rcpt;
- TAILQ_INSERT_TAIL(&s->tx->rcpts, rcpt, entry);
-
- s->tx->destcount = 0;
- s->tx->rcptcount++;
- smtp_reply(s, "250 %s %s: Recipient ok",
- esc_code(ESC_STATUS_OK, ESC_DESTINATION_ADDRESS_VALID),
- esc_description(ESC_DESTINATION_ADDRESS_VALID));
- }
- return;
-
- case IMSG_SMTP_MESSAGE_COMMIT:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- m_end(&m);
- s = tree_xpop(&wait_queue_commit, reqid);
- if (!success) {
- smtp_reply(s, "421 %s Temporary failure",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_tx_free(s->tx);
- smtp_enter_state(s, STATE_QUIT);
- return;
- }
-
- smtp_reply(s, "250 %s %08x Message accepted for delivery",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS),
- s->tx->msgid);
- smtp_report_tx_commit(s, s->tx->msgid, s->tx->odatalen);
- smtp_report_tx_reset(s, s->tx->msgid);
-
- log_info("%016"PRIx64" smtp message "
- "msgid=%08x size=%zu nrcpt=%zu proto=%s",
- s->id,
- s->tx->msgid,
- s->tx->odatalen,
- s->tx->rcptcount,
- s->flags & SF_EHLO ? "ESMTP" : "SMTP");
- TAILQ_FOREACH(rcpt, &s->tx->rcpts, entry) {
- log_info("%016"PRIx64" smtp envelope "
- "evpid=%016"PRIx64" from=<%s%s%s> to=<%s%s%s>",
- s->id,
- rcpt->evpid,
- s->tx->evp.sender.user,
- s->tx->evp.sender.user[0] == '\0' ? "" : "@",
- s->tx->evp.sender.domain,
- rcpt->maddr.user,
- rcpt->maddr.user[0] == '\0' ? "" : "@",
- rcpt->maddr.domain);
- }
- smtp_tx_free(s->tx);
- s->mailcount++;
- smtp_enter_state(s, STATE_HELO);
- return;
-
- case IMSG_SMTP_AUTHENTICATE:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &success);
- m_end(&m);
-
- s = tree_xpop(&wait_parent_auth, reqid);
- strnvis(user, s->username, sizeof user, VIS_WHITE | VIS_SAFE);
- if (success == LKA_OK) {
- log_info("%016"PRIx64" smtp "
- "authentication user=%s "
- "result=ok",
- s->id, user);
- s->flags |= SF_AUTHENTICATED;
- smtp_report_link_auth(s, user, "pass");
- smtp_reply(s, "235 %s Authentication succeeded",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
- }
- else if (success == LKA_PERMFAIL) {
- log_info("%016"PRIx64" smtp "
- "authentication user=%s "
- "result=permfail",
- s->id, user);
- smtp_report_link_auth(s, user, "fail");
- smtp_auth_failure_pause(s);
- return;
- }
- else if (success == LKA_TEMPFAIL) {
- log_info("%016"PRIx64" smtp "
- "authentication user=%s "
- "result=tempfail",
- s->id, user);
- smtp_report_link_auth(s, user, "error");
- smtp_reply(s, "421 %s Temporary failure",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- }
- else
- fatalx("bad lka response");
-
- smtp_enter_state(s, STATE_HELO);
- return;
-
- case IMSG_FILTER_SMTP_PROTOCOL:
- m_msg(&m, imsg);
- m_get_id(&m, &reqid);
- m_get_int(&m, &filter_response);
- if (filter_response != FILTER_PROCEED &&
- filter_response != FILTER_JUNK)
- m_get_string(&m, &filter_param);
- else
- filter_param = NULL;
- m_end(&m);
-
- s = tree_xpop(&wait_filters, reqid);
-
- switch (filter_response) {
- case FILTER_REJECT:
- case FILTER_DISCONNECT:
- if (!valid_smtp_response(filter_param) ||
- (filter_param[0] != '4' && filter_param[0] != '5'))
- filter_param = "421 Internal server error";
- if (!strncmp(filter_param, "421", 3))
- filter_response = FILTER_DISCONNECT;
-
- smtp_report_filter_response(s, s->filter_phase,
- filter_response, filter_param);
-
- smtp_reply(s, "%s", filter_param);
-
- if (filter_response == FILTER_DISCONNECT)
- smtp_enter_state(s, STATE_QUIT);
- else if (s->filter_phase == FILTER_COMMIT)
- smtp_proceed_rollback(s, NULL);
- break;
-
-
- case FILTER_JUNK:
- if (s->tx)
- s->tx->junk = 1;
- else
- s->junk = 1;
- /* fallthrough */
-
- case FILTER_PROCEED:
- filter_param = s->filter_param;
- /* fallthrough */
-
- case FILTER_REWRITE:
- smtp_report_filter_response(s, s->filter_phase,
- filter_response,
- filter_param == s->filter_param ? NULL : filter_param);
- if (s->filter_phase == FILTER_CONNECT) {
- smtp_proceed_connected(s);
- return;
- }
- for (i = 0; i < nitems(commands); ++i)
- if (commands[i].filter_phase == s->filter_phase) {
- if (filter_response == FILTER_REWRITE)
- if (!commands[i].check(s, filter_param))
- break;
- commands[i].proceed(s, filter_param);
- break;
- }
- break;
- }
- return;
- }
-
- log_warnx("smtp_session_imsg: unexpected %s imsg",
- imsg_to_str(imsg->hdr.type));
- fatalx(NULL);
-}
-
-static void
-smtp_tls_verified(struct smtp_session *s)
-{
- X509 *x;
-
- x = SSL_get_peer_certificate(io_tls(s->io));
- if (x) {
- log_info("%016"PRIx64" smtp "
- "client-cert-check result=\"%s\"",
- s->id,
- (s->flags & SF_VERIFIED) ? "success" : "failure");
- X509_free(x);
- }
-
- if (s->listener->flags & F_SMTPS) {
- stat_increment("smtp.smtps", 1);
- io_set_write(s->io);
- smtp_send_banner(s);
- }
- else {
- stat_increment("smtp.tls", 1);
- smtp_enter_state(s, STATE_HELO);
- }
-}
-
-static void
-smtp_io(struct io *io, int evt, void *arg)
-{
- struct smtp_session *s = arg;
- char *line;
- size_t len;
- int eom;
-
- log_trace(TRACE_IO, "smtp: %p: %s %s", s, io_strevent(evt),
- io_strio(io));
-
- switch (evt) {
-
- case IO_TLSREADY:
- log_info("%016"PRIx64" smtp tls ciphers=%s",
- s->id, ssl_to_text(io_tls(s->io)));
-
- smtp_report_link_tls(s, ssl_to_text(io_tls(s->io)));
-
- s->flags |= SF_SECURE;
- s->helo[0] = '\0';
-
- smtp_cert_verify(s);
- break;
-
- case IO_DATAIN:
- nextline:
- line = io_getline(s->io, &len);
- if ((line == NULL && io_datalen(s->io) >= SMTP_LINE_MAX) ||
- (line && len >= SMTP_LINE_MAX)) {
- s->flags |= SF_BADINPUT;
- smtp_reply(s, "500 %s Line too long",
- esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- io_set_write(io);
- return;
- }
-
- /* No complete line received */
- if (line == NULL)
- return;
-
- /* Strip trailing '\r' */
- if (len && line[len - 1] == '\r')
- line[--len] = '\0';
-
- /* Message body */
- eom = 0;
- if (s->state == STATE_BODY) {
- if (strcmp(line, ".")) {
- s->tx->datain += strlen(line) + 1;
- if (s->tx->datain > env->sc_maxsize)
- s->tx->error = TX_ERROR_SIZE;
- }
- eom = (s->tx->filter == NULL) ?
- smtp_tx_dataline(s->tx, line) :
- smtp_tx_filtered_dataline(s->tx, line);
- if (eom == 0)
- goto nextline;
- }
-
- /* Pipelining not supported */
- if (io_datalen(s->io)) {
- s->flags |= SF_BADINPUT;
- smtp_reply(s, "500 %s %s: Pipelining not supported",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- smtp_enter_state(s, STATE_QUIT);
- io_set_write(io);
- return;
- }
-
- if (eom) {
- io_set_write(io);
- if (s->tx->filter == NULL)
- smtp_tx_eom(s->tx);
- return;
- }
-
- /* Must be a command */
- if (strlcpy(s->cmd, line, sizeof(s->cmd)) >= sizeof(s->cmd)) {
- s->flags |= SF_BADINPUT;
- smtp_reply(s, "500 %s Command line too long",
- esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- io_set_write(io);
- return;
- }
- io_set_write(io);
- smtp_command(s, line);
- break;
-
- case IO_LOWAT:
- if (s->state == STATE_QUIT) {
- log_info("%016"PRIx64" smtp disconnected "
- "reason=quit",
- s->id);
- smtp_free(s, "done");
- break;
- }
-
- /* Wait for the client to start tls */
- if (s->state == STATE_TLS) {
- smtp_cert_init(s);
- break;
- }
-
- io_set_read(io);
- break;
-
- case IO_TIMEOUT:
- log_info("%016"PRIx64" smtp disconnected "
- "reason=timeout",
- s->id);
- smtp_report_timeout(s);
- smtp_free(s, "timeout");
- break;
-
- case IO_DISCONNECTED:
- log_info("%016"PRIx64" smtp disconnected "
- "reason=disconnect",
- s->id);
- smtp_free(s, "disconnected");
- break;
-
- case IO_ERROR:
- log_info("%016"PRIx64" smtp disconnected "
- "reason=\"io-error: %s\"",
- s->id, io_error(io));
- smtp_free(s, "IO error");
- break;
-
- default:
- fatalx("smtp_io()");
- }
-}
-
-static void
-smtp_command(struct smtp_session *s, char *line)
-{
- char *args;
- int cmd, i;
-
- log_trace(TRACE_SMTP, "smtp: %p: <<< %s", s, line);
-
- /*
- * These states are special.
- */
- if (s->state == STATE_AUTH_INIT) {
- smtp_report_protocol_client(s, "********");
- smtp_rfc4954_auth_plain(s, line);
- return;
- }
- if (s->state == STATE_AUTH_USERNAME || s->state == STATE_AUTH_PASSWORD) {
- smtp_report_protocol_client(s, "********");
- smtp_rfc4954_auth_login(s, line);
- return;
- }
-
- if (s->state == STATE_HELO && strncasecmp(line, "AUTH PLAIN ", 11) == 0)
- smtp_report_protocol_client(s, "AUTH PLAIN ********");
- else
- smtp_report_protocol_client(s, line);
-
-
- /*
- * Unlike other commands, "mail from" and "rcpt to" contain a
- * space in the command name.
- */
- if (strncasecmp("mail from:", line, 10) == 0 ||
- strncasecmp("rcpt to:", line, 8) == 0)
- args = strchr(line, ':');
- else
- args = strchr(line, ' ');
-
- if (args) {
- *args++ = '\0';
- while (isspace((unsigned char)*args))
- args++;
- }
-
- cmd = -1;
- for (i = 0; commands[i].code != -1; i++)
- if (!strcasecmp(line, commands[i].cmd)) {
- cmd = commands[i].code;
- break;
- }
-
- s->last_cmd = cmd;
- switch (cmd) {
- /*
- * INIT
- */
- case CMD_HELO:
- if (!smtp_check_helo(s, args))
- break;
- smtp_filter_phase(FILTER_HELO, s, args);
- break;
-
- case CMD_EHLO:
- if (!smtp_check_ehlo(s, args))
- break;
- smtp_filter_phase(FILTER_EHLO, s, args);
- break;
-
- /*
- * SETUP
- */
- case CMD_STARTTLS:
- if (!smtp_check_starttls(s, args))
- break;
-
- smtp_filter_phase(FILTER_STARTTLS, s, NULL);
- break;
-
- case CMD_AUTH:
- if (!smtp_check_auth(s, args))
- break;
- smtp_filter_phase(FILTER_AUTH, s, args);
- break;
-
- case CMD_MAIL_FROM:
- if (!smtp_check_mail_from(s, args))
- break;
- smtp_filter_phase(FILTER_MAIL_FROM, s, args);
- break;
-
- /*
- * TRANSACTION
- */
- case CMD_RCPT_TO:
- if (!smtp_check_rcpt_to(s, args))
- break;
- smtp_filter_phase(FILTER_RCPT_TO, s, args);
- break;
-
- case CMD_RSET:
- if (!smtp_check_rset(s, args))
- break;
- smtp_filter_phase(FILTER_RSET, s, NULL);
- break;
-
- case CMD_DATA:
- if (!smtp_check_data(s, args))
- break;
- smtp_filter_phase(FILTER_DATA, s, NULL);
- break;
-
- /*
- * ANY
- */
- case CMD_QUIT:
- if (!smtp_check_noparam(s, args))
- break;
- smtp_filter_phase(FILTER_QUIT, s, NULL);
- break;
-
- case CMD_NOOP:
- if (!smtp_check_noparam(s, args))
- break;
- smtp_filter_phase(FILTER_NOOP, s, NULL);
- break;
-
- case CMD_HELP:
- if (!smtp_check_noparam(s, args))
- break;
- smtp_proceed_help(s, NULL);
- break;
-
- case CMD_WIZ:
- if (!smtp_check_noparam(s, args))
- break;
- smtp_proceed_wiz(s, NULL);
- break;
-
- default:
- smtp_reply(s, "500 %s %s: Command unrecognized",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- break;
- }
-}
-
-static int
-smtp_check_rset(struct smtp_session *s, const char *args)
-{
- if (!smtp_check_noparam(s, args))
- return 0;
-
- if (s->helo[0] == '\0') {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
- return 1;
-}
-
-static int
-smtp_check_helo(struct smtp_session *s, const char *args)
-{
- if (!s->banner_sent) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->helo[0]) {
- smtp_reply(s, "503 %s %s: Already identified",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (args == NULL) {
- smtp_reply(s, "501 %s %s: HELO requires domain name",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (!valid_domainpart(args)) {
- smtp_reply(s, "501 %s %s: Invalid domain name",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_ehlo(struct smtp_session *s, const char *args)
-{
- if (!s->banner_sent) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->helo[0]) {
- smtp_reply(s, "503 %s %s: Already identified",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (args == NULL) {
- smtp_reply(s, "501 %s %s: EHLO requires domain name",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (!valid_domainpart(args)) {
- smtp_reply(s, "501 %s %s: Invalid domain name",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_auth(struct smtp_session *s, const char *args)
-{
- if (s->helo[0] == '\0' || s->tx) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->flags & SF_AUTHENTICATED) {
- smtp_reply(s, "503 %s %s: Already authenticated",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (!ADVERTISE_AUTH(s)) {
- smtp_reply(s, "503 %s %s: Command not supported",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (args == NULL) {
- smtp_reply(s, "501 %s %s: No parameters given",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_starttls(struct smtp_session *s, const char *args)
-{
- if (s->helo[0] == '\0' || s->tx) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (!(s->listener->flags & F_STARTTLS)) {
- smtp_reply(s, "503 %s %s: Command not supported",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->flags & SF_SECURE) {
- smtp_reply(s, "503 %s %s: Channel already secured",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (args != NULL) {
- smtp_reply(s, "501 %s %s: No parameters allowed",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_mail_from(struct smtp_session *s, const char *args)
-{
- char *copy;
- char tmp[SMTP_LINE_MAX];
- struct mailaddr sender;
-
- (void)strlcpy(tmp, args, sizeof tmp);
- copy = tmp;
-
- if (s->helo[0] == '\0' || s->tx) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->listener->flags & F_STARTTLS_REQUIRE &&
- !(s->flags & SF_SECURE)) {
- smtp_reply(s,
- "530 %s %s: Must issue a STARTTLS command first",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->listener->flags & F_AUTH_REQUIRE &&
- !(s->flags & SF_AUTHENTICATED)) {
- smtp_reply(s,
- "530 %s %s: Must issue an AUTH command first",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->mailcount >= env->sc_session_max_mails) {
- /* we can pretend we had too many recipients */
- smtp_reply(s, "452 %s %s: Too many messages sent",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
- esc_description(ESC_TOO_MANY_RECIPIENTS));
- return 0;
- }
-
- if (smtp_mailaddr(&sender, copy, 1, &copy,
- s->smtpname) == 0) {
- smtp_reply(s, "553 %s Sender address syntax error",
- esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_rcpt_to(struct smtp_session *s, const char *args)
-{
- char *copy;
- char tmp[SMTP_LINE_MAX];
-
- (void)strlcpy(tmp, args, sizeof tmp);
- copy = tmp;
-
- if (s->tx == NULL) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->tx->rcptcount >= env->sc_session_max_rcpt) {
- smtp_reply(s->tx->session, "451 %s %s: Too many recipients",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
- esc_description(ESC_TOO_MANY_RECIPIENTS));
- return 0;
- }
-
- if (smtp_mailaddr(&s->tx->evp.rcpt, copy, 0, &copy,
- s->tx->session->smtpname) == 0) {
- smtp_reply(s->tx->session,
- "501 %s Recipient address syntax error",
- esc_code(ESC_STATUS_PERMFAIL,
- ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_data(struct smtp_session *s, const char *args)
-{
- if (!smtp_check_noparam(s, args))
- return 0;
-
- if (s->tx == NULL) {
- smtp_reply(s, "503 %s %s: Command not allowed at this point.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
- return 0;
- }
-
- if (s->tx->rcptcount == 0) {
- smtp_reply(s, "503 %s %s: No recipient specified",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
-
- return 1;
-}
-
-static int
-smtp_check_noparam(struct smtp_session *s, const char *args)
-{
- if (args != NULL) {
- smtp_reply(s, "500 %s %s: command does not accept arguments.",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS));
- return 0;
- }
- return 1;
-}
-
-static void
-smtp_query_filters(enum filter_phase phase, struct smtp_session *s, const char *args)
-{
- m_create(p_lka, IMSG_FILTER_SMTP_PROTOCOL, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_int(p_lka, phase);
- m_add_string(p_lka, args);
- m_close(p_lka);
- tree_xset(&wait_filters, s->id, s);
-}
-
-static void
-smtp_filter_begin(struct smtp_session *s)
-{
- if (!SESSION_FILTERED(s))
- return;
-
- m_create(p_lka, IMSG_FILTER_SMTP_BEGIN, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_string(p_lka, s->listener->filter_name);
- m_close(p_lka);
-}
-
-static void
-smtp_filter_end(struct smtp_session *s)
-{
- if (!SESSION_FILTERED(s))
- return;
-
- m_create(p_lka, IMSG_FILTER_SMTP_END, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_close(p_lka);
-}
-
-static void
-smtp_filter_data_begin(struct smtp_session *s)
-{
- if (!SESSION_FILTERED(s))
- return;
-
- m_create(p_lka, IMSG_FILTER_SMTP_DATA_BEGIN, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_close(p_lka);
- tree_xset(&wait_filter_fd, s->id, s);
-}
-
-static void
-smtp_filter_data_end(struct smtp_session *s)
-{
- if (!SESSION_FILTERED(s))
- return;
-
- if (s->tx->filter == NULL)
- return;
-
- io_free(s->tx->filter);
- s->tx->filter = NULL;
-
- m_create(p_lka, IMSG_FILTER_SMTP_DATA_END, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_close(p_lka);
-}
-
-static void
-smtp_filter_phase(enum filter_phase phase, struct smtp_session *s, const char *param)
-{
- uint8_t i;
-
- s->filter_phase = phase;
- s->filter_param = param;
-
- if (SESSION_FILTERED(s)) {
- smtp_query_filters(phase, s, param ? param : "");
- return;
- }
-
- if (s->filter_phase == FILTER_CONNECT) {
- smtp_proceed_connected(s);
- return;
- }
-
- for (i = 0; i < nitems(commands); ++i)
- if (commands[i].filter_phase == s->filter_phase) {
- commands[i].proceed(s, param);
- break;
- }
-}
-
-static void
-smtp_proceed_rset(struct smtp_session *s, const char *args)
-{
- smtp_reply(s, "250 %s Reset state",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
-
- if (s->tx) {
- if (s->tx->msgid)
- smtp_tx_rollback(s->tx);
- smtp_tx_free(s->tx);
- }
-}
-
-static void
-smtp_proceed_helo(struct smtp_session *s, const char *args)
-{
- (void)strlcpy(s->helo, args, sizeof(s->helo));
- s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED;
-
- smtp_report_link_identify(s, "HELO", s->helo);
-
- smtp_enter_state(s, STATE_HELO);
-
- smtp_reply(s, "250 %s Hello %s %s%s%s, pleased to meet you",
- s->smtpname,
- s->helo,
- s->ss.ss_family == AF_INET6 ? "" : "[",
- ss_to_text(&s->ss),
- s->ss.ss_family == AF_INET6 ? "" : "]");
-}
-
-static void
-smtp_proceed_ehlo(struct smtp_session *s, const char *args)
-{
- (void)strlcpy(s->helo, args, sizeof(s->helo));
- s->flags &= SF_SECURE | SF_AUTHENTICATED | SF_VERIFIED;
- s->flags |= SF_EHLO;
- s->flags |= SF_8BITMIME;
-
- smtp_report_link_identify(s, "EHLO", s->helo);
-
- smtp_enter_state(s, STATE_HELO);
- smtp_reply(s, "250-%s Hello %s %s%s%s, pleased to meet you",
- s->smtpname,
- s->helo,
- s->ss.ss_family == AF_INET6 ? "" : "[",
- ss_to_text(&s->ss),
- s->ss.ss_family == AF_INET6 ? "" : "]");
-
- smtp_reply(s, "250-8BITMIME");
- smtp_reply(s, "250-ENHANCEDSTATUSCODES");
- smtp_reply(s, "250-SIZE %zu", env->sc_maxsize);
- if (ADVERTISE_EXT_DSN(s))
- smtp_reply(s, "250-DSN");
- if (ADVERTISE_TLS(s))
- smtp_reply(s, "250-STARTTLS");
- if (ADVERTISE_AUTH(s))
- smtp_reply(s, "250-AUTH PLAIN LOGIN");
- smtp_reply(s, "250 HELP");
-}
-
-static void
-smtp_proceed_auth(struct smtp_session *s, const char *args)
-{
- char tmp[SMTP_LINE_MAX];
- char *eom, *method;
-
- (void)strlcpy(tmp, args, sizeof tmp);
-
- method = tmp;
- eom = strchr(tmp, ' ');
- if (eom == NULL)
- eom = strchr(tmp, '\t');
- if (eom != NULL)
- *eom++ = '\0';
- if (strcasecmp(method, "PLAIN") == 0)
- smtp_rfc4954_auth_plain(s, eom);
- else if (strcasecmp(method, "LOGIN") == 0)
- smtp_rfc4954_auth_login(s, eom);
- else
- smtp_reply(s, "504 %s %s: AUTH method \"%s\" not supported",
- esc_code(ESC_STATUS_PERMFAIL, ESC_SECURITY_FEATURES_NOT_SUPPORTED),
- esc_description(ESC_SECURITY_FEATURES_NOT_SUPPORTED),
- method);
-}
-
-static void
-smtp_proceed_starttls(struct smtp_session *s, const char *args)
-{
- smtp_reply(s, "220 %s Ready to start TLS",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
- smtp_enter_state(s, STATE_TLS);
-}
-
-static void
-smtp_proceed_mail_from(struct smtp_session *s, const char *args)
-{
- char *copy;
- char tmp[SMTP_LINE_MAX];
-
- (void)strlcpy(tmp, args, sizeof tmp);
- copy = tmp;
-
- if (!smtp_tx(s)) {
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- return;
- }
-
- if (smtp_mailaddr(&s->tx->evp.sender, copy, 1, &copy,
- s->smtpname) == 0) {
- smtp_reply(s, "553 %s Sender address syntax error",
- esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS));
- smtp_tx_free(s->tx);
- return;
- }
-
- smtp_tx_mail_from(s->tx, args);
-}
-
-static void
-smtp_proceed_rcpt_to(struct smtp_session *s, const char *args)
-{
- smtp_tx_rcpt_to(s->tx, args);
-}
-
-static void
-smtp_proceed_data(struct smtp_session *s, const char *args)
-{
- smtp_tx_open_message(s->tx);
-}
-
-static void
-smtp_proceed_quit(struct smtp_session *s, const char *args)
-{
- smtp_reply(s, "221 %s Bye",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
- smtp_enter_state(s, STATE_QUIT);
-}
-
-static void
-smtp_proceed_noop(struct smtp_session *s, const char *args)
-{
- smtp_reply(s, "250 %s Ok",
- esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
-}
-
-static void
-smtp_proceed_help(struct smtp_session *s, const char *args)
-{
- const char *code = esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS);
-
- smtp_reply(s, "214-%s This is " SMTPD_NAME, code);
- smtp_reply(s, "214-%s To report bugs in the implementation, "
- "please contact bugs@openbsd.org", code);
- smtp_reply(s, "214-%s with full details", code);
- smtp_reply(s, "214 %s End of HELP info", code);
-}
-
-static void
-smtp_proceed_wiz(struct smtp_session *s, const char *args)
-{
- smtp_reply(s, "500 %s %s: this feature is not supported yet ;-)",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND),
- esc_description(ESC_INVALID_COMMAND));
-}
-
-static void
-smtp_proceed_commit(struct smtp_session *s, const char *args)
-{
- smtp_message_end(s->tx);
-}
-
-static void
-smtp_proceed_rollback(struct smtp_session *s, const char *args)
-{
- struct smtp_tx *tx;
-
- tx = s->tx;
-
- fclose(tx->ofile);
- tx->ofile = NULL;
-
- smtp_tx_rollback(tx);
- smtp_tx_free(tx);
- smtp_enter_state(s, STATE_HELO);
-}
-
-static void
-smtp_rfc4954_auth_plain(struct smtp_session *s, char *arg)
-{
- char buf[1024], *user, *pass;
- int len;
-
- switch (s->state) {
- case STATE_HELO:
- if (arg == NULL) {
- smtp_enter_state(s, STATE_AUTH_INIT);
- smtp_reply(s, "334 ");
- return;
- }
- smtp_enter_state(s, STATE_AUTH_INIT);
- /* FALLTHROUGH */
-
- case STATE_AUTH_INIT:
- /* String is not NUL terminated, leave room. */
- if ((len = base64_decode(arg, (unsigned char *)buf,
- sizeof(buf) - 1)) == -1)
- goto abort;
- /* buf is a byte string, NUL terminate. */
- buf[len] = '\0';
-
- /*
- * Skip "foo" in "foo\0user\0pass", if present.
- */
- user = memchr(buf, '\0', len);
- if (user == NULL || user >= buf + len - 2)
- goto abort;
- user++; /* skip NUL */
- if (strlcpy(s->username, user, sizeof(s->username))
- >= sizeof(s->username))
- goto abort;
-
- pass = memchr(user, '\0', len - (user - buf));
- if (pass == NULL || pass >= buf + len - 2)
- goto abort;
- pass++; /* skip NUL */
-
- m_create(p_lka, IMSG_SMTP_AUTHENTICATE, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_string(p_lka, s->listener->authtable);
- m_add_string(p_lka, user);
- m_add_string(p_lka, pass);
- m_close(p_lka);
- tree_xset(&wait_parent_auth, s->id, s);
- return;
-
- default:
- fatal("smtp_rfc4954_auth_plain: unknown state");
- }
-
-abort:
- smtp_reply(s, "501 %s %s: Syntax error",
- esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR),
- esc_description(ESC_SYNTAX_ERROR));
- smtp_enter_state(s, STATE_HELO);
-}
-
-static void
-smtp_rfc4954_auth_login(struct smtp_session *s, char *arg)
-{
- char buf[LINE_MAX];
-
- switch (s->state) {
- case STATE_HELO:
- smtp_enter_state(s, STATE_AUTH_USERNAME);
- if (arg != NULL && *arg != '\0') {
- smtp_rfc4954_auth_login(s, arg);
- return;
- }
- smtp_reply(s, "334 VXNlcm5hbWU6");
- return;
-
- case STATE_AUTH_USERNAME:
- memset(s->username, 0, sizeof(s->username));
- if (base64_decode(arg, (unsigned char *)s->username,
- sizeof(s->username) - 1) == -1)
- goto abort;
-
- smtp_enter_state(s, STATE_AUTH_PASSWORD);
- smtp_reply(s, "334 UGFzc3dvcmQ6");
- return;
-
- case STATE_AUTH_PASSWORD:
- memset(buf, 0, sizeof(buf));
- if (base64_decode(arg, (unsigned char *)buf,
- sizeof(buf)-1) == -1)
- goto abort;
-
- m_create(p_lka, IMSG_SMTP_AUTHENTICATE, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_string(p_lka, s->listener->authtable);
- m_add_string(p_lka, s->username);
- m_add_string(p_lka, buf);
- m_close(p_lka);
- tree_xset(&wait_parent_auth, s->id, s);
- return;
-
- default:
- fatal("smtp_rfc4954_auth_login: unknown state");
- }
-
-abort:
- smtp_reply(s, "501 %s %s: Syntax error",
- esc_code(ESC_STATUS_PERMFAIL, ESC_SYNTAX_ERROR),
- esc_description(ESC_SYNTAX_ERROR));
- smtp_enter_state(s, STATE_HELO);
-}
-
-static void
-smtp_lookup_servername(struct smtp_session *s)
-{
- if (s->listener->hostnametable[0]) {
- m_create(p_lka, IMSG_SMTP_LOOKUP_HELO, 0, 0, -1);
- m_add_id(p_lka, s->id);
- m_add_string(p_lka, s->listener->hostnametable);
- m_add_sockaddr(p_lka, (struct sockaddr*)&s->listener->ss);
- m_close(p_lka);
- tree_xset(&wait_lka_helo, s->id, s);
- return;
- }
-
- smtp_connected(s);
-}
-
-static void
-smtp_connected(struct smtp_session *s)
-{
- smtp_enter_state(s, STATE_CONNECTED);
-
- log_info("%016"PRIx64" smtp connected address=%s host=%s",
- s->id, ss_to_text(&s->ss), s->rdns);
-
- smtp_filter_begin(s);
-
- smtp_report_link_connect(s, s->rdns, s->fcrdns, &s->ss,
- &s->listener->ss);
-
- smtp_filter_phase(FILTER_CONNECT, s, ss_to_text(&s->ss));
-}
-
-static void
-smtp_proceed_connected(struct smtp_session *s)
-{
- if (s->listener->flags & F_SMTPS)
- smtp_cert_init(s);
- else
- smtp_send_banner(s);
-}
-
-static void
-smtp_send_banner(struct smtp_session *s)
-{
- smtp_reply(s, "220 %s ESMTP %s", s->smtpname, SMTPD_NAME);
- s->banner_sent = 1;
- smtp_report_link_greeting(s, s->smtpname);
-}
-
-void
-smtp_enter_state(struct smtp_session *s, int newstate)
-{
- log_trace(TRACE_SMTP, "smtp: %p: %s -> %s", s,
- smtp_strstate(s->state),
- smtp_strstate(newstate));
-
- s->state = newstate;
-}
-
-static void
-smtp_reply(struct smtp_session *s, char *fmt, ...)
-{
- va_list ap;
- int n;
- char buf[LINE_MAX*2], tmp[LINE_MAX*2];
-
- va_start(ap, fmt);
- n = vsnprintf(buf, sizeof buf, fmt, ap);
- va_end(ap);
- if (n < 0)
- fatalx("smtp_reply: response format error");
- if (n < 4)
- fatalx("smtp_reply: response too short");
- if (n >= (int)sizeof buf) {
- /* only first three bytes are used by SMTP logic,
- * so if _our_ reply does not fit entirely in the
- * buffer, it's ok to truncate.
- */
- }
-
- log_trace(TRACE_SMTP, "smtp: %p: >>> %s", s, buf);
- smtp_report_protocol_server(s, buf);
-
- switch (buf[0]) {
- case '2':
- if (s->tx) {
- if (s->last_cmd == CMD_MAIL_FROM) {
- smtp_report_tx_begin(s, s->tx->msgid);
- smtp_report_tx_mail(s, s->tx->msgid, s->cmd + 10, 1);
- }
- else if (s->last_cmd == CMD_RCPT_TO)
- smtp_report_tx_rcpt(s, s->tx->msgid, s->cmd + 8, 1);
- }
- break;
- case '3':
- if (s->tx) {
- if (s->last_cmd == CMD_DATA)
- smtp_report_tx_data(s, s->tx->msgid, 1);
- }
- break;
- case '5':
- case '4':
- /* do not report smtp_tx_mail/smtp_tx_rcpt errors
- * if they happened outside of a transaction.
- */
- if (s->tx) {
- if (s->last_cmd == CMD_MAIL_FROM)
- smtp_report_tx_mail(s, s->tx->msgid,
- s->cmd + 10, buf[0] == '4' ? -1 : 0);
- else if (s->last_cmd == CMD_RCPT_TO)
- smtp_report_tx_rcpt(s,
- s->tx->msgid, s->cmd + 8, buf[0] == '4' ? -1 : 0);
- else if (s->last_cmd == CMD_DATA && s->tx->rcptcount)
- smtp_report_tx_data(s, s->tx->msgid,
- buf[0] == '4' ? -1 : 0);
- }
-
- if (s->flags & SF_BADINPUT) {
- log_info("%016"PRIx64" smtp "
- "bad-input result=\"%.*s\"",
- s->id, n, buf);
- }
- else if (s->state == STATE_AUTH_INIT) {
- log_info("%016"PRIx64" smtp "
- "failed-command "
- "command=\"AUTH PLAIN (...)\" result=\"%.*s\"",
- s->id, n, buf);
- }
- else if (s->state == STATE_AUTH_USERNAME) {
- log_info("%016"PRIx64" smtp "
- "failed-command "
- "command=\"AUTH LOGIN (username)\" result=\"%.*s\"",
- s->id, n, buf);
- }
- else if (s->state == STATE_AUTH_PASSWORD) {
- log_info("%016"PRIx64" smtp "
- "failed-command "
- "command=\"AUTH LOGIN (password)\" result=\"%.*s\"",
- s->id, n, buf);
- }
- else {
- strnvis(tmp, s->cmd, sizeof tmp, VIS_SAFE | VIS_CSTYLE);
- log_info("%016"PRIx64" smtp "
- "failed-command command=\"%s\" "
- "result=\"%.*s\"",
- s->id, tmp, n, buf);
- }
- break;
- }
-
- io_xprintf(s->io, "%s\r\n", buf);
-}
-
-static void
-smtp_free(struct smtp_session *s, const char * reason)
-{
- if (s->tx) {
- if (s->tx->msgid)
- smtp_tx_rollback(s->tx);
- smtp_tx_free(s->tx);
- }
-
- smtp_report_link_disconnect(s);
- smtp_filter_end(s);
-
- if (s->flags & SF_SECURE && s->listener->flags & F_SMTPS)
- stat_decrement("smtp.smtps", 1);
- if (s->flags & SF_SECURE && s->listener->flags & F_STARTTLS)
- stat_decrement("smtp.tls", 1);
-
- io_free(s->io);
- free(s);
-
- smtp_collect();
-}
-
-static int
-smtp_mailaddr(struct mailaddr *maddr, char *line, int mailfrom, char **args,
- const char *domain)
-{
- char *p, *e;
-
- if (line == NULL)
- return (0);
-
- if (*line != '<')
- return (0);
-
- e = strchr(line, '>');
- if (e == NULL)
- return (0);
- *e++ = '\0';
- while (*e == ' ')
- e++;
- *args = e;
-
- if (!text_to_mailaddr(maddr, line + 1))
- return (0);
-
- p = strchr(maddr->user, ':');
- if (p != NULL) {
- p++;
- memmove(maddr->user, p, strlen(p) + 1);
- }
-
- /* accept empty return-path in MAIL FROM, required for bounces */
- if (mailfrom && maddr->user[0] == '\0' && maddr->domain[0] == '\0')
- return (1);
-
- /* no or invalid user-part, reject */
- if (maddr->user[0] == '\0' || !valid_localpart(maddr->user))
- return (0);
-
- /* no domain part, local user */
- if (maddr->domain[0] == '\0') {
- (void)strlcpy(maddr->domain, domain,
- sizeof(maddr->domain));
- }
-
- if (!valid_domainpart(maddr->domain))
- return (0);
-
- return (1);
-}
-
-static void
-smtp_cert_init(struct smtp_session *s)
-{
- const char *name;
- int fallback;
-
- if (s->listener->pki_name[0]) {
- name = s->listener->pki_name;
- fallback = 0;
- }
- else {
- name = s->smtpname;
- fallback = 1;
- }
-
- if (cert_init(name, fallback, smtp_cert_init_cb, s))
- tree_xset(&wait_ssl_init, s->id, s);
-}
-
-static void
-smtp_cert_init_cb(void *arg, int status, const char *name, const void *cert,
- size_t cert_len)
-{
- struct smtp_session *s = arg;
- void *ssl, *ssl_ctx;
-
- tree_pop(&wait_ssl_init, s->id);
-
- if (status == CA_FAIL) {
- log_info("%016"PRIx64" smtp disconnected "
- "reason=ca-failure",
- s->id);
- smtp_free(s, "CA failure");
- return;
- }
-
- ssl_ctx = dict_get(env->sc_ssl_dict, name);
- ssl = ssl_smtp_init(ssl_ctx, s->listener->flags & F_TLS_VERIFY);
- io_set_read(s->io);
- io_start_tls(s->io, ssl);
-}
-
-static void
-smtp_cert_verify(struct smtp_session *s)
-{
- const char *name;
- int fallback;
-
- if (s->listener->ca_name[0]) {
- name = s->listener->ca_name;
- fallback = 0;
- }
- else {
- name = s->smtpname;
- fallback = 1;
- }
-
- if (cert_verify(io_tls(s->io), name, fallback, smtp_cert_verify_cb, s)) {
- tree_xset(&wait_ssl_verify, s->id, s);
- io_pause(s->io, IO_IN);
- }
-}
-
-static void
-smtp_cert_verify_cb(void *arg, int status)
-{
- struct smtp_session *s = arg;
- const char *reason = NULL;
- int resume;
-
- resume = tree_pop(&wait_ssl_verify, s->id) != NULL;
-
- switch (status) {
- case CERT_OK:
- reason = "cert-ok";
- s->flags |= SF_VERIFIED;
- break;
- case CERT_NOCA:
- reason = "no-ca";
- break;
- case CERT_NOCERT:
- reason = "no-client-cert";
- break;
- case CERT_INVALID:
- reason = "cert-invalid";
- break;
- default:
- reason = "cert-check-failed";
- break;
- }
-
- log_debug("smtp: %p: smtp_cert_verify_cb: %s", s, reason);
-
- if (!(s->flags & SF_VERIFIED) && (s->listener->flags & F_TLS_VERIFY)) {
- log_info("%016"PRIx64" smtp disconnected "
- " reason=%s", s->id,
- reason);
- smtp_free(s, "SSL certificate check failed");
- return;
- }
-
- smtp_tls_verified(s);
- if (resume)
- io_resume(s->io, IO_IN);
-}
-
-static void
-smtp_auth_failure_resume(int fd, short event, void *p)
-{
- struct smtp_session *s = p;
-
- smtp_reply(s, "535 Authentication failed");
- smtp_enter_state(s, STATE_HELO);
-}
-
-static void
-smtp_auth_failure_pause(struct smtp_session *s)
-{
- struct timeval tv;
-
- tv.tv_sec = 0;
- tv.tv_usec = arc4random_uniform(1000000);
- log_trace(TRACE_SMTP, "smtp: timing-attack protection triggered, "
- "will defer answer for %lu microseconds", tv.tv_usec);
- evtimer_set(&s->pause, smtp_auth_failure_resume, s);
- evtimer_add(&s->pause, &tv);
-}
-
-static int
-smtp_tx(struct smtp_session *s)
-{
- struct smtp_tx *tx;
-
- tx = calloc(1, sizeof(*tx));
- if (tx == NULL)
- return 0;
-
- TAILQ_INIT(&tx->rcpts);
-
- s->tx = tx;
- tx->session = s;
-
- /* setup the envelope */
- tx->evp.ss = s->ss;
- (void)strlcpy(tx->evp.tag, s->listener->tag, sizeof(tx->evp.tag));
- (void)strlcpy(tx->evp.smtpname, s->smtpname, sizeof(tx->evp.smtpname));
- (void)strlcpy(tx->evp.hostname, s->rdns, sizeof tx->evp.hostname);
- (void)strlcpy(tx->evp.helo, s->helo, sizeof(tx->evp.helo));
- (void)strlcpy(tx->evp.username, s->username, sizeof(tx->evp.username));
-
- if (s->flags & SF_BOUNCE)
- tx->evp.flags |= EF_BOUNCE;
- if (s->flags & SF_AUTHENTICATED)
- tx->evp.flags |= EF_AUTHENTICATED;
-
- if ((tx->parser = rfc5322_parser_new()) == NULL) {
- free(tx);
- return 0;
- }
-
- return 1;
-}
-
-static void
-smtp_tx_free(struct smtp_tx *tx)
-{
- struct smtp_rcpt *rcpt;
-
- rfc5322_free(tx->parser);
-
- while ((rcpt = TAILQ_FIRST(&tx->rcpts))) {
- TAILQ_REMOVE(&tx->rcpts, rcpt, entry);
- free(rcpt);
- }
-
- if (tx->ofile)
- fclose(tx->ofile);
-
- tx->session->tx = NULL;
-
- free(tx);
-}
-
-static void
-smtp_tx_mail_from(struct smtp_tx *tx, const char *line)
-{
- char *opt;
- char *copy;
- char tmp[SMTP_LINE_MAX];
-
- (void)strlcpy(tmp, line, sizeof tmp);
- copy = tmp;
-
- if (smtp_mailaddr(&tx->evp.sender, copy, 1, &copy,
- tx->session->smtpname) == 0) {
- smtp_reply(tx->session, "553 %s Sender address syntax error",
- esc_code(ESC_STATUS_PERMFAIL, ESC_OTHER_ADDRESS_STATUS));
- smtp_tx_free(tx);
- return;
- }
-
- while ((opt = strsep(&copy, " "))) {
- if (*opt == '\0')
- continue;
-
- if (strncasecmp(opt, "AUTH=", 5) == 0)
- log_debug("debug: smtp: AUTH in MAIL FROM command");
- else if (strncasecmp(opt, "SIZE=", 5) == 0)
- log_debug("debug: smtp: SIZE in MAIL FROM command");
- else if (strcasecmp(opt, "BODY=7BIT") == 0)
- /* XXX only for this transaction */
- tx->session->flags &= ~SF_8BITMIME;
- else if (strcasecmp(opt, "BODY=8BITMIME") == 0)
- ;
- else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "RET=", 4) == 0) {
- opt += 4;
- if (strcasecmp(opt, "HDRS") == 0)
- tx->evp.dsn_ret = DSN_RETHDRS;
- else if (strcasecmp(opt, "FULL") == 0)
- tx->evp.dsn_ret = DSN_RETFULL;
- } else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "ENVID=", 6) == 0) {
- opt += 6;
- if (strlcpy(tx->evp.dsn_envid, opt, sizeof(tx->evp.dsn_envid))
- >= sizeof(tx->evp.dsn_envid)) {
- smtp_reply(tx->session,
- "503 %s %s: option too large, truncated: %s",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS), opt);
- smtp_tx_free(tx);
- return;
- }
- } else {
- smtp_reply(tx->session, "503 %s %s: Unsupported option %s",
- esc_code(ESC_STATUS_PERMFAIL, ESC_INVALID_COMMAND_ARGUMENTS),
- esc_description(ESC_INVALID_COMMAND_ARGUMENTS), opt);
- smtp_tx_free(tx);
- return;
- }
- }
-
- /* only check sendertable if defined and user has authenticated */
- if (tx->session->flags & SF_AUTHENTICATED &&
- tx->session->listener->sendertable[0]) {
- m_create(p_lka, IMSG_SMTP_CHECK_SENDER, 0, 0, -1);
- m_add_id(p_lka, tx->session->id);
- m_add_string(p_lka, tx->session->listener->sendertable);
- m_add_string(p_lka, tx->session->username);
- m_add_mailaddr(p_lka, &tx->evp.sender);
- m_close(p_lka);
- tree_xset(&wait_lka_mail, tx->session->id, tx->session);
- }
- else
- smtp_tx_create_message(tx);
-}
-
-static void
-smtp_tx_create_message(struct smtp_tx *tx)
-{
- m_create(p_queue, IMSG_SMTP_MESSAGE_CREATE, 0, 0, -1);
- m_add_id(p_queue, tx->session->id);
- m_close(p_queue);
- tree_xset(&wait_queue_msg, tx->session->id, tx->session);
-}
-
-static void
-smtp_tx_rcpt_to(struct smtp_tx *tx, const char *line)
-{
- char *opt, *p;
- char *copy;
- char tmp[SMTP_LINE_MAX];
-
- (void)strlcpy(tmp, line, sizeof tmp);
- copy = tmp;
-
- if (tx->rcptcount >= env->sc_session_max_rcpt) {
- smtp_reply(tx->session, "451 %s %s: Too many recipients",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_TOO_MANY_RECIPIENTS),
- esc_description(ESC_TOO_MANY_RECIPIENTS));
- return;
- }
-
- if (smtp_mailaddr(&tx->evp.rcpt, copy, 0, &copy,
- tx->session->smtpname) == 0) {
- smtp_reply(tx->session,
- "501 %s Recipient address syntax error",
- esc_code(ESC_STATUS_PERMFAIL,
- ESC_BAD_DESTINATION_MAILBOX_ADDRESS_SYNTAX));
- return;
- }
-
- while ((opt = strsep(&copy, " "))) {
- if (*opt == '\0')
- continue;
-
- if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "NOTIFY=", 7) == 0) {
- opt += 7;
- while ((p = strsep(&opt, ","))) {
- if (strcasecmp(p, "SUCCESS") == 0)
- tx->evp.dsn_notify |= DSN_SUCCESS;
- else if (strcasecmp(p, "FAILURE") == 0)
- tx->evp.dsn_notify |= DSN_FAILURE;
- else if (strcasecmp(p, "DELAY") == 0)
- tx->evp.dsn_notify |= DSN_DELAY;
- else if (strcasecmp(p, "NEVER") == 0)
- tx->evp.dsn_notify |= DSN_NEVER;
- }
-
- if (tx->evp.dsn_notify & DSN_NEVER &&
- tx->evp.dsn_notify & (DSN_SUCCESS | DSN_FAILURE |
- DSN_DELAY)) {
- smtp_reply(tx->session,
- "553 NOTIFY option NEVER cannot be"
- " combined with other options");
- return;
- }
- } else if (ADVERTISE_EXT_DSN(tx->session) && strncasecmp(opt, "ORCPT=", 6) == 0) {
- opt += 6;
-
- if (strncasecmp(opt, "rfc822;", 7) == 0)
- opt += 7;
-
- if (!text_to_mailaddr(&tx->evp.dsn_orcpt, opt) ||
- !valid_localpart(tx->evp.dsn_orcpt.user) ||
- !valid_domainpart(tx->evp.dsn_orcpt.domain)) {
- smtp_reply(tx->session,
- "553 ORCPT address syntax error");
- return;
- }
- } else {
- smtp_reply(tx->session, "503 Unsupported option %s", opt);
- return;
- }
- }
-
- m_create(p_lka, IMSG_SMTP_EXPAND_RCPT, 0, 0, -1);
- m_add_id(p_lka, tx->session->id);
- m_add_envelope(p_lka, &tx->evp);
- m_close(p_lka);
- tree_xset(&wait_lka_rcpt, tx->session->id, tx->session);
-}
-
-static void
-smtp_tx_open_message(struct smtp_tx *tx)
-{
- m_create(p_queue, IMSG_SMTP_MESSAGE_OPEN, 0, 0, -1);
- m_add_id(p_queue, tx->session->id);
- m_add_msgid(p_queue, tx->msgid);
- m_close(p_queue);
- tree_xset(&wait_queue_fd, tx->session->id, tx->session);
-}
-
-static void
-smtp_tx_commit(struct smtp_tx *tx)
-{
- m_create(p_queue, IMSG_SMTP_MESSAGE_COMMIT, 0, 0, -1);
- m_add_id(p_queue, tx->session->id);
- m_add_msgid(p_queue, tx->msgid);
- m_close(p_queue);
- tree_xset(&wait_queue_commit, tx->session->id, tx->session);
- smtp_filter_data_end(tx->session);
-}
-
-static void
-smtp_tx_rollback(struct smtp_tx *tx)
-{
- m_create(p_queue, IMSG_SMTP_MESSAGE_ROLLBACK, 0, 0, -1);
- m_add_msgid(p_queue, tx->msgid);
- m_close(p_queue);
- smtp_report_tx_rollback(tx->session, tx->msgid);
- smtp_report_tx_reset(tx->session, tx->msgid);
- smtp_filter_data_end(tx->session);
-}
-
-static int
-smtp_tx_dataline(struct smtp_tx *tx, const char *line)
-{
- struct rfc5322_result res;
- int r;
-
- log_trace(TRACE_SMTP, "<<< [MSG] %s", line);
-
- if (!strcmp(line, ".")) {
- smtp_report_protocol_client(tx->session, ".");
- log_trace(TRACE_SMTP, "<<< [EOM]");
- if (tx->error)
- return 1;
- line = NULL;
- }
- else {
- /* ignore data line if an error is set */
- if (tx->error)
- return 0;
-
- /* escape lines starting with a '.' */
- if (line[0] == '.')
- line += 1;
- }
-
- if (rfc5322_push(tx->parser, line) == -1) {
- log_warnx("failed to push dataline");
- tx->error = TX_ERROR_INTERNAL;
- return 0;
- }
-
- for(;;) {
- r = rfc5322_next(tx->parser, &res);
- switch (r) {
- case -1:
- if (errno == ENOMEM)
- tx->error = TX_ERROR_INTERNAL;
- else
- tx->error = TX_ERROR_MALFORMED;
- return 0;
-
- case RFC5322_NONE:
- /* Need more data */
- return 0;
-
- case RFC5322_HEADER_START:
- /* ignore bcc */
- if (!strcasecmp("Bcc", res.hdr))
- continue;
-
- if (!strcasecmp("To", res.hdr) ||
- !strcasecmp("Cc", res.hdr) ||
- !strcasecmp("From", res.hdr)) {
- rfc5322_unfold_header(tx->parser);
- continue;
- }
-
- if (!strcasecmp("Received", res.hdr)) {
- if (++tx->rcvcount >= MAX_HOPS_COUNT) {
- log_warnx("warn: loop detected");
- tx->error = TX_ERROR_LOOP;
- return 0;
- }
- }
- else if (!tx->has_date && !strcasecmp("Date", res.hdr))
- tx->has_date = 1;
- else if (!tx->has_message_id &&
- !strcasecmp("Message-Id", res.hdr))
- tx->has_message_id = 1;
-
- smtp_message_printf(tx, "%s:%s\n", res.hdr, res.value);
- break;
-
- case RFC5322_HEADER_CONT:
-
- if (!strcasecmp("Bcc", res.hdr) ||
- !strcasecmp("To", res.hdr) ||
- !strcasecmp("Cc", res.hdr) ||
- !strcasecmp("From", res.hdr))
- continue;
-
- smtp_message_printf(tx, "%s\n", res.value);
- break;
-
- case RFC5322_HEADER_END:
- if (!strcasecmp("To", res.hdr) ||
- !strcasecmp("Cc", res.hdr) ||
- !strcasecmp("From", res.hdr))
- header_domain_append_callback(tx, res.hdr,
- res.value);
- break;
-
- case RFC5322_END_OF_HEADERS:
- if (tx->session->listener->local ||
- tx->session->listener->port == 587) {
-
- if (!tx->has_date) {
- log_debug("debug: %p: adding Date", tx);
- smtp_message_printf(tx, "Date: %s\n",
- time_to_text(tx->time));
- }
-
- if (!tx->has_message_id) {
- log_debug("debug: %p: adding Message-ID", tx);
- smtp_message_printf(tx,
- "Message-ID: <%016"PRIx64"@%s>\n",
- generate_uid(),
- tx->session->listener->hostname);
- }
- }
- break;
-
- case RFC5322_BODY_START:
- case RFC5322_BODY:
- smtp_message_printf(tx, "%s\n", res.value);
- break;
-
- case RFC5322_END_OF_MESSAGE:
- return 1;
-
- default:
- fatalx("%s", __func__);
- }
- }
-}
-
-static int
-smtp_tx_filtered_dataline(struct smtp_tx *tx, const char *line)
-{
- if (!strcmp(line, "."))
- line = NULL;
- else {
- /* ignore data line if an error is set */
- if (tx->error)
- return 0;
- }
- io_printf(tx->filter, "%s\n", line ? line : ".");
- return line ? 0 : 1;
-}
-
-static void
-smtp_tx_eom(struct smtp_tx *tx)
-{
- smtp_filter_phase(FILTER_COMMIT, tx->session, NULL);
-}
-
-static int
-smtp_message_fd(struct smtp_tx *tx, int fd)
-{
- struct smtp_session *s;
-
- s = tx->session;
-
- log_debug("smtp: %p: message fd %d", s, fd);
-
- if ((tx->ofile = fdopen(fd, "w")) == NULL) {
- close(fd);
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- smtp_enter_state(s, STATE_QUIT);
- return 0;
- }
- return 1;
-}
-
-static void
-filter_session_io(struct io *io, int evt, void *arg)
-{
- struct smtp_tx*tx = arg;
- char*line = NULL;
- ssize_t len;
-
- log_trace(TRACE_IO, "filter session io (smtp): %p: %s %s", tx, io_strevent(evt),
- io_strio(io));
-
- switch (evt) {
- case IO_DATAIN:
- nextline:
- line = io_getline(tx->filter, &len);
- /* No complete line received */
- if (line == NULL)
- return;
-
- if (smtp_tx_dataline(tx, line)) {
- smtp_tx_eom(tx);
- return;
- }
-
- goto nextline;
- }
-}
-
-static void
-smtp_filter_fd(struct smtp_tx *tx, int fd)
-{
- struct smtp_session *s;
-
- s = tx->session;
-
- log_debug("smtp: %p: filter fd %d", s, fd);
-
- tx->filter = io_new();
- io_set_fd(tx->filter, fd);
- io_set_callback(tx->filter, filter_session_io, tx);
-}
-
-static void
-smtp_message_begin(struct smtp_tx *tx)
-{
- struct smtp_session *s;
- X509 *x;
- int (*m_printf)(struct smtp_tx *, const char *, ...);
-
- m_printf = smtp_message_printf;
- if (tx->filter)
- m_printf = smtp_filter_printf;
-
- s = tx->session;
-
- log_debug("smtp: %p: message begin", s);
-
- smtp_reply(s, "354 Enter mail, end with \".\""
- " on a line by itself");
-
- if (s->junk || (s->tx && s->tx->junk))
- m_printf(tx, "X-Spam: Yes\n");
-
- m_printf(tx, "Received: ");
- if (!(s->listener->flags & F_MASK_SOURCE)) {
- m_printf(tx, "from %s (%s %s%s%s)",
- s->helo,
- s->rdns,
- s->ss.ss_family == AF_INET6 ? "" : "[",
- ss_to_text(&s->ss),
- s->ss.ss_family == AF_INET6 ? "" : "]");
- }
- m_printf(tx, "\n\tby %s (%s) with %sSMTP%s%s id %08x",
- s->smtpname,
- SMTPD_NAME,
- s->flags & SF_EHLO ? "E" : "",
- s->flags & SF_SECURE ? "S" : "",
- s->flags & SF_AUTHENTICATED ? "A" : "",
- tx->msgid);
-
- if (s->flags & SF_SECURE) {
- x = SSL_get_peer_certificate(io_tls(s->io));
- m_printf(tx, " (%s:%s:%d:%s)",
- SSL_get_version(io_tls(s->io)),
- SSL_get_cipher_name(io_tls(s->io)),
- SSL_get_cipher_bits(io_tls(s->io), NULL),
- (s->flags & SF_VERIFIED) ? "YES" : (x ? "FAIL" : "NO"));
- X509_free(x);
-
- if (s->listener->flags & F_RECEIVEDAUTH) {
- m_printf(tx, " auth=%s",
- s->username[0] ? "yes" : "no");
- if (s->username[0])
- m_printf(tx, " user=%s", s->username);
- }
- }
-
- if (tx->rcptcount == 1) {
- m_printf(tx, "\n\tfor <%s@%s>",
- tx->evp.rcpt.user,
- tx->evp.rcpt.domain);
- }
-
- m_printf(tx, ";\n\t%s\n", time_to_text(time(&tx->time)));
-
- smtp_enter_state(s, STATE_BODY);
-}
-
-static void
-smtp_message_end(struct smtp_tx *tx)
-{
- struct smtp_session *s;
-
- s = tx->session;
-
- log_debug("debug: %p: end of message, error=%d", s, tx->error);
-
- fclose(tx->ofile);
- tx->ofile = NULL;
-
- switch(tx->error) {
- case TX_OK:
- smtp_tx_commit(tx);
- return;
-
- case TX_ERROR_SIZE:
- smtp_reply(s, "554 %s %s: Transaction failed, message too big",
- esc_code(ESC_STATUS_PERMFAIL, ESC_MESSAGE_TOO_BIG_FOR_SYSTEM),
- esc_description(ESC_MESSAGE_TOO_BIG_FOR_SYSTEM));
- break;
-
- case TX_ERROR_LOOP:
- smtp_reply(s, "500 %s %s: Loop detected",
- esc_code(ESC_STATUS_PERMFAIL, ESC_ROUTING_LOOP_DETECTED),
- esc_description(ESC_ROUTING_LOOP_DETECTED));
- break;
-
- case TX_ERROR_MALFORMED:
- smtp_reply(s, "550 %s %s: Message is not RFC 2822 compliant",
- esc_code(ESC_STATUS_PERMFAIL, ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED),
- esc_description(ESC_DELIVERY_NOT_AUTHORIZED_MESSAGE_REFUSED));
- break;
-
- case TX_ERROR_IO:
- case TX_ERROR_RESOURCES:
- smtp_reply(s, "421 %s Temporary Error",
- esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
- break;
-
- default:
- /* fatal? */
- smtp_reply(s, "421 Internal server error");
- }
-
- smtp_tx_rollback(tx);
- smtp_tx_free(tx);
- smtp_enter_state(s, STATE_HELO);
-}
-
-static int
-smtp_filter_printf(struct smtp_tx *tx, const char *fmt, ...)
-{
- va_list ap;
- int len;
-
- if (tx->error)
- return -1;
-
- va_start(ap, fmt);
- len = io_vprintf(tx->filter, fmt, ap);
- va_end(ap);
-
- if (len < 0) {
- log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id);
- tx->error = TX_ERROR_IO;
- }
- else
- tx->odatalen += len;
-
- return len;
-}
-
-static int
-smtp_message_printf(struct smtp_tx *tx, const char *fmt, ...)
-{
- va_list ap;
- int len;
-
- if (tx->error)
- return -1;
-
- va_start(ap, fmt);
- len = vfprintf(tx->ofile, fmt, ap);
- va_end(ap);
-
- if (len == -1) {
- log_warn("smtp-in: session %016"PRIx64": vfprintf", tx->session->id);
- tx->error = TX_ERROR_IO;
- }
- else
- tx->odatalen += len;
-
- return len;
-}
-
-#define CASE(x) case x : return #x
-
-const char *
-smtp_strstate(int state)
-{
- static char buf[32];
-
- switch (state) {
- CASE(STATE_NEW);
- CASE(STATE_CONNECTED);
- CASE(STATE_TLS);
- CASE(STATE_HELO);
- CASE(STATE_AUTH_INIT);
- CASE(STATE_AUTH_USERNAME);
- CASE(STATE_AUTH_PASSWORD);
- CASE(STATE_AUTH_FINALIZE);
- CASE(STATE_BODY);
- CASE(STATE_QUIT);
- default:
- (void)snprintf(buf, sizeof(buf), "STATE_??? (%d)", state);
- return (buf);
- }
-}
-
-
-static void
-smtp_report_link_connect(struct smtp_session *s, const char *rdns, int fcrdns,
- const struct sockaddr_storage *ss_src,
- const struct sockaddr_storage *ss_dest)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_connect("smtp-in", s->id, rdns, fcrdns, ss_src, ss_dest);
-}
-
-static void
-smtp_report_link_greeting(struct smtp_session *s,
- const char *domain)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_greeting("smtp-in", s->id, domain);
-}
-
-static void
-smtp_report_link_identify(struct smtp_session *s, const char *method, const char *identity)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_identify("smtp-in", s->id, method, identity);
-}
-
-static void
-smtp_report_link_tls(struct smtp_session *s, const char *ssl)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_tls("smtp-in", s->id, ssl);
-}
-
-static void
-smtp_report_link_disconnect(struct smtp_session *s)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_disconnect("smtp-in", s->id);
-}
-
-static void
-smtp_report_link_auth(struct smtp_session *s, const char *user, const char *result)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_link_auth("smtp-in", s->id, user, result);
-}
-
-static void
-smtp_report_tx_reset(struct smtp_session *s, uint32_t msgid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_reset("smtp-in", s->id, msgid);
-}
-
-static void
-smtp_report_tx_begin(struct smtp_session *s, uint32_t msgid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_begin("smtp-in", s->id, msgid);
-}
-
-static void
-smtp_report_tx_mail(struct smtp_session *s, uint32_t msgid, const char *address, int ok)
-{
- char mailaddr[SMTPD_MAXMAILADDRSIZE];
- char *p;
-
- if (! SESSION_FILTERED(s))
- return;
-
- if ((p = strchr(address, '<')) == NULL)
- return;
- (void)strlcpy(mailaddr, p + 1, sizeof mailaddr);
- if ((p = strchr(mailaddr, '>')) == NULL)
- return;
- *p = '\0';
-
- report_smtp_tx_mail("smtp-in", s->id, msgid, mailaddr, ok);
-}
-
-static void
-smtp_report_tx_rcpt(struct smtp_session *s, uint32_t msgid, const char *address, int ok)
-{
- char mailaddr[SMTPD_MAXMAILADDRSIZE];
- char *p;
-
- if (! SESSION_FILTERED(s))
- return;
-
- if ((p = strchr(address, '<')) == NULL)
- return;
- (void)strlcpy(mailaddr, p + 1, sizeof mailaddr);
- if ((p = strchr(mailaddr, '>')) == NULL)
- return;
- *p = '\0';
-
- report_smtp_tx_rcpt("smtp-in", s->id, msgid, mailaddr, ok);
-}
-
-static void
-smtp_report_tx_envelope(struct smtp_session *s, uint32_t msgid, uint64_t evpid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_envelope("smtp-in", s->id, msgid, evpid);
-}
-
-static void
-smtp_report_tx_data(struct smtp_session *s, uint32_t msgid, int ok)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_data("smtp-in", s->id, msgid, ok);
-}
-
-static void
-smtp_report_tx_commit(struct smtp_session *s, uint32_t msgid, size_t msgsz)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_commit("smtp-in", s->id, msgid, msgsz);
-}
-
-static void
-smtp_report_tx_rollback(struct smtp_session *s, uint32_t msgid)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_tx_rollback("smtp-in", s->id, msgid);
-}
-
-static void
-smtp_report_protocol_client(struct smtp_session *s, const char *command)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_protocol_client("smtp-in", s->id, command);
-}
-
-static void
-smtp_report_protocol_server(struct smtp_session *s, const char *response)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_protocol_server("smtp-in", s->id, response);
-}
-
-static void
-smtp_report_filter_response(struct smtp_session *s, int phase, int response, const char *param)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_filter_response("smtp-in", s->id, phase, response, param);
-}
-
-static void
-smtp_report_timeout(struct smtp_session *s)
-{
- if (! SESSION_FILTERED(s))
- return;
-
- report_smtp_timeout("smtp-in", s->id);
-}