diff options
Diffstat (limited to 'smtp_session.c')
-rw-r--r-- | smtp_session.c | 3213 |
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, ©, - 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, ©, - 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, ©, - 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, ©, - 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(©, " "))) { - 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, ©, - 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(©, " "))) { - 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); -} |