diff options
author | Gilles Chehade <gilles@poolp.org> | 2020-04-30 02:01:35 +0200 |
---|---|---|
committer | Gilles Chehade <gilles@poolp.org> | 2020-04-30 02:01:35 +0200 |
commit | 6fb3965e81d9e4278ee5d96c2d68c014df1f3802 (patch) | |
tree | c3c895a4a39d4888e0ff978c97587120712a6b9d /smtpd/mta_session.c | |
parent | plug ubuntu-gcc10 to CI (diff) | |
download | OpenSMTPD-6fb3965e81d9e4278ee5d96c2d68c014df1f3802.tar.xz OpenSMTPD-6fb3965e81d9e4278ee5d96c2d68c014df1f3802.zip |
move
Diffstat (limited to 'smtpd/mta_session.c')
-rw-r--r-- | smtpd/mta_session.c | 2004 |
1 files changed, 0 insertions, 2004 deletions
diff --git a/smtpd/mta_session.c b/smtpd/mta_session.c deleted file mode 100644 index 632f5be7..00000000 --- a/smtpd/mta_session.c +++ /dev/null @@ -1,2004 +0,0 @@ -/* $OpenBSD: mta_session.c,v 1.135 2020/04/24 11:34:07 eric Exp $ */ - -/* - * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> - * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> - * Copyright (c) 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 "includes.h" - -#include <sys/types.h> -#include <sys/queue.h> -#include <sys/tree.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/uio.h> - -#include <ctype.h> -#include <err.h> -#include <errno.h> -#include <event.h> -#include <imsg.h> -#include <inttypes.h> -#include <netdb.h> -#include <openssl/ssl.h> -#include <pwd.h> -#include <resolv.h> -#include <limits.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include "smtpd.h" -#include "log.h" -#include "ssl.h" - -#define MAX_TRYBEFOREDISABLE 10 - -#define MTA_HIWAT 65535 - -enum mta_state { - MTA_INIT, - MTA_BANNER, - MTA_EHLO, - MTA_HELO, - MTA_LHLO, - MTA_STARTTLS, - MTA_AUTH, - MTA_AUTH_PLAIN, - MTA_AUTH_LOGIN, - MTA_AUTH_LOGIN_USER, - MTA_AUTH_LOGIN_PASS, - MTA_READY, - MTA_MAIL, - MTA_RCPT, - MTA_DATA, - MTA_BODY, - MTA_EOM, - MTA_LMTP_EOM, - MTA_RSET, - MTA_QUIT, -}; - -#define MTA_FORCE_ANYSSL 0x0001 -#define MTA_FORCE_SMTPS 0x0002 -#define MTA_FORCE_TLS 0x0004 -#define MTA_FORCE_PLAIN 0x0008 -#define MTA_WANT_SECURE 0x0010 -#define MTA_DOWNGRADE_PLAIN 0x0080 - -#define MTA_TLS 0x0100 -#define MTA_TLS_VERIFIED 0x0200 - -#define MTA_FREE 0x0400 -#define MTA_LMTP 0x0800 -#define MTA_WAIT 0x1000 -#define MTA_HANGON 0x2000 -#define MTA_RECONN 0x4000 - -#define MTA_EXT_STARTTLS 0x01 -#define MTA_EXT_PIPELINING 0x02 -#define MTA_EXT_AUTH 0x04 -#define MTA_EXT_AUTH_PLAIN 0x08 -#define MTA_EXT_AUTH_LOGIN 0x10 -#define MTA_EXT_SIZE 0x20 - -struct mta_session { - uint64_t id; - struct mta_relay *relay; - struct mta_route *route; - char *helo; - char *mxname; - - char *username; - - int flags; - - int attempt; - int use_smtps; - int use_starttls; - int use_smtp_tls; - int ready; - - struct event ev; - struct io *io; - int ext; - - size_t ext_size; - - size_t msgtried; - size_t msgcount; - size_t rcptcount; - int hangon; - - enum mta_state state; - struct mta_task *task; - struct mta_envelope *currevp; - FILE *datafp; - size_t datalen; - - size_t failures; - - char replybuf[2048]; -}; - -static void mta_session_init(void); -static void mta_start(int fd, short ev, void *arg); -static void mta_io(struct io *, int, void *); -static void mta_free(struct mta_session *); -static void mta_getnameinfo_cb(void *, int, const char *, const char *); -static void mta_on_ptr(void *, void *, void *); -static void mta_on_timeout(struct runq *, void *); -static void mta_connect(struct mta_session *); -static void mta_enter_state(struct mta_session *, int); -static void mta_flush_task(struct mta_session *, int, const char *, size_t, int); -static void mta_error(struct mta_session *, const char *, ...); -static void mta_send(struct mta_session *, char *, ...); -static ssize_t mta_queue_data(struct mta_session *); -static void mta_response(struct mta_session *, char *); -static const char * mta_strstate(int); -static void mta_cert_init(struct mta_session *); -static void mta_cert_init_cb(void *, int, const char *, const void *, size_t); -static void mta_cert_verify(struct mta_session *); -static void mta_cert_verify_cb(void *, int); -static void mta_tls_verified(struct mta_session *); -static struct mta_session *mta_tree_pop(struct tree *, uint64_t); -static const char * dsn_strret(enum dsn_ret); -static const char * dsn_strnotify(uint8_t); - -void mta_hoststat_update(const char *, const char *); -void mta_hoststat_reschedule(const char *); -void mta_hoststat_cache(const char *, uint64_t); -void mta_hoststat_uncache(const char *, uint64_t); - - -static void mta_filter_begin(struct mta_session *); -static void mta_filter_end(struct mta_session *); -static void mta_connected(struct mta_session *); -static void mta_disconnected(struct mta_session *); - -static void mta_report_link_connect(struct mta_session *, const char *, int, - const struct sockaddr_storage *, - const struct sockaddr_storage *); -static void mta_report_link_greeting(struct mta_session *, const char *); -static void mta_report_link_identify(struct mta_session *, const char *, const char *); -static void mta_report_link_tls(struct mta_session *, const char *); -static void mta_report_link_disconnect(struct mta_session *); -static void mta_report_link_auth(struct mta_session *, const char *, const char *); -static void mta_report_tx_reset(struct mta_session *, uint32_t); -static void mta_report_tx_begin(struct mta_session *, uint32_t); -static void mta_report_tx_mail(struct mta_session *, uint32_t, const char *, int); -static void mta_report_tx_rcpt(struct mta_session *, uint32_t, const char *, int); -static void mta_report_tx_envelope(struct mta_session *, uint32_t, uint64_t); -static void mta_report_tx_data(struct mta_session *, uint32_t, int); -static void mta_report_tx_commit(struct mta_session *, uint32_t, size_t); -static void mta_report_tx_rollback(struct mta_session *, uint32_t); -static void mta_report_protocol_client(struct mta_session *, const char *); -static void mta_report_protocol_server(struct mta_session *, const char *); -#if 0 -static void mta_report_filter_response(struct mta_session *, int, int, const char *); -#endif -static void mta_report_timeout(struct mta_session *); - - -static struct tree wait_helo; -static struct tree wait_ptr; -static struct tree wait_fd; -static struct tree wait_tls_init; -static struct tree wait_tls_verify; - -static struct runq *hangon; - -#define SESSION_FILTERED(s) \ - ((s)->relay->dispatcher->u.remote.filtername) - -static void -mta_session_init(void) -{ - static int init = 0; - - if (!init) { - tree_init(&wait_helo); - tree_init(&wait_ptr); - tree_init(&wait_fd); - tree_init(&wait_tls_init); - tree_init(&wait_tls_verify); - runq_init(&hangon, mta_on_timeout); - init = 1; - } -} - -void -mta_session(struct mta_relay *relay, struct mta_route *route, const char *mxname) -{ - struct mta_session *s; - struct timeval tv; - - mta_session_init(); - - s = xcalloc(1, sizeof *s); - s->id = generate_uid(); - s->relay = relay; - s->route = route; - s->mxname = xstrdup(mxname); - - mta_filter_begin(s); - - if (relay->flags & RELAY_LMTP) - s->flags |= MTA_LMTP; - switch (relay->tls) { - case RELAY_TLS_SMTPS: - s->flags |= MTA_FORCE_SMTPS; - s->flags |= MTA_WANT_SECURE; - break; - case RELAY_TLS_STARTTLS: - s->flags |= MTA_FORCE_TLS; - s->flags |= MTA_WANT_SECURE; - break; - case RELAY_TLS_OPPORTUNISTIC: - /* do not force anything, try tls then smtp */ - break; - case RELAY_TLS_NO: - s->flags |= MTA_FORCE_PLAIN; - break; - default: - fatalx("bad value for relay->tls: %d", relay->tls); - } - - log_debug("debug: mta: %p: spawned for relay %s", s, - mta_relay_to_text(relay)); - stat_increment("mta.session", 1); - - if (route->dst->ptrname || route->dst->lastptrquery) { - /* We want to delay the connection since to always notify - * the relay asynchronously. - */ - tv.tv_sec = 0; - tv.tv_usec = 0; - evtimer_set(&s->ev, mta_start, s); - evtimer_add(&s->ev, &tv); - } else if (waitq_wait(&route->dst->ptrname, mta_on_ptr, s)) { - resolver_getnameinfo(s->route->dst->sa, 0, mta_getnameinfo_cb, s); - } -} - -void -mta_session_imsg(struct mproc *p, struct imsg *imsg) -{ - struct mta_session *s; - struct msg m; - uint64_t reqid; - const char *name; - int status; - struct stat sb; - - switch (imsg->hdr.type) { - - case IMSG_MTA_OPEN_MESSAGE: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_end(&m); - - s = mta_tree_pop(&wait_fd, reqid); - if (s == NULL) { - if (imsg->fd != -1) - close(imsg->fd); - return; - } - - if (imsg->fd == -1) { - log_debug("debug: mta: failed to obtain msg fd"); - mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, - "Could not get message fd", 0, 0); - mta_enter_state(s, MTA_READY); - return; - } - - if ((s->ext & MTA_EXT_SIZE) && s->ext_size != 0) { - if (fstat(imsg->fd, &sb) == -1) { - log_debug("debug: mta: failed to stat msg fd"); - mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, - "Could not stat message fd", 0, 0); - mta_enter_state(s, MTA_READY); - close(imsg->fd); - return; - } - if (sb.st_size > (off_t)s->ext_size) { - log_debug("debug: mta: message too large for peer"); - mta_flush_task(s, IMSG_MTA_DELIVERY_PERMFAIL, - "message too large for peer", 0, 0); - mta_enter_state(s, MTA_READY); - close(imsg->fd); - return; - } - } - - s->datafp = fdopen(imsg->fd, "r"); - if (s->datafp == NULL) - fatal("mta: fdopen"); - - mta_enter_state(s, MTA_MAIL); - return; - - case IMSG_MTA_LOOKUP_HELO: - m_msg(&m, imsg); - m_get_id(&m, &reqid); - m_get_int(&m, &status); - if (status == LKA_OK) - m_get_string(&m, &name); - m_end(&m); - - s = mta_tree_pop(&wait_helo, reqid); - if (s == NULL) - return; - - if (status == LKA_OK) { - s->helo = xstrdup(name); - mta_connect(s); - } else { - mta_source_error(s->relay, s->route, - "Failed to retrieve helo string"); - mta_free(s); - } - return; - - default: - errx(1, "mta_session_imsg: unexpected %s imsg", - imsg_to_str(imsg->hdr.type)); - } -} - -static struct mta_session * -mta_tree_pop(struct tree *wait, uint64_t reqid) -{ - struct mta_session *s; - - s = tree_xpop(wait, reqid); - if (s->flags & MTA_FREE) { - log_debug("debug: mta: %p: zombie session", s); - mta_free(s); - return (NULL); - } - s->flags &= ~MTA_WAIT; - - return (s); -} - -static void -mta_free(struct mta_session *s) -{ - struct mta_relay *relay; - struct mta_route *route; - - log_debug("debug: mta: %p: session done", s); - - mta_disconnected(s); - - if (s->ready) - s->relay->nconn_ready -= 1; - - if (s->flags & MTA_HANGON) { - log_debug("debug: mta: %p: cancelling hangon timer", s); - runq_cancel(hangon, s); - } - - if (s->io) - io_free(s->io); - - if (s->task) - fatalx("current task should have been deleted already"); - if (s->datafp) { - fclose(s->datafp); - s->datalen = 0; - } - free(s->helo); - - relay = s->relay; - route = s->route; - free(s->username); - free(s->mxname); - free(s); - stat_decrement("mta.session", 1); - mta_route_collect(relay, route); -} - -static void -mta_getnameinfo_cb(void *arg, int gaierrno, const char *host, const char *serv) -{ - struct mta_session *s = arg; - struct mta_host *h; - - h = s->route->dst; - h->lastptrquery = time(NULL); - if (host) - h->ptrname = xstrdup(host); - waitq_run(&h->ptrname, h->ptrname); -} - -static void -mta_on_timeout(struct runq *runq, void *arg) -{ - struct mta_session *s = arg; - - log_debug("mta: timeout for session hangon"); - - s->flags &= ~MTA_HANGON; - s->hangon++; - - mta_enter_state(s, MTA_READY); -} - -static void -mta_on_ptr(void *tag, void *arg, void *data) -{ - struct mta_session *s = arg; - - mta_connect(s); -} - -static void -mta_start(int fd, short ev, void *arg) -{ - struct mta_session *s = arg; - - mta_connect(s); -} - -static void -mta_connect(struct mta_session *s) -{ - struct sockaddr_storage ss; - struct sockaddr *sa; - int portno; - const char *schema; - - if (s->helo == NULL) { - if (s->relay->helotable && s->route->src->sa) { - m_create(p_lka, IMSG_MTA_LOOKUP_HELO, 0, 0, -1); - m_add_id(p_lka, s->id); - m_add_string(p_lka, s->relay->helotable); - m_add_sockaddr(p_lka, s->route->src->sa); - m_close(p_lka); - tree_xset(&wait_helo, s->id, s); - s->flags |= MTA_WAIT; - return; - } - else if (s->relay->heloname) - s->helo = xstrdup(s->relay->heloname); - else - s->helo = xstrdup(env->sc_hostname); - } - - if (s->io) { - io_free(s->io); - s->io = NULL; - } - - s->use_smtps = s->use_starttls = s->use_smtp_tls = 0; - - switch (s->attempt) { - case 0: - if (s->flags & MTA_FORCE_SMTPS) - s->use_smtps = 1; /* smtps */ - else if (s->flags & (MTA_FORCE_TLS|MTA_FORCE_ANYSSL)) - s->use_starttls = 1; /* tls, tls+smtps */ - else if (!(s->flags & MTA_FORCE_PLAIN)) - s->use_smtp_tls = 1; - break; - case 1: - if (s->flags & MTA_FORCE_ANYSSL) { - s->use_smtps = 1; /* tls+smtps */ - break; - } - else if (s->flags & MTA_DOWNGRADE_PLAIN) { - /* smtp, with tls failure */ - break; - } - default: - mta_free(s); - return; - } - portno = s->use_smtps ? 465 : 25; - - /* Override with relay-specified port */ - if (s->relay->port) - portno = s->relay->port; - - memmove(&ss, s->route->dst->sa, SA_LEN(s->route->dst->sa)); - sa = (struct sockaddr *)&ss; - - if (sa->sa_family == AF_INET) - ((struct sockaddr_in *)sa)->sin_port = htons(portno); - else if (sa->sa_family == AF_INET6) - ((struct sockaddr_in6 *)sa)->sin6_port = htons(portno); - - s->attempt += 1; - if (s->use_smtp_tls) - schema = "smtp://"; - else if (s->use_starttls) - schema = "smtp+tls://"; - else if (s->use_smtps) - schema = "smtps://"; - else if (s->flags & MTA_LMTP) - schema = "lmtp://"; - else - schema = "smtp+notls://"; - - log_info("%016"PRIx64" mta " - "connecting address=%s%s:%d host=%s", - s->id, schema, sa_to_text(s->route->dst->sa), - portno, s->route->dst->ptrname); - - mta_enter_state(s, MTA_INIT); - s->io = io_new(); - io_set_callback(s->io, mta_io, s); - io_set_timeout(s->io, 300000); - if (io_connect(s->io, sa, s->route->src->sa) == -1) { - /* - * This error is most likely a "no route", - * so there is no need to try again. - */ - log_debug("debug: mta: io_connect failed: %s", io_error(s->io)); - if (errno == EADDRNOTAVAIL) - mta_source_error(s->relay, s->route, io_error(s->io)); - else - mta_error(s, "Connection failed: %s", io_error(s->io)); - mta_free(s); - } -} - -static void -mta_enter_state(struct mta_session *s, int newstate) -{ - struct mta_envelope *e; - size_t envid_sz; - int oldstate; - ssize_t q; - char ibuf[LINE_MAX]; - char obuf[LINE_MAX]; - int offset; - const char *srs_sender; - -again: - oldstate = s->state; - - log_trace(TRACE_MTA, "mta: %p: %s -> %s", s, - mta_strstate(oldstate), - mta_strstate(newstate)); - - s->state = newstate; - - memset(s->replybuf, 0, sizeof s->replybuf); - - /* don't try this at home! */ -#define mta_enter_state(_s, _st) do { newstate = _st; goto again; } while (0) - - switch (s->state) { - case MTA_INIT: - case MTA_BANNER: - break; - - case MTA_EHLO: - s->ext = 0; - mta_send(s, "EHLO %s", s->helo); - mta_report_link_identify(s, "EHLO", s->helo); - break; - - case MTA_HELO: - s->ext = 0; - mta_send(s, "HELO %s", s->helo); - mta_report_link_identify(s, "HELO", s->helo); - break; - - case MTA_LHLO: - s->ext = 0; - mta_send(s, "LHLO %s", s->helo); - mta_report_link_identify(s, "LHLO", s->helo); - break; - - case MTA_STARTTLS: - if (s->flags & MTA_DOWNGRADE_PLAIN) - mta_enter_state(s, MTA_AUTH); - if (s->flags & MTA_TLS) /* already started */ - mta_enter_state(s, MTA_AUTH); - else if ((s->ext & MTA_EXT_STARTTLS) == 0) { - if (s->flags & MTA_FORCE_TLS || s->flags & MTA_WANT_SECURE) { - mta_error(s, "TLS required but not supported by remote host"); - s->flags |= MTA_RECONN; - } - else - /* server doesn't support starttls, do not use it */ - mta_enter_state(s, MTA_AUTH); - } - else - mta_send(s, "STARTTLS"); - break; - - case MTA_AUTH: - if (s->relay->secret && s->flags & MTA_TLS) { - if (s->ext & MTA_EXT_AUTH) { - if (s->ext & MTA_EXT_AUTH_PLAIN) { - mta_enter_state(s, MTA_AUTH_PLAIN); - break; - } - if (s->ext & MTA_EXT_AUTH_LOGIN) { - mta_enter_state(s, MTA_AUTH_LOGIN); - break; - } - log_debug("debug: mta: %p: no supported AUTH method on session", s); - mta_error(s, "no supported AUTH method"); - } - else { - log_debug("debug: mta: %p: AUTH not advertised on session", s); - mta_error(s, "AUTH not advertised"); - } - } - else if (s->relay->secret) { - log_debug("debug: mta: %p: not using AUTH on non-TLS " - "session", s); - mta_error(s, "Refuse to AUTH over unsecure channel"); - mta_connect(s); - } else { - mta_enter_state(s, MTA_READY); - } - break; - - case MTA_AUTH_PLAIN: - memset(ibuf, 0, sizeof ibuf); - if (base64_decode(s->relay->secret, (unsigned char *)ibuf, - sizeof(ibuf)-1) == -1) { - log_debug("debug: mta: %p: credentials too large on session", s); - mta_error(s, "Credentials too large"); - break; - } - s->username = xstrdup(ibuf+1); - mta_send(s, "AUTH PLAIN %s", s->relay->secret); - break; - - case MTA_AUTH_LOGIN: - mta_send(s, "AUTH LOGIN"); - break; - - case MTA_AUTH_LOGIN_USER: - memset(ibuf, 0, sizeof ibuf); - if (base64_decode(s->relay->secret, (unsigned char *)ibuf, - sizeof(ibuf)-1) == -1) { - log_debug("debug: mta: %p: credentials too large on session", s); - mta_error(s, "Credentials too large"); - break; - } - s->username = xstrdup(ibuf+1); - - memset(obuf, 0, sizeof obuf); - base64_encode((unsigned char *)ibuf + 1, strlen(ibuf + 1), obuf, sizeof obuf); - mta_send(s, "%s", obuf); - - memset(ibuf, 0, sizeof ibuf); - memset(obuf, 0, sizeof obuf); - break; - - case MTA_AUTH_LOGIN_PASS: - memset(ibuf, 0, sizeof ibuf); - if (base64_decode(s->relay->secret, (unsigned char *)ibuf, - sizeof(ibuf)-1) == -1) { - log_debug("debug: mta: %p: credentials too large on session", s); - mta_error(s, "Credentials too large"); - break; - } - - offset = strlen(ibuf+1)+2; - memset(obuf, 0, sizeof obuf); - base64_encode((unsigned char *)ibuf + offset, strlen(ibuf + offset), obuf, sizeof obuf); - mta_send(s, "%s", obuf); - - memset(ibuf, 0, sizeof ibuf); - memset(obuf, 0, sizeof obuf); - break; - - case MTA_READY: - /* Ready to send a new mail */ - if (s->ready == 0) { - s->ready = 1; - s->relay->nconn_ready += 1; - mta_route_ok(s->relay, s->route); - } - - if (s->msgtried >= MAX_TRYBEFOREDISABLE) { - log_info("%016"PRIx64" mta host-rejects-all-mails", - s->id); - mta_route_down(s->relay, s->route); - mta_enter_state(s, MTA_QUIT); - break; - } - - if (s->msgcount >= s->relay->limits->max_mail_per_session) { - log_debug("debug: mta: " - "%p: cannot send more message to relay %s", s, - mta_relay_to_text(s->relay)); - mta_enter_state(s, MTA_QUIT); - break; - } - - /* - * When downgrading from opportunistic TLS, clear flag and - * possibly reuse the same task (forbidden in other cases). - */ - if (s->flags & MTA_DOWNGRADE_PLAIN) - s->flags &= ~MTA_DOWNGRADE_PLAIN; - else if (s->task) - fatalx("task should be NULL at this point"); - - if (s->task == NULL) - s->task = mta_route_next_task(s->relay, s->route); - if (s->task == NULL) { - log_debug("debug: mta: %p: no task for relay %s", - s, mta_relay_to_text(s->relay)); - - if (s->relay->nconn > 1 || - s->hangon >= s->relay->limits->sessdelay_keepalive) { - mta_enter_state(s, MTA_QUIT); - break; - } - - log_debug("mta: debug: last connection: hanging on for %llds", - (long long)(s->relay->limits->sessdelay_keepalive - - s->hangon)); - s->flags |= MTA_HANGON; - runq_schedule(hangon, 1, s); - break; - } - - log_debug("debug: mta: %p: handling next task for relay %s", s, - mta_relay_to_text(s->relay)); - - stat_increment("mta.task.running", 1); - - m_create(p_queue, IMSG_MTA_OPEN_MESSAGE, 0, 0, -1); - m_add_id(p_queue, s->id); - m_add_msgid(p_queue, s->task->msgid); - m_close(p_queue); - - tree_xset(&wait_fd, s->id, s); - s->flags |= MTA_WAIT; - break; - - case MTA_MAIL: - s->currevp = TAILQ_FIRST(&s->task->envelopes); - - e = s->currevp; - s->hangon = 0; - s->msgtried++; - envid_sz = strlen(e->dsn_envid); - - /* SRS-encode if requested for the relay action, AND we're not - * bouncing, AND we have an RCPT which means we are forwarded, - * AND the RCPT has a '@' just for sanity check (will always). - */ - if (env->sc_srs_key != NULL && - s->relay->srs && - strchr(s->task->sender, '@') && - e->rcpt && - strchr(e->rcpt, '@')) { - /* encode and replace task sender with new SRS-sender */ - srs_sender = srs_encode(s->task->sender, - strchr(e->rcpt, '@') + 1); - if (srs_sender) { - free(s->task->sender); - s->task->sender = xstrdup(srs_sender); - } - } - - if (s->ext & MTA_EXT_DSN) { - mta_send(s, "MAIL FROM:<%s>%s%s%s%s", - s->task->sender, - e->dsn_ret ? " RET=" : "", - e->dsn_ret ? dsn_strret(e->dsn_ret) : "", - envid_sz ? " ENVID=" : "", - envid_sz ? e->dsn_envid : ""); - } else - mta_send(s, "MAIL FROM:<%s>", s->task->sender); - break; - - case MTA_RCPT: - if (s->currevp == NULL) - s->currevp = TAILQ_FIRST(&s->task->envelopes); - - e = s->currevp; - if (s->ext & MTA_EXT_DSN) { - mta_send(s, "RCPT TO:<%s>%s%s%s%s", - e->dest, - e->dsn_notify ? " NOTIFY=" : "", - e->dsn_notify ? dsn_strnotify(e->dsn_notify) : "", - e->dsn_orcpt ? " ORCPT=rfc822;" : "", - e->dsn_orcpt ? e->dsn_orcpt : ""); - } else - mta_send(s, "RCPT TO:<%s>", e->dest); - - mta_report_tx_envelope(s, s->task->msgid, e->id); - s->rcptcount++; - break; - - case MTA_DATA: - fseek(s->datafp, 0, SEEK_SET); - mta_send(s, "DATA"); - break; - - case MTA_BODY: - if (s->datafp == NULL) { - log_trace(TRACE_MTA, "mta: %p: end-of-file", s); - mta_enter_state(s, MTA_EOM); - break; - } - - if ((q = mta_queue_data(s)) == -1) { - s->flags |= MTA_FREE; - break; - } - if (q == 0) { - mta_enter_state(s, MTA_BODY); - break; - } - - log_trace(TRACE_MTA, "mta: %p: >>> [...%zd bytes...]", s, q); - break; - - case MTA_EOM: - mta_send(s, "."); - break; - - case MTA_LMTP_EOM: - /* LMTP reports status of each delivery, so enable read */ - io_set_read(s->io); - break; - - case MTA_RSET: - if (s->datafp) { - fclose(s->datafp); - s->datafp = NULL; - s->datalen = 0; - } - mta_send(s, "RSET"); - break; - - case MTA_QUIT: - mta_send(s, "QUIT"); - break; - - default: - fatalx("mta_enter_state: unknown state"); - } -#undef mta_enter_state -} - -/* - * Handle a response to an SMTP command - */ -static void -mta_response(struct mta_session *s, char *line) -{ - struct mta_envelope *e; - struct sockaddr_storage ss; - struct sockaddr *sa; - const char *domain; - char *pbuf; - socklen_t sa_len; - char buf[LINE_MAX]; - int delivery; - - switch (s->state) { - - case MTA_BANNER: - if (line[0] != '2') { - mta_error(s, "BANNER rejected: %s", line); - s->flags |= MTA_FREE; - return; - } - - pbuf = ""; - if (strlen(line) > 4) { - (void)strlcpy(buf, line + 4, sizeof buf); - if ((pbuf = strchr(buf, ' '))) - *pbuf = '\0'; - pbuf = valid_domainpart(buf) ? buf : ""; - } - mta_report_link_greeting(s, pbuf); - - if (s->flags & MTA_LMTP) - mta_enter_state(s, MTA_LHLO); - else - mta_enter_state(s, MTA_EHLO); - break; - - case MTA_EHLO: - if (line[0] != '2') { - /* rejected at ehlo state */ - if ((s->relay->flags & RELAY_AUTH) || - (s->flags & MTA_WANT_SECURE)) { - mta_error(s, "EHLO rejected: %s", line); - s->flags |= MTA_FREE; - return; - } - mta_enter_state(s, MTA_HELO); - return; - } - if (!(s->flags & MTA_FORCE_PLAIN)) - mta_enter_state(s, MTA_STARTTLS); - else - mta_enter_state(s, MTA_READY); - break; - - case MTA_HELO: - if (line[0] != '2') { - mta_error(s, "HELO rejected: %s", line); - s->flags |= MTA_FREE; - return; - } - mta_enter_state(s, MTA_READY); - break; - - case MTA_LHLO: - if (line[0] != '2') { - mta_error(s, "LHLO rejected: %s", line); - s->flags |= MTA_FREE; - return; - } - mta_enter_state(s, MTA_READY); - break; - - case MTA_STARTTLS: - if (line[0] != '2') { - if (!(s->flags & MTA_WANT_SECURE)) { - mta_enter_state(s, MTA_AUTH); - return; - } - /* XXX mark that the MX doesn't support STARTTLS */ - mta_error(s, "STARTTLS rejected: %s", line); - s->flags |= MTA_FREE; - return; - } - - mta_cert_init(s); - break; - - case MTA_AUTH_PLAIN: - if (line[0] != '2') { - mta_error(s, "AUTH rejected: %s", line); - mta_report_link_auth(s, s->username, "fail"); - s->flags |= MTA_FREE; - return; - } - mta_report_link_auth(s, s->username, "pass"); - mta_enter_state(s, MTA_READY); - break; - - case MTA_AUTH_LOGIN: - if (strncmp(line, "334 ", 4) != 0) { - mta_error(s, "AUTH rejected: %s", line); - mta_report_link_auth(s, s->username, "fail"); - s->flags |= MTA_FREE; - return; - } - mta_enter_state(s, MTA_AUTH_LOGIN_USER); - break; - - case MTA_AUTH_LOGIN_USER: - if (strncmp(line, "334 ", 4) != 0) { - mta_error(s, "AUTH rejected: %s", line); - mta_report_link_auth(s, s->username, "fail"); - s->flags |= MTA_FREE; - return; - } - mta_enter_state(s, MTA_AUTH_LOGIN_PASS); - break; - - case MTA_AUTH_LOGIN_PASS: - if (line[0] != '2') { - mta_error(s, "AUTH rejected: %s", line); - mta_report_link_auth(s, s->username, "fail"); - s->flags |= MTA_FREE; - return; - } - mta_report_link_auth(s, s->username, "pass"); - mta_enter_state(s, MTA_READY); - break; - - case MTA_MAIL: - if (line[0] != '2') { - if (line[0] == '5') - delivery = IMSG_MTA_DELIVERY_PERMFAIL; - else - delivery = IMSG_MTA_DELIVERY_TEMPFAIL; - - mta_flush_task(s, delivery, line, 0, 0); - mta_enter_state(s, MTA_RSET); - return; - } - mta_report_tx_begin(s, s->task->msgid); - mta_report_tx_mail(s, s->task->msgid, s->task->sender, 1); - mta_enter_state(s, MTA_RCPT); - break; - - case MTA_RCPT: - e = s->currevp; - - /* remove envelope from hosttat cache if there */ - if ((domain = strchr(e->dest, '@')) != NULL) { - domain++; - mta_hoststat_uncache(domain, e->id); - } - - s->currevp = TAILQ_NEXT(s->currevp, entry); - if (line[0] == '2') { - s->failures = 0; - /* - * this host is up, reschedule envelopes that - * were cached for reschedule. - */ - if (domain) - mta_hoststat_reschedule(domain); - } - else { - mta_report_tx_rollback(s, s->task->msgid); - mta_report_tx_reset(s, s->task->msgid); - if (line[0] == '5') - delivery = IMSG_MTA_DELIVERY_PERMFAIL; - else - delivery = IMSG_MTA_DELIVERY_TEMPFAIL; - s->failures++; - - /* remove failed envelope from task list */ - TAILQ_REMOVE(&s->task->envelopes, e, entry); - stat_decrement("mta.envelope", 1); - - /* log right away */ - (void)snprintf(buf, sizeof(buf), "%s", - mta_host_to_text(s->route->dst)); - - e->session = s->id; - /* XXX */ - /* - * getsockname() can only fail with ENOBUFS here - * best effort, don't log source ... - */ - sa_len = sizeof(ss); - sa = (struct sockaddr *)&ss; - if (getsockname(io_fileno(s->io), sa, &sa_len) == -1) - mta_delivery_log(e, NULL, buf, delivery, line); - else - mta_delivery_log(e, sa_to_text(sa), - buf, delivery, line); - - if (domain) - mta_hoststat_update(domain, e->status); - mta_delivery_notify(e); - - if (s->relay->limits->max_failures_per_session && - s->failures == s->relay->limits->max_failures_per_session) { - mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, - "Too many consecutive errors, closing connection", 0, 1); - mta_enter_state(s, MTA_QUIT); - break; - } - - /* - * if no more envelopes, flush failed queue - */ - if (TAILQ_EMPTY(&s->task->envelopes)) { - mta_flush_task(s, IMSG_MTA_DELIVERY_OK, - "No envelope", 0, 0); - mta_enter_state(s, MTA_RSET); - break; - } - } - - switch (line[0]) { - case '2': - mta_report_tx_rcpt(s, - s->task->msgid, e->dest, 1); - break; - case '4': - mta_report_tx_rcpt(s, - s->task->msgid, e->dest, -1); - break; - case '5': - mta_report_tx_rcpt(s, - s->task->msgid, e->dest, 0); - break; - } - - if (s->currevp == NULL) - mta_enter_state(s, MTA_DATA); - else - mta_enter_state(s, MTA_RCPT); - break; - - case MTA_DATA: - if (line[0] == '2' || line[0] == '3') { - mta_report_tx_data(s, s->task->msgid, 1); - mta_enter_state(s, MTA_BODY); - break; - } - - if (line[0] == '5') - delivery = IMSG_MTA_DELIVERY_PERMFAIL; - else - delivery = IMSG_MTA_DELIVERY_TEMPFAIL; - mta_report_tx_data(s, s->task->msgid, - delivery == IMSG_MTA_DELIVERY_TEMPFAIL ? -1 : 0); - mta_report_tx_rollback(s, s->task->msgid); - mta_report_tx_reset(s, s->task->msgid); - mta_flush_task(s, delivery, line, 0, 0); - mta_enter_state(s, MTA_RSET); - break; - - case MTA_LMTP_EOM: - case MTA_EOM: - if (line[0] == '2') { - delivery = IMSG_MTA_DELIVERY_OK; - s->msgtried = 0; - s->msgcount++; - } - else if (line[0] == '5') - delivery = IMSG_MTA_DELIVERY_PERMFAIL; - else - delivery = IMSG_MTA_DELIVERY_TEMPFAIL; - if (delivery != IMSG_MTA_DELIVERY_OK) { - mta_report_tx_rollback(s, s->task->msgid); - mta_report_tx_reset(s, s->task->msgid); - } - else { - mta_report_tx_commit(s, s->task->msgid, s->datalen); - mta_report_tx_reset(s, s->task->msgid); - } - mta_flush_task(s, delivery, line, (s->flags & MTA_LMTP) ? 1 : 0, 0); - if (s->task) { - s->rcptcount--; - mta_enter_state(s, MTA_LMTP_EOM); - } else { - s->rcptcount = 0; - if (s->relay->limits->sessdelay_transaction) { - log_debug("debug: mta: waiting for %llds before next transaction", - (long long int)s->relay->limits->sessdelay_transaction); - s->hangon = s->relay->limits->sessdelay_transaction -1; - s->flags |= MTA_HANGON; - runq_schedule(hangon, - s->relay->limits->sessdelay_transaction, s); - } - else - mta_enter_state(s, MTA_READY); - } - break; - - case MTA_RSET: - s->rcptcount = 0; - - if (s->task) { - mta_report_tx_rollback(s, s->task->msgid); - mta_report_tx_reset(s, s->task->msgid); - } - if (s->relay->limits->sessdelay_transaction) { - log_debug("debug: mta: waiting for %llds after reset", - (long long int)s->relay->limits->sessdelay_transaction); - s->hangon = s->relay->limits->sessdelay_transaction -1; - s->flags |= MTA_HANGON; - runq_schedule(hangon, - s->relay->limits->sessdelay_transaction, s); - } - else - mta_enter_state(s, MTA_READY); - break; - - default: - fatalx("mta_response() bad state"); - } -} - -static void -mta_io(struct io *io, int evt, void *arg) -{ - struct mta_session *s = arg; - char *line, *msg, *p; - size_t len; - const char *error; - int cont; - - log_trace(TRACE_IO, "mta: %p: %s %s", s, io_strevent(evt), - io_strio(io)); - - switch (evt) { - - case IO_CONNECTED: - mta_connected(s); - - if (s->use_smtps) { - io_set_write(io); - mta_cert_init(s); - } - else { - mta_enter_state(s, MTA_BANNER); - io_set_read(io); - } - break; - - case IO_TLSREADY: - log_info("%016"PRIx64" mta tls ciphers=%s", - s->id, ssl_to_text(io_tls(s->io))); - s->flags |= MTA_TLS; - - mta_report_link_tls(s, - ssl_to_text(io_tls(s->io))); - - mta_cert_verify(s); - break; - - case IO_DATAIN: - nextline: - line = io_getline(s->io, &len); - if (line == NULL) { - if (io_datalen(s->io) >= LINE_MAX) { - mta_error(s, "Input too long"); - mta_free(s); - } - return; - } - - /* Strip trailing '\r' */ - if (len && line[len - 1] == '\r') - line[--len] = '\0'; - - log_trace(TRACE_MTA, "mta: %p: <<< %s", s, line); - mta_report_protocol_server(s, line); - - if ((error = parse_smtp_response(line, len, &msg, &cont))) { - mta_error(s, "Bad response: %s", error); - mta_free(s); - return; - } - - /* read extensions */ - if (s->state == MTA_EHLO) { - if (strcmp(msg, "STARTTLS") == 0) - s->ext |= MTA_EXT_STARTTLS; - else if (strncmp(msg, "AUTH ", 5) == 0) { - s->ext |= MTA_EXT_AUTH; - if ((p = strstr(msg, " PLAIN")) && - (*(p+6) == '\0' || *(p+6) == ' ')) - s->ext |= MTA_EXT_AUTH_PLAIN; - if ((p = strstr(msg, " LOGIN")) && - (*(p+6) == '\0' || *(p+6) == ' ')) - s->ext |= MTA_EXT_AUTH_LOGIN; - } - else if (strcmp(msg, "PIPELINING") == 0) - s->ext |= MTA_EXT_PIPELINING; - else if (strcmp(msg, "DSN") == 0) - s->ext |= MTA_EXT_DSN; - else if (strncmp(msg, "SIZE ", 5) == 0) { - s->ext_size = strtonum(msg+5, 0, UINT32_MAX, &error); - if (error == NULL) - s->ext |= MTA_EXT_SIZE; - } - } - - /* continuation reply, we parse out the repeating statuses and ESC */ - if (cont) { - if (s->replybuf[0] == '\0') - (void)strlcat(s->replybuf, line, sizeof s->replybuf); - else if (len > 4) { - p = line + 4; - if (isdigit((unsigned char)p[0]) && p[1] == '.' && - isdigit((unsigned char)p[2]) && p[3] == '.' && - isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5])) - p += 5; - (void)strlcat(s->replybuf, p, sizeof s->replybuf); - } - goto nextline; - } - - /* last line of a reply, check if we're on a continuation to parse out status and ESC. - * if we overflow reply buffer or are not on continuation, log entire last line. - */ - if (s->replybuf[0] == '\0') - (void)strlcat(s->replybuf, line, sizeof s->replybuf); - else if (len > 4) { - p = line + 4; - if (isdigit((unsigned char)p[0]) && p[1] == '.' && - isdigit((unsigned char)p[2]) && p[3] == '.' && - isdigit((unsigned char)p[4]) && isspace((unsigned char)p[5])) - p += 5; - if (strlcat(s->replybuf, p, sizeof s->replybuf) >= sizeof s->replybuf) - (void)strlcpy(s->replybuf, line, sizeof s->replybuf); - } - - if (s->state == MTA_QUIT) { - log_info("%016"PRIx64" mta disconnected reason=quit messages=%zu", - s->id, s->msgcount); - mta_free(s); - return; - } - io_set_write(io); - mta_response(s, s->replybuf); - if (s->flags & MTA_FREE) { - mta_free(s); - return; - } - if (s->flags & MTA_RECONN) { - s->flags &= ~MTA_RECONN; - mta_connect(s); - return; - } - - if (io_datalen(s->io)) { - log_debug("debug: mta: remaining data in input buffer"); - mta_error(s, "Remote host sent too much data"); - if (s->flags & MTA_WAIT) - s->flags |= MTA_FREE; - else - mta_free(s); - } - break; - - case IO_LOWAT: - if (s->state == MTA_BODY) { - mta_enter_state(s, MTA_BODY); - if (s->flags & MTA_FREE) { - mta_free(s); - return; - } - } - - if (io_queued(s->io) == 0) - io_set_read(io); - break; - - case IO_TIMEOUT: - log_debug("debug: mta: %p: connection timeout", s); - mta_error(s, "Connection timeout"); - mta_report_timeout(s); - if (!s->ready) - mta_connect(s); - else - mta_free(s); - break; - - case IO_ERROR: - case IO_TLSERROR: - log_debug("debug: mta: %p: IO error: %s", s, io_error(io)); - - if (s->state == MTA_STARTTLS && s->use_smtp_tls) { - /* error in non-strict SSL negotiation, downgrade to plain */ - log_info("smtp-out: Error on session %016"PRIx64 - ": opportunistic TLS failed, " - "downgrading to plain", s->id); - s->flags &= ~MTA_TLS; - s->flags |= MTA_DOWNGRADE_PLAIN; - mta_connect(s); - break; - } - - mta_error(s, "IO Error: %s", io_error(io)); - mta_free(s); - break; - - case IO_DISCONNECTED: - log_debug("debug: mta: %p: disconnected in state %s", - s, mta_strstate(s->state)); - mta_error(s, "Connection closed unexpectedly"); - if (!s->ready) - mta_connect(s); - else - mta_free(s); - break; - - default: - fatalx("mta_io() bad event"); - } -} - -static void -mta_send(struct mta_session *s, char *fmt, ...) -{ - va_list ap; - char *p; - int len; - - va_start(ap, fmt); - if ((len = vasprintf(&p, fmt, ap)) == -1) - fatal("mta: vasprintf"); - va_end(ap); - - log_trace(TRACE_MTA, "mta: %p: >>> %s", s, p); - - if (strncasecmp(p, "AUTH PLAIN ", 11) == 0) - mta_report_protocol_client(s, "AUTH PLAIN ********"); - else if (s->state == MTA_AUTH_LOGIN_USER || s->state == MTA_AUTH_LOGIN_PASS) - mta_report_protocol_client(s, "********"); - else - mta_report_protocol_client(s, p); - - io_xprintf(s->io, "%s\r\n", p); - - free(p); -} - -/* - * Queue some data into the input buffer - */ -static ssize_t -mta_queue_data(struct mta_session *s) -{ - char *ln = NULL; - size_t sz = 0, q; - ssize_t len; - - q = io_queued(s->io); - - while (io_queued(s->io) < MTA_HIWAT) { - if ((len = getline(&ln, &sz, s->datafp)) == -1) - break; - if (ln[len - 1] == '\n') - ln[len - 1] = '\0'; - s->datalen += io_xprintf(s->io, "%s%s\r\n", *ln == '.' ? "." : "", ln); - } - - free(ln); - if (ferror(s->datafp)) { - mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, - "Error reading content file", 0, 0); - return (-1); - } - - if (feof(s->datafp)) { - fclose(s->datafp); - s->datafp = NULL; - } - - return (io_queued(s->io) - q); -} - -static void -mta_flush_task(struct mta_session *s, int delivery, const char *error, size_t count, - int cache) -{ - struct mta_envelope *e; - char relay[LINE_MAX]; - size_t n; - struct sockaddr_storage ss; - struct sockaddr *sa; - socklen_t sa_len; - const char *domain; - - (void)snprintf(relay, sizeof relay, "%s", mta_host_to_text(s->route->dst)); - n = 0; - while ((e = TAILQ_FIRST(&s->task->envelopes))) { - - if (count && n == count) { - stat_decrement("mta.envelope", n); - return; - } - - TAILQ_REMOVE(&s->task->envelopes, e, entry); - - /* we're about to log, associate session to envelope */ - e->session = s->id; - e->ext = s->ext; - - /* XXX */ - /* - * getsockname() can only fail with ENOBUFS here - * best effort, don't log source ... - */ - sa = (struct sockaddr *)&ss; - sa_len = sizeof(ss); - if (getsockname(io_fileno(s->io), sa, &sa_len) == -1) - mta_delivery_log(e, NULL, relay, delivery, error); - else - mta_delivery_log(e, sa_to_text(sa), - relay, delivery, error); - - mta_delivery_notify(e); - - domain = strchr(e->dest, '@'); - if (domain) { - domain++; - mta_hoststat_update(domain, error); - if (cache) - mta_hoststat_cache(domain, e->id); - } - - n++; - } - - free(s->task->sender); - free(s->task); - s->task = NULL; - - if (s->datafp) { - fclose(s->datafp); - s->datafp = NULL; - } - - stat_decrement("mta.envelope", n); - stat_decrement("mta.task.running", 1); - stat_decrement("mta.task", 1); -} - -static void -mta_error(struct mta_session *s, const char *fmt, ...) -{ - va_list ap; - char *error; - int len; - - va_start(ap, fmt); - if ((len = vasprintf(&error, fmt, ap)) == -1) - fatal("mta: vasprintf"); - va_end(ap); - - if (s->msgcount) - log_info("smtp-out: Error on session %016"PRIx64 - " after %zu message%s sent: %s", s->id, s->msgcount, - (s->msgcount > 1) ? "s" : "", error); - else - log_info("%016"PRIx64" mta error reason=%s", - s->id, error); - - /* - * If not connected yet, and the error is not local, just ignore it - * and try to reconnect. - */ - if (s->state == MTA_INIT && - (errno == ETIMEDOUT || errno == ECONNREFUSED)) { - log_debug("debug: mta: not reporting route error yet"); - free(error); - return; - } - - mta_route_error(s->relay, s->route); - - if (s->task) - mta_flush_task(s, IMSG_MTA_DELIVERY_TEMPFAIL, error, 0, 0); - - free(error); -} - -static void -mta_cert_init(struct mta_session *s) -{ - const char *name; - int fallback; - - if (s->relay->pki_name) { - name = s->relay->pki_name; - fallback = 0; - } - else { - name = s->helo; - fallback = 1; - } - - if (cert_init(name, fallback, mta_cert_init_cb, s)) { - tree_xset(&wait_tls_init, s->id, s); - s->flags |= MTA_WAIT; - } -} - -static void -mta_cert_init_cb(void *arg, int status, const char *name, const void *cert, - size_t cert_len) -{ - struct mta_session *s = arg; - void *ssl; - char *xname = NULL, *xcert = NULL; - - if (s->flags & MTA_WAIT) - mta_tree_pop(&wait_tls_init, s->id); - - if (status == CA_FAIL && s->relay->pki_name) { - log_info("%016"PRIx64" mta closing reason=ca-failure", s->id); - mta_free(s); - return; - } - - if (name) - xname = xstrdup(name); - if (cert) - xcert = xmemdup(cert, cert_len); - ssl = ssl_mta_init(xname, xcert, cert_len, env->sc_tls_ciphers); - free(xname); - free(xcert); - if (ssl == NULL) - fatal("mta: ssl_mta_init"); - io_start_tls(s->io, ssl); -} - -static void -mta_cert_verify(struct mta_session *s) -{ - const char *name; - int fallback; - - if (s->relay->ca_name) { - name = s->relay->ca_name; - fallback = 0; - } - else { - name = s->helo; - fallback = 1; - } - - if (cert_verify(io_tls(s->io), name, fallback, mta_cert_verify_cb, s)) { - tree_xset(&wait_tls_verify, s->id, s); - io_pause(s->io, IO_IN); - s->flags |= MTA_WAIT; - } -} - -static void -mta_cert_verify_cb(void *arg, int status) -{ - struct mta_session *s = arg; - int match, resume = 0; - X509 *cert; - - if (s->flags & MTA_WAIT) { - mta_tree_pop(&wait_tls_verify, s->id); - resume = 1; - } - - if (status == CERT_OK) { - cert = SSL_get_peer_certificate(io_tls(s->io)); - if (!cert) - status = CERT_NOCERT; - else { - match = 0; - (void)ssl_check_name(cert, s->mxname, &match); - X509_free(cert); - if (!match) { - log_info("%016"PRIx64" mta " - "ssl_check_name: no match for '%s' in cert", - s->id, s->mxname); - status = CERT_INVALID; - } - } - } - - if (status == CERT_OK) - s->flags |= MTA_TLS_VERIFIED; - else if (s->relay->flags & RELAY_TLS_VERIFY) { - errno = 0; - mta_error(s, "SSL certificate check failed"); - mta_free(s); - return; - } - - mta_tls_verified(s); - if (resume) - io_resume(s->io, IO_IN); -} - -static void -mta_tls_verified(struct mta_session *s) -{ - X509 *x; - - x = SSL_get_peer_certificate(io_tls(s->io)); - if (x) { - log_info("%016"PRIx64" mta " - "server-cert-check result=\"%s\"", - s->id, - (s->flags & MTA_TLS_VERIFIED) ? "success" : "failure"); - X509_free(x); - } - - if (s->use_smtps) { - mta_enter_state(s, MTA_BANNER); - io_set_read(s->io); - } - else - mta_enter_state(s, MTA_EHLO); -} - -static const char * -dsn_strret(enum dsn_ret ret) -{ - if (ret == DSN_RETHDRS) - return "HDRS"; - else if (ret == DSN_RETFULL) - return "FULL"; - else { - log_debug("mta: invalid ret %d", ret); - return "???"; - } -} - -static const char * -dsn_strnotify(uint8_t arg) -{ - static char buf[32]; - size_t sz; - - buf[0] = '\0'; - if (arg & DSN_SUCCESS) - (void)strlcat(buf, "SUCCESS,", sizeof(buf)); - - if (arg & DSN_FAILURE) - (void)strlcat(buf, "FAILURE,", sizeof(buf)); - - if (arg & DSN_DELAY) - (void)strlcat(buf, "DELAY,", sizeof(buf)); - - if (arg & DSN_NEVER) - (void)strlcat(buf, "NEVER,", sizeof(buf)); - - /* trim trailing comma */ - sz = strlen(buf); - if (sz) - buf[sz - 1] = '\0'; - - return (buf); -} - -#define CASE(x) case x : return #x - -static const char * -mta_strstate(int state) -{ - switch (state) { - CASE(MTA_INIT); - CASE(MTA_BANNER); - CASE(MTA_EHLO); - CASE(MTA_HELO); - CASE(MTA_STARTTLS); - CASE(MTA_AUTH); - CASE(MTA_AUTH_PLAIN); - CASE(MTA_AUTH_LOGIN); - CASE(MTA_AUTH_LOGIN_USER); - CASE(MTA_AUTH_LOGIN_PASS); - CASE(MTA_READY); - CASE(MTA_MAIL); - CASE(MTA_RCPT); - CASE(MTA_DATA); - CASE(MTA_BODY); - CASE(MTA_EOM); - CASE(MTA_LMTP_EOM); - CASE(MTA_RSET); - CASE(MTA_QUIT); - default: - return "MTA_???"; - } -} - -static void -mta_filter_begin(struct mta_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->relay->dispatcher->u.remote.filtername); - m_close(p_lka); -} - -static void -mta_filter_end(struct mta_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 -mta_connected(struct mta_session *s) -{ - struct sockaddr sa_src; - struct sockaddr sa_dest; - int sa_len; - - log_info("%016"PRIx64" mta connected", s->id); - - if (getsockname(io_fileno(s->io), &sa_src, &sa_len) == -1) - bzero(&sa_src, sizeof sa_src); - if (getpeername(io_fileno(s->io), &sa_dest, &sa_len) == -1) - bzero(&sa_dest, sizeof sa_dest); - - mta_report_link_connect(s, - s->route->dst->ptrname, 1, - (struct sockaddr_storage *)&sa_src, - (struct sockaddr_storage *)&sa_dest); -} - -static void -mta_disconnected(struct mta_session *s) -{ - mta_report_link_disconnect(s); - mta_filter_end(s); -} - - -static void -mta_report_link_connect(struct mta_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-out", s->id, rdns, fcrdns, ss_src, ss_dest); -} - -static void -mta_report_link_greeting(struct mta_session *s, - const char *domain) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_greeting("smtp-out", s->id, domain); -} - -static void -mta_report_link_identify(struct mta_session *s, const char *method, const char *identity) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_identify("smtp-out", s->id, method, identity); -} - -static void -mta_report_link_tls(struct mta_session *s, const char *ssl) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_tls("smtp-out", s->id, ssl); -} - -static void -mta_report_link_disconnect(struct mta_session *s) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_disconnect("smtp-out", s->id); -} - -static void -mta_report_link_auth(struct mta_session *s, const char *user, const char *result) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_link_auth("smtp-out", s->id, user, result); -} - -static void -mta_report_tx_reset(struct mta_session *s, uint32_t msgid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_reset("smtp-out", s->id, msgid); -} - -static void -mta_report_tx_begin(struct mta_session *s, uint32_t msgid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_begin("smtp-out", s->id, msgid); -} - -static void -mta_report_tx_mail(struct mta_session *s, uint32_t msgid, const char *address, int ok) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_mail("smtp-out", s->id, msgid, address, ok); -} - -static void -mta_report_tx_rcpt(struct mta_session *s, uint32_t msgid, const char *address, int ok) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_rcpt("smtp-out", s->id, msgid, address, ok); -} - -static void -mta_report_tx_envelope(struct mta_session *s, uint32_t msgid, uint64_t evpid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_envelope("smtp-out", s->id, msgid, evpid); -} - -static void -mta_report_tx_data(struct mta_session *s, uint32_t msgid, int ok) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_data("smtp-out", s->id, msgid, ok); -} - -static void -mta_report_tx_commit(struct mta_session *s, uint32_t msgid, size_t msgsz) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_commit("smtp-out", s->id, msgid, msgsz); -} - -static void -mta_report_tx_rollback(struct mta_session *s, uint32_t msgid) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_tx_rollback("smtp-out", s->id, msgid); -} - -static void -mta_report_protocol_client(struct mta_session *s, const char *command) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_protocol_client("smtp-out", s->id, command); -} - -static void -mta_report_protocol_server(struct mta_session *s, const char *response) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_protocol_server("smtp-out", s->id, response); -} - -#if 0 -static void -mta_report_filter_response(struct mta_session *s, int phase, int response, const char *param) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_filter_response("smtp-out", s->id, phase, response, param); -} -#endif - -static void -mta_report_timeout(struct mta_session *s) -{ - if (! SESSION_FILTERED(s)) - return; - - report_smtp_timeout("smtp-out", s->id); -} |