aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGilles Chehade <gilles@poolp.org>2020-01-06 17:09:29 +0100
committerGilles Chehade <gilles@poolp.org>2020-01-06 17:09:29 +0100
commitfdb3ae1f13635fe9a8f0ee31510ef4273d83ce27 (patch)
treebd04c5bbf42df322ed029ce98a0f6876b7d3ba73
parentMerge branch 'master' into libtls (diff)
parentsync (diff)
downloadOpenSMTPD-fdb3ae1f13635fe9a8f0ee31510ef4273d83ce27.tar.xz
OpenSMTPD-fdb3ae1f13635fe9a8f0ee31510ef4273d83ce27.zip
Merge branch 'master' into libtls
-rw-r--r--THANKS19
-rw-r--r--smtpd/bounce.c100
-rw-r--r--smtpd/config.c10
-rw-r--r--smtpd/envelope.c12
-rw-r--r--smtpd/lka.c19
-rw-r--r--smtpd/lka_filter.c684
-rw-r--r--smtpd/lka_proc.c188
-rw-r--r--smtpd/lka_report.c509
-rw-r--r--smtpd/mail.lmtp.c51
-rw-r--r--smtpd/mail.mboxfile.c2
-rw-r--r--smtpd/mta.c7
-rw-r--r--smtpd/mta_session.c340
-rw-r--r--smtpd/parse.y249
-rw-r--r--smtpd/ruleset.c25
-rw-r--r--smtpd/smtp_session.c257
-rw-r--r--smtpd/smtpd-filters.7646
-rw-r--r--smtpd/smtpd.c56
-rw-r--r--smtpd/smtpd.conf.5238
-rw-r--r--smtpd/smtpd.h35
-rw-r--r--smtpd/smtpd/Makefile2
-rw-r--r--smtpd/spfwalk.c157
-rw-r--r--smtpd/to.c4
22 files changed, 2562 insertions, 1048 deletions
diff --git a/THANKS b/THANKS
deleted file mode 100644
index 20f8fe99..00000000
--- a/THANKS
+++ /dev/null
@@ -1,19 +0,0 @@
-Jason McIntyre
-Freddy Dissaux
-Alexandre Lissy
-Brad Arrington
-Todd T. Fries
-Francois Tigeot
-Ashish Shukla
-Jean-Loup Colautti
-Rune Lynge
-Jason A. Donenfeld
-Gleb Kozyrev
-Colin Didier
-Ryan Kavanagh
-Sunil Nimmagadda
-
-
-Miod Vallat
-
-And of course Gilles Chehade and Eric Faurot for their work and continuous help.
diff --git a/smtpd/bounce.c b/smtpd/bounce.c
index 02239988..4ce0ff71 100644
--- a/smtpd/bounce.c
+++ b/smtpd/bounce.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: bounce.c,v 1.80 2018/12/08 08:01:15 sunil Exp $ */
+/* $OpenBSD: bounce.c,v 1.81 2019/11/25 12:11:26 eric Exp $ */
/*
* Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
@@ -184,7 +184,7 @@ bounce_add(uint64_t evpid)
line = evp.errorline;
if (strlen(line) > 4 && (*line == '1' || *line == '6'))
line += 4;
- (void)snprintf(buf, sizeof(buf), "%s@%s: %s\n", evp.dest.user,
+ (void)snprintf(buf, sizeof(buf), "%s@%s: %s", evp.dest.user,
evp.dest.domain, line);
be = xmalloc(sizeof *be);
@@ -196,8 +196,7 @@ bounce_add(uint64_t evpid)
be->esc_class = evp.esc_class;
be->esc_code = evp.esc_code;
TAILQ_INSERT_TAIL(&msg->envelopes, be, entry);
- buf[strcspn(buf, "\n")] = '\0';
- log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, buf);
+ log_debug("debug: bounce: adding report %16"PRIx64": %s", be->id, be->report);
msg->timeout = time(NULL) + 1;
TAILQ_INSERT_TAIL(&pending, msg, entry);
@@ -311,7 +310,7 @@ bounce_send(struct bounce_session *s, const char *fmt, ...)
log_trace(TRACE_BOUNCE, "bounce: %p: >>> %s", s, p);
- io_xprintf(s->io, "%s\n", p);
+ io_xprintf(s->io, "%s\r\n", p);
free(p);
}
@@ -343,27 +342,27 @@ bounce_duration(long long int d)
}
#define NOTICE_INTRO \
- " Hi!\n\n" \
- " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\n"
+ " Hi!\r\n\r\n" \
+ " This is the MAILER-DAEMON, please DO NOT REPLY to this email.\r\n"
const char *notice_error =
- " An error has occurred while attempting to deliver a message for\n"
- " the following list of recipients:\n\n";
+ " An error has occurred while attempting to deliver a message for\r\n"
+ " the following list of recipients:\r\n\r\n";
const char *notice_warning =
- " A message is delayed for more than %s for the following\n"
- " list of recipients:\n\n";
+ " A message is delayed for more than %s for the following\r\n"
+ " list of recipients:\r\n\r\n";
const char *notice_warning2 =
- " Please note that this is only a temporary failure report.\n"
- " The message is kept in the queue for up to %s.\n"
- " You DO NOT NEED to re-send the message to these recipients.\n\n";
+ " Please note that this is only a temporary failure report.\r\n"
+ " The message is kept in the queue for up to %s.\r\n"
+ " You DO NOT NEED to re-send the message to these recipients.\r\n\r\n";
const char *notice_success =
- " Your message was successfully delivered to these recipients.\n\n";
+ " Your message was successfully delivered to these recipients.\r\n\r\n";
const char *notice_relay =
- " Your message was relayed to these recipients.\n\n";
+ " Your message was relayed to these recipients.\r\n\r\n";
static int
bounce_next_message(struct bounce_session *s)
@@ -451,16 +450,16 @@ bounce_next(struct bounce_session *s)
/* Construct an appropriate notice. */
io_xprintf(s->io,
- "Subject: Delivery status notification: %s\n"
- "From: Mailer Daemon <MAILER-DAEMON@%s>\n"
- "To: %s\n"
- "Date: %s\n"
- "MIME-Version: 1.0\n"
+ "Subject: Delivery status notification: %s\r\n"
+ "From: Mailer Daemon <MAILER-DAEMON@%s>\r\n"
+ "To: %s\r\n"
+ "Date: %s\r\n"
+ "MIME-Version: 1.0\r\n"
"Content-Type: multipart/mixed;"
- "boundary=\"%16" PRIu64 "/%s\"\n"
- "\n"
- "This is a MIME-encapsulated message.\n"
- "\n",
+ "boundary=\"%16" PRIu64 "/%s\"\r\n"
+ "\r\n"
+ "This is a MIME-encapsulated message.\r\n"
+ "\r\n",
action_str(&s->msg->bounce),
s->smtpname,
s->msg->to,
@@ -469,12 +468,12 @@ bounce_next(struct bounce_session *s)
s->smtpname);
io_xprintf(s->io,
- "--%16" PRIu64 "/%s\n"
- "Content-Description: Notification\n"
- "Content-Type: text/plain; charset=us-ascii\n"
- "\n"
+ "--%16" PRIu64 "/%s\r\n"
+ "Content-Description: Notification\r\n"
+ "Content-Type: text/plain; charset=us-ascii\r\n"
+ "\r\n"
NOTICE_INTRO
- "\n",
+ "\r\n",
s->boundary, s->smtpname);
switch (s->msg->bounce.type) {
@@ -495,35 +494,36 @@ bounce_next(struct bounce_session *s)
TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
io_xprint(s->io, evp->report);
+ io_xprint(s->io, "\r\n");
}
- io_xprint(s->io, "\n");
+ io_xprint(s->io, "\r\n");
if (s->msg->bounce.type == B_DELAYED)
io_xprintf(s->io, notice_warning2,
bounce_duration(s->msg->bounce.ttl));
io_xprintf(s->io,
- " Below is a copy of the original message:\n"
- "\n");
+ " Below is a copy of the original message:\r\n"
+ "\r\n");
io_xprintf(s->io,
- "--%16" PRIu64 "/%s\n"
- "Content-Description: Delivery Report\n"
- "Content-Type: message/delivery-status\n"
- "\n",
+ "--%16" PRIu64 "/%s\r\n"
+ "Content-Description: Delivery Report\r\n"
+ "Content-Type: message/delivery-status\r\n"
+ "\r\n",
s->boundary, s->smtpname);
io_xprintf(s->io,
- "Reporting-MTA: dns; %s\n"
- "\n",
+ "Reporting-MTA: dns; %s\r\n"
+ "\r\n",
s->smtpname);
TAILQ_FOREACH(evp, &s->msg->envelopes, entry) {
io_xprintf(s->io,
- "Final-Recipient: rfc822; %s@%s\n"
- "Action: %s\n"
- "Status: %s\n"
- "\n",
+ "Final-Recipient: rfc822; %s@%s\r\n"
+ "Action: %s\r\n"
+ "Status: %s\r\n"
+ "\r\n",
evp->dest.user,
evp->dest.domain,
action_str(&s->msg->bounce),
@@ -538,10 +538,10 @@ bounce_next(struct bounce_session *s)
case BOUNCE_DATA_MESSAGE:
io_xprintf(s->io,
- "--%16" PRIu64 "/%s\n"
- "Content-Description: Message headers\n"
- "Content-Type: text/rfc822-headers\n"
- "\n",
+ "--%16" PRIu64 "/%s\r\n"
+ "Content-Description: Message headers\r\n"
+ "Content-Type: text/rfc822-headers\r\n"
+ "\r\n",
s->boundary, s->smtpname);
n = io_queued(s->io);
@@ -555,14 +555,14 @@ bounce_next(struct bounce_session *s)
fclose(s->msgfp);
s->msgfp = NULL;
io_xprintf(s->io,
- "\n--%16" PRIu64 "/%s--\n", s->boundary,
+ "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary,
s->smtpname);
bounce_send(s, ".");
s->state = BOUNCE_DATA_END;
return (0);
}
line[len - 1] = '\0';
- io_xprintf(s->io, "%s%s\n",
+ io_xprintf(s->io, "%s%s\r\n",
(len == 2 && line[0] == '.') ? "." : "", line);
}
free(line);
@@ -577,7 +577,7 @@ bounce_next(struct bounce_session *s)
}
io_xprintf(s->io,
- "\n--%16" PRIu64 "/%s--\n", s->boundary, s->smtpname);
+ "\r\n--%16" PRIu64 "/%s--\r\n", s->boundary, s->smtpname);
log_trace(TRACE_BOUNCE, "bounce: %p: >>> [... %zu bytes ...]",
s, io_queued(s->io) - n);
diff --git a/smtpd/config.c b/smtpd/config.c
index 054cf03d..521139b1 100644
--- a/smtpd/config.c
+++ b/smtpd/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.50 2019/09/20 17:46:05 gilles Exp $ */
+/* $OpenBSD: config.c,v 1.51 2019/12/18 10:00:39 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -88,7 +88,7 @@ config_default(void)
conf->sc_pki_dict = calloc(1, sizeof(*conf->sc_pki_dict));
conf->sc_limits_dict = calloc(1, sizeof(*conf->sc_limits_dict));
conf->sc_mda_wrappers = calloc(1, sizeof(*conf->sc_mda_wrappers));
- conf->sc_processors_dict = calloc(1, sizeof(*conf->sc_processors_dict));
+ conf->sc_filter_processes_dict = calloc(1, sizeof(*conf->sc_filter_processes_dict));
conf->sc_dispatcher_bounce = calloc(1, sizeof(*conf->sc_dispatcher_bounce));
conf->sc_filters_dict = calloc(1, sizeof(*conf->sc_filters_dict));
limits = calloc(1, sizeof(*limits));
@@ -101,7 +101,7 @@ config_default(void)
conf->sc_pki_dict == NULL ||
conf->sc_limits_dict == NULL ||
conf->sc_mda_wrappers == NULL ||
- conf->sc_processors_dict == NULL ||
+ conf->sc_filter_processes_dict == NULL ||
conf->sc_dispatcher_bounce == NULL ||
conf->sc_filters_dict == NULL ||
limits == NULL)
@@ -113,7 +113,7 @@ config_default(void)
dict_init(conf->sc_pki_dict);
dict_init(conf->sc_tables_dict);
dict_init(conf->sc_limits_dict);
- dict_init(conf->sc_processors_dict);
+ dict_init(conf->sc_filter_processes_dict);
limit_mta_set_defaults(limits);
@@ -151,7 +151,7 @@ error:
free(conf->sc_pki_dict);
free(conf->sc_limits_dict);
free(conf->sc_mda_wrappers);
- free(conf->sc_processors_dict);
+ free(conf->sc_filter_processes_dict);
free(conf->sc_dispatcher_bounce);
free(conf->sc_filters_dict);
free(limits);
diff --git a/smtpd/envelope.c b/smtpd/envelope.c
index 01957dc5..05a239e1 100644
--- a/smtpd/envelope.c
+++ b/smtpd/envelope.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: envelope.c,v 1.46 2019/09/19 16:00:59 gilles Exp $ */
+/* $OpenBSD: envelope.c,v 1.47 2019/11/25 14:18:32 gilles Exp $ */
/*
* Copyright (c) 2013 Eric Faurot <eric@openbsd.org>
@@ -177,6 +177,7 @@ envelope_dump_buffer(const struct envelope *ep, char *dest, size_t len)
envelope_ascii_dump(ep, &dest, &len, "smtpname");
envelope_ascii_dump(ep, &dest, &len, "helo");
envelope_ascii_dump(ep, &dest, &len, "hostname");
+ envelope_ascii_dump(ep, &dest, &len, "username");
envelope_ascii_dump(ep, &dest, &len, "errorline");
envelope_ascii_dump(ep, &dest, &len, "sockaddr");
envelope_ascii_dump(ep, &dest, &len, "sender");
@@ -400,6 +401,9 @@ ascii_load_field(const char *field, struct envelope *ep, char *buf)
if (strcasecmp("dest", field) == 0)
return ascii_load_mailaddr(&ep->dest, buf);
+ if (strcasecmp("username", field) == 0)
+ return ascii_load_string(ep->username, buf, sizeof(ep->username));
+
if (strcasecmp("errorline", field) == 0)
return ascii_load_string(ep->errorline, buf,
sizeof ep->errorline);
@@ -646,6 +650,12 @@ ascii_dump_field(const char *field, const struct envelope *ep,
if (strcasecmp(field, "dest") == 0)
return ascii_dump_mailaddr(&ep->dest, buf, len);
+ if (strcasecmp(field, "username") == 0) {
+ if (ep->username[0])
+ return ascii_dump_string(ep->username, buf, len);
+ return 1;
+ }
+
if (strcasecmp(field, "errorline") == 0)
return ascii_dump_string(ep->errorline, buf, len);
diff --git a/smtpd/lka.c b/smtpd/lka.c
index 0d4b43db..aa9f2335 100644
--- a/smtpd/lka.c
+++ b/smtpd/lka.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka.c,v 1.240 2019/08/28 15:50:36 martijn Exp $ */
+/* $OpenBSD: lka.c,v 1.243 2019/12/21 10:23:37 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -94,6 +94,7 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
int filter_phase;
const char *filter_param;
uint32_t msgid;
+ uint32_t subsystems;
uint64_t evpid;
size_t msgsz;
int ok;
@@ -274,6 +275,7 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
case IMSG_MTA_LOOKUP_SMARTHOST:
m_msg(&m, imsg);
m_get_id(&m, &reqid);
+ m_get_string(&m, &domain);
m_get_string(&m, &tablename);
m_end(&m);
@@ -287,7 +289,11 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
m_add_int(p, LKA_TEMPFAIL);
}
else {
- ret = table_fetch(table, K_RELAYHOST, &lk);
+ if (domain == NULL)
+ ret = table_fetch(table, K_RELAYHOST, &lk);
+ else
+ ret = table_lookup(table, K_RELAYHOST, domain, &lk);
+
if (ret == -1)
m_add_int(p, LKA_TEMPFAIL);
else if (ret == 0)
@@ -362,13 +368,14 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
case IMSG_LKA_PROCESSOR_FORK:
m_msg(&m, imsg);
m_get_string(&m, &procname);
+ m_get_u32(&m, &subsystems);
m_end(&m);
m_create(p, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, -1);
m_add_string(p, procname);
m_close(p);
- lka_proc_forked(procname, imsg->fd);
+ lka_proc_forked(procname, subsystems, imsg->fd);
return;
case IMSG_LKA_PROCESSOR_ERRFD:
@@ -605,13 +612,9 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
m_msg(&m, imsg);
m_get_id(&m, &reqid);
m_get_string(&m, &filter_name);
- m_get_sockaddr(&m, (struct sockaddr *)&ss_src);
- m_get_sockaddr(&m, (struct sockaddr *)&ss_dest);
- m_get_string(&m, &rdns);
- m_get_int(&m, &fcrdns);
m_end(&m);
- lka_filter_begin(reqid, filter_name, &ss_src, &ss_dest, rdns, fcrdns);
+ lka_filter_begin(reqid, filter_name);
return;
case IMSG_FILTER_SMTP_END:
diff --git a/smtpd/lka_filter.c b/smtpd/lka_filter.c
index 695ce4c3..d8d0652b 100644
--- a/smtpd/lka_filter.c
+++ b/smtpd/lka_filter.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka_filter.c,v 1.50 2019/09/11 20:06:26 gilles Exp $ */
+/* $OpenBSD: lka_filter.c,v 1.57 2019/12/21 11:47:34 gilles Exp $ */
/*
* Copyright (c) 2018 Gilles Chehade <gilles@poolp.org>
@@ -35,7 +35,7 @@
#include "smtpd.h"
#include "log.h"
-#define PROTOCOL_VERSION "0.4"
+#define PROTOCOL_VERSION "0.5"
struct filter;
struct filter_session;
@@ -126,13 +126,208 @@ struct filter_chain {
TAILQ_HEAD(, filter_entry) chain[nitems(filter_execs)];
};
-static struct dict smtp_in;
+static struct dict filter_smtp_in;
static struct tree sessions;
-static int inited;
+static int filters_inited;
static struct dict filter_chains;
+struct reporter_proc {
+ TAILQ_ENTRY(reporter_proc) entries;
+ const char *name;
+};
+TAILQ_HEAD(reporters, reporter_proc);
+
+static struct dict report_smtp_in;
+static struct dict report_smtp_out;
+
+static struct smtp_events {
+ const char *event;
+} smtp_events[] = {
+ { "link-connect" },
+ { "link-disconnect" },
+ { "link-greeting" },
+ { "link-identify" },
+ { "link-tls" },
+ { "link-auth" },
+
+ { "tx-reset" },
+ { "tx-begin" },
+ { "tx-mail" },
+ { "tx-rcpt" },
+ { "tx-envelope" },
+ { "tx-data" },
+ { "tx-commit" },
+ { "tx-rollback" },
+
+ { "protocol-client" },
+ { "protocol-server" },
+
+ { "filter-report" },
+ { "filter-response" },
+
+ { "timeout" },
+};
+
+static int processors_inited = 0;
+static struct dict processors;
+
+struct processor_instance {
+ char *name;
+ struct io *io;
+ struct io *errfd;
+ int ready;
+ uint32_t subsystems;
+};
+
+static void processor_io(struct io *, int, void *);
+static void processor_errfd(struct io *, int, void *);
+void lka_filter_process_response(const char *, const char *);
+
+int
+lka_proc_ready(void)
+{
+ void *iter;
+ struct processor_instance *pi;
+
+ iter = NULL;
+ while (dict_iter(&processors, &iter, NULL, (void **)&pi))
+ if (!pi->ready)
+ return 0;
+ return 1;
+}
+
+static void
+lka_proc_config(struct processor_instance *pi)
+{
+ io_printf(pi->io, "config|smtpd-version|%s\n", SMTPD_VERSION);
+ io_printf(pi->io, "config|smtp-session-timeout|%d\n", SMTPD_SESSION_TIMEOUT);
+ if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_IN)
+ io_printf(pi->io, "config|subsystem|smtp-in\n");
+ if (pi->subsystems & FILTER_SUBSYSTEM_SMTP_OUT)
+ io_printf(pi->io, "config|subsystem|smtp-out\n");
+ io_printf(pi->io, "config|ready\n");
+}
+
+void
+lka_proc_forked(const char *name, uint32_t subsystems, int fd)
+{
+ struct processor_instance *processor;
+
+ if (!processors_inited) {
+ dict_init(&processors);
+ processors_inited = 1;
+ }
+
+ processor = xcalloc(1, sizeof *processor);
+ processor->name = xstrdup(name);
+ processor->io = io_new();
+ processor->subsystems = subsystems;
+
+ io_set_nonblocking(fd);
+
+ io_set_fd(processor->io, fd);
+ io_set_callback(processor->io, processor_io, processor->name);
+ dict_xset(&processors, name, processor);
+}
+
+void
+lka_proc_errfd(const char *name, int fd)
+{
+ struct processor_instance *processor;
+
+ processor = dict_xget(&processors, name);
+
+ io_set_nonblocking(fd);
+
+ processor->errfd = io_new();
+ io_set_fd(processor->errfd, fd);
+ io_set_callback(processor->errfd, processor_errfd, processor->name);
+
+ lka_proc_config(processor);
+}
+
+struct io *
+lka_proc_get_io(const char *name)
+{
+ struct processor_instance *processor;
+
+ processor = dict_xget(&processors, name);
+
+ return processor->io;
+}
+
+static void
+processor_register(const char *name, const char *line)
+{
+ struct processor_instance *processor;
+
+ processor = dict_xget(&processors, name);
+
+ if (strcmp(line, "register|ready") == 0) {
+ processor->ready = 1;
+ return;
+ }
+
+ if (strncmp(line, "register|report|", 16) == 0) {
+ lka_report_register_hook(name, line+16);
+ return;
+ }
+
+ if (strncmp(line, "register|filter|", 16) == 0) {
+ lka_filter_register_hook(name, line+16);
+ return;
+ }
+
+ fatalx("Invalid register line received: %s", line);
+}
+
+static void
+processor_io(struct io *io, int evt, void *arg)
+{
+ struct processor_instance *processor;
+ const char *name = arg;
+ char *line = NULL;
+ ssize_t len;
+
+ switch (evt) {
+ case IO_DATAIN:
+ while ((line = io_getline(io, &len)) != NULL) {
+ if (strncmp("register|", line, 9) == 0) {
+ processor_register(name, line);
+ continue;
+ }
+
+ processor = dict_xget(&processors, name);
+ if (!processor->ready)
+ fatalx("Non-register message before register|"
+ "ready: %s", line);
+ else if (strncmp(line, "filter-result|", 14) == 0 ||
+ strncmp(line, "filter-dataline|", 16) == 0)
+ lka_filter_process_response(name, line);
+ else if (strncmp(line, "report|", 7) == 0)
+ lka_report_proc(name, line);
+ else
+ fatalx("Invalid filter message type: %s", line);
+ }
+ }
+}
+
+static void
+processor_errfd(struct io *io, int evt, void *arg)
+{
+ const char *name = arg;
+ char *line = NULL;
+ ssize_t len;
+
+ switch (evt) {
+ case IO_DATAIN:
+ while ((line = io_getline(io, &len)) != NULL)
+ log_warnx("%s: %s", name, line);
+ }
+}
+
void
lka_filter_init(void)
{
@@ -215,7 +410,7 @@ lka_filter_register_hook(const char *name, const char *hook)
size_t i;
if (strncasecmp(hook, "smtp-in|", 8) == 0) {
- subsystem = &smtp_in;
+ subsystem = &filter_smtp_in;
hook += 8;
}
else
@@ -292,7 +487,7 @@ lka_filter_proc_in_session(uint64_t reqid, const char *proc)
return 0;
filter = dict_get(&filters, fs->filter_name);
- if (filter->proc == NULL && filter->chain == NULL)
+ if (filter == NULL || (filter->proc == NULL && filter->chain == NULL))
return 0;
if (filter->proc)
@@ -307,27 +502,18 @@ lka_filter_proc_in_session(uint64_t reqid, const char *proc)
}
void
-lka_filter_begin(uint64_t reqid,
- const char *filter_name,
- const struct sockaddr_storage *ss_src,
- const struct sockaddr_storage *ss_dest,
- const char *rdns,
- int fcrdns)
+lka_filter_begin(uint64_t reqid, const char *filter_name)
{
struct filter_session *fs;
- if (!inited) {
+ if (!filters_inited) {
tree_init(&sessions);
- inited = 1;
+ filters_inited = 1;
}
fs = xcalloc(1, sizeof (struct filter_session));
fs->id = reqid;
fs->filter_name = xstrdup(filter_name);
- fs->ss_src = *ss_src;
- fs->ss_dest = *ss_dest;
- fs->rdns = xstrdup(rdns);
- fs->fcrdns = fcrdns;
tree_xset(&sessions, fs->id, fs);
log_trace(TRACE_FILTERS, "%016"PRIx64" filters session-begin", reqid);
@@ -441,22 +627,22 @@ lka_filter_process_response(const char *name, const char *line)
fatalx("Missing reqid: %s", line);
ep[0] = '\0';
- token = strtoull(qid, &ep, 16);
+ reqid = strtoull(qid, &ep, 16);
if (qid[0] == '\0' || *ep != '\0')
- fatalx("Invalid token: %s", line);
- if (errno == ERANGE && token == ULLONG_MAX)
- fatal("Invalid token: %s", line);
+ fatalx("Invalid reqid: %s", line);
+ if (errno == ERANGE && reqid == ULLONG_MAX)
+ fatal("Invalid reqid: %s", line);
qid = ep+1;
if ((ep = strchr(qid, '|')) == NULL)
fatal("Missing directive: %s", line);
ep[0] = '\0';
- reqid = strtoull(qid, &ep, 16);
+ token = strtoull(qid, &ep, 16);
if (qid[0] == '\0' || *ep != '\0')
- fatalx("Invalid reqid: %s", line);
- if (errno == ERANGE && reqid == ULLONG_MAX)
- fatal("Invalid reqid: %s", line);
+ fatalx("Invalid token: %s", line);
+ if (errno == ERANGE && token == ULLONG_MAX)
+ fatal("Invalid token: %s", line);
response = ep+1;
@@ -604,6 +790,14 @@ filter_protocol_internal(struct filter_session *fs, uint64_t *token, uint64_t re
gettimeofday(&tv, NULL);
lka_report_filter_report(fs->id, filter->name, 1,
"smtp-in", &tv, filter->config->report);
+ } else if (filter->config->bypass) {
+ log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
+ "resume=%s, action=bypass, filter=%s, query=%s",
+ fs->id, phase_name, resume ? "y" : "n",
+ filter->name,
+ param);
+ filter_result_proceed(reqid);
+ return;
} else {
log_trace(TRACE_FILTERS, "%016"PRIx64" filters protocol phase=%s, "
"resume=%s, action=reject, filter=%s, query=%s, response=%s",
@@ -1044,3 +1238,441 @@ filter_builtins_rcpt_to(struct filter_session *fs, struct filter *filter, uint64
filter_check_rcpt_to_table(filter, K_MAILADDR, param) ||
filter_check_rcpt_to_regex(filter, param);
}
+
+static void
+report_smtp_broadcast(uint64_t, const char *, struct timeval *, const char *,
+ const char *, ...) __attribute__((__format__ (printf, 5, 6)));
+
+void
+lka_report_init(void)
+{
+ struct reporters *tailq;
+ size_t i;
+
+ dict_init(&report_smtp_in);
+ dict_init(&report_smtp_out);
+
+ for (i = 0; i < nitems(smtp_events); ++i) {
+ tailq = xcalloc(1, sizeof (struct reporters));
+ TAILQ_INIT(tailq);
+ dict_xset(&report_smtp_in, smtp_events[i].event, tailq);
+
+ tailq = xcalloc(1, sizeof (struct reporters));
+ TAILQ_INIT(tailq);
+ dict_xset(&report_smtp_out, smtp_events[i].event, tailq);
+ }
+}
+
+void
+lka_report_register_hook(const char *name, const char *hook)
+{
+ struct dict *subsystem;
+ struct reporter_proc *rp;
+ struct reporters *tailq;
+ void *iter;
+ size_t i;
+
+ if (strncmp(hook, "smtp-in|", 8) == 0) {
+ subsystem = &report_smtp_in;
+ hook += 8;
+ }
+ else if (strncmp(hook, "smtp-out|", 9) == 0) {
+ subsystem = &report_smtp_out;
+ hook += 9;
+ }
+ else
+ fatalx("Invalid message direction: %s", hook);
+
+ if (strcmp(hook, "*") == 0) {
+ iter = NULL;
+ while (dict_iter(subsystem, &iter, NULL, (void **)&tailq)) {
+ rp = xcalloc(1, sizeof *rp);
+ rp->name = xstrdup(name);
+ TAILQ_INSERT_TAIL(tailq, rp, entries);
+ }
+ return;
+ }
+
+ for (i = 0; i < nitems(smtp_events); i++)
+ if (strcmp(hook, smtp_events[i].event) == 0)
+ break;
+ if (i == nitems(smtp_events))
+ fatalx("Unrecognized report name: %s", hook);
+
+ tailq = dict_get(subsystem, hook);
+ rp = xcalloc(1, sizeof *rp);
+ rp->name = xstrdup(name);
+ TAILQ_INSERT_TAIL(tailq, rp, entries);
+}
+
+static void
+report_smtp_broadcast(uint64_t reqid, const char *direction, struct timeval *tv, const char *event,
+ const char *format, ...)
+{
+ va_list ap;
+ struct dict *d;
+ struct reporters *tailq;
+ struct reporter_proc *rp;
+
+ if (strcmp("smtp-in", direction) == 0)
+ d = &report_smtp_in;
+
+ else if (strcmp("smtp-out", direction) == 0)
+ d = &report_smtp_out;
+
+ else
+ fatalx("unexpected direction: %s", direction);
+
+ tailq = dict_xget(d, event);
+ TAILQ_FOREACH(rp, tailq, entries) {
+ if (!lka_filter_proc_in_session(reqid, rp->name))
+ continue;
+
+ va_start(ap, format);
+ if (io_printf(lka_proc_get_io(rp->name),
+ "report|%s|%lld.%06ld|%s|%s|%016"PRIx64"%s",
+ PROTOCOL_VERSION, tv->tv_sec, tv->tv_usec, direction,
+ event, reqid, format[0] != '\n' ? "|" : "") == -1 ||
+ io_vprintf(lka_proc_get_io(rp->name), format, ap) == -1)
+ fatalx("failed to write to processor");
+ va_end(ap);
+ }
+}
+
+void
+lka_report_smtp_link_connect(const char *direction, struct timeval *tv, uint64_t reqid, const char *rdns,
+ int fcrdns,
+ const struct sockaddr_storage *ss_src,
+ const struct sockaddr_storage *ss_dest)
+{
+ struct filter_session *fs;
+ char src[NI_MAXHOST + 5];
+ char dest[NI_MAXHOST + 5];
+ uint16_t src_port = 0;
+ uint16_t dest_port = 0;
+ const char *fcrdns_str;
+
+ if (ss_src->ss_family == AF_INET)
+ src_port = ntohs(((const struct sockaddr_in *)ss_src)->sin_port);
+ else if (ss_src->ss_family == AF_INET6)
+ src_port = ntohs(((const struct sockaddr_in6 *)ss_src)->sin6_port);
+
+ if (ss_dest->ss_family == AF_INET)
+ dest_port = ntohs(((const struct sockaddr_in *)ss_dest)->sin_port);
+ else if (ss_dest->ss_family == AF_INET6)
+ dest_port = ntohs(((const struct sockaddr_in6 *)ss_dest)->sin6_port);
+
+ if (strcmp(ss_to_text(ss_src), "local") == 0) {
+ (void)snprintf(src, sizeof src, "unix:%s", SMTPD_SOCKET);
+ (void)snprintf(dest, sizeof dest, "unix:%s", SMTPD_SOCKET);
+ } else {
+ (void)snprintf(src, sizeof src, "%s:%d", ss_to_text(ss_src), src_port);
+ (void)snprintf(dest, sizeof dest, "%s:%d", ss_to_text(ss_dest), dest_port);
+ }
+
+ switch (fcrdns) {
+ case 1:
+ fcrdns_str = "pass";
+ break;
+ case 0:
+ fcrdns_str = "fail";
+ break;
+ default:
+ fcrdns_str = "error";
+ break;
+ }
+
+ fs = tree_xget(&sessions, reqid);
+ fs->rdns = xstrdup(rdns);
+ fs->fcrdns = fcrdns;
+ fs->ss_src = *ss_src;
+ fs->ss_dest = *ss_dest;
+
+ report_smtp_broadcast(reqid, direction, tv, "link-connect",
+ "%s|%s|%s|%s\n", rdns, fcrdns_str, src, dest);
+}
+
+void
+lka_report_smtp_link_disconnect(const char *direction, struct timeval *tv, uint64_t reqid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "link-disconnect", "\n");
+}
+
+void
+lka_report_smtp_link_greeting(const char *direction, uint64_t reqid,
+ struct timeval *tv, const char *domain)
+{
+ report_smtp_broadcast(reqid, direction, tv, "link-greeting", "%s\n",
+ domain);
+}
+
+void
+lka_report_smtp_link_auth(const char *direction, struct timeval *tv, uint64_t reqid,
+ const char *username, const char *result)
+{
+ report_smtp_broadcast(reqid, direction, tv, "link-auth", "%s|%s\n",
+ username, result);
+}
+
+void
+lka_report_smtp_link_identify(const char *direction, struct timeval *tv,
+ uint64_t reqid, const char *method, const char *heloname)
+{
+ report_smtp_broadcast(reqid, direction, tv, "link-identify", "%s|%s\n",
+ method, heloname);
+}
+
+void
+lka_report_smtp_link_tls(const char *direction, struct timeval *tv, uint64_t reqid, const char *ciphers)
+{
+ report_smtp_broadcast(reqid, direction, tv, "link-tls", "%s\n",
+ ciphers);
+}
+
+void
+lka_report_smtp_tx_reset(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "tx-reset", "%08x\n",
+ msgid);
+}
+
+void
+lka_report_smtp_tx_begin(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "tx-begin", "%08x\n",
+ msgid);
+}
+
+void
+lka_report_smtp_tx_mail(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
+{
+ const char *result;
+
+ switch (ok) {
+ case 1:
+ result = "ok";
+ break;
+ case 0:
+ result = "permfail";
+ break;
+ default:
+ result = "tempfail";
+ break;
+ }
+ report_smtp_broadcast(reqid, direction, tv, "tx-mail", "%08x|%s|%s\n",
+ msgid, address, result);
+}
+
+void
+lka_report_smtp_tx_rcpt(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
+{
+ const char *result;
+
+ switch (ok) {
+ case 1:
+ result = "ok";
+ break;
+ case 0:
+ result = "permfail";
+ break;
+ default:
+ result = "tempfail";
+ break;
+ }
+ report_smtp_broadcast(reqid, direction, tv, "tx-rcpt", "%08x|%s|%s\n",
+ msgid, address, result);
+}
+
+void
+lka_report_smtp_tx_envelope(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, uint64_t evpid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "tx-envelope",
+ "%08x|%016"PRIx64"\n", msgid, evpid);
+}
+
+void
+lka_report_smtp_tx_data(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, int ok)
+{
+ const char *result;
+
+ switch (ok) {
+ case 1:
+ result = "ok";
+ break;
+ case 0:
+ result = "permfail";
+ break;
+ default:
+ result = "tempfail";
+ break;
+ }
+ report_smtp_broadcast(reqid, direction, tv, "tx-data", "%08x|%s\n",
+ msgid, result);
+}
+
+void
+lka_report_smtp_tx_commit(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, size_t msgsz)
+{
+ report_smtp_broadcast(reqid, direction, tv, "tx-commit", "%08x|%zd\n",
+ msgid, msgsz);
+}
+
+void
+lka_report_smtp_tx_rollback(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "tx-rollback", "%08x\n",
+ msgid);
+}
+
+void
+lka_report_smtp_protocol_client(const char *direction, struct timeval *tv, uint64_t reqid, const char *command)
+{
+ report_smtp_broadcast(reqid, direction, tv, "protocol-client", "%s\n",
+ command);
+}
+
+void
+lka_report_smtp_protocol_server(const char *direction, struct timeval *tv, uint64_t reqid, const char *response)
+{
+ report_smtp_broadcast(reqid, direction, tv, "protocol-server", "%s\n",
+ response);
+}
+
+void
+lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint64_t reqid,
+ int phase, int response, const char *param)
+{
+ const char *phase_name;
+ const char *response_name;
+
+ switch (phase) {
+ case FILTER_CONNECT:
+ phase_name = "connected";
+ break;
+ case FILTER_HELO:
+ phase_name = "helo";
+ break;
+ case FILTER_EHLO:
+ phase_name = "ehlo";
+ break;
+ case FILTER_STARTTLS:
+ phase_name = "tls";
+ break;
+ case FILTER_AUTH:
+ phase_name = "auth";
+ break;
+ case FILTER_MAIL_FROM:
+ phase_name = "mail-from";
+ break;
+ case FILTER_RCPT_TO:
+ phase_name = "rcpt-to";
+ break;
+ case FILTER_DATA:
+ phase_name = "data";
+ break;
+ case FILTER_DATA_LINE:
+ phase_name = "data-line";
+ break;
+ case FILTER_RSET:
+ phase_name = "rset";
+ break;
+ case FILTER_QUIT:
+ phase_name = "quit";
+ break;
+ case FILTER_NOOP:
+ phase_name = "noop";
+ break;
+ case FILTER_HELP:
+ phase_name = "help";
+ break;
+ case FILTER_WIZ:
+ phase_name = "wiz";
+ break;
+ case FILTER_COMMIT:
+ phase_name = "commit";
+ break;
+ default:
+ phase_name = "";
+ }
+
+ switch (response) {
+ case FILTER_PROCEED:
+ response_name = "proceed";
+ break;
+ case FILTER_JUNK:
+ response_name = "junk";
+ break;
+ case FILTER_REWRITE:
+ response_name = "rewrite";
+ break;
+ case FILTER_REJECT:
+ response_name = "reject";
+ break;
+ case FILTER_DISCONNECT:
+ response_name = "disconnect";
+ break;
+ default:
+ response_name = "";
+ }
+
+ report_smtp_broadcast(reqid, direction, tv, "filter-response",
+ "%s|%s%s%s\n", phase_name, response_name, param ? "|" : "",
+ param ? param : "");
+}
+
+void
+lka_report_smtp_timeout(const char *direction, struct timeval *tv, uint64_t reqid)
+{
+ report_smtp_broadcast(reqid, direction, tv, "timeout", "\n");
+}
+
+void
+lka_report_filter_report(uint64_t reqid, const char *name, int builtin,
+ const char *direction, struct timeval *tv, const char *message)
+{
+ report_smtp_broadcast(reqid, direction, tv, "filter-report",
+ "%s|%s|%s\n", builtin ? "builtin" : "proc",
+ name, message);
+}
+
+void
+lka_report_proc(const char *name, const char *line)
+{
+ char buffer[LINE_MAX];
+ struct timeval tv;
+ char *ep, *sp, *direction;
+ uint64_t reqid;
+
+ if (strlcpy(buffer, line + 7, sizeof(buffer)) >= sizeof(buffer))
+ fatalx("Invalid report: line too long: %s", line);
+
+ errno = 0;
+ tv.tv_sec = strtoll(buffer, &ep, 10);
+ if (ep[0] != '.' || errno != 0)
+ fatalx("Invalid report: invalid time: %s", line);
+ sp = ep + 1;
+ tv.tv_usec = strtol(sp, &ep, 10);
+ if (ep[0] != '|' || errno != 0)
+ fatalx("Invalid report: invalid time: %s", line);
+ if (ep - sp != 6)
+ fatalx("Invalid report: invalid time: %s", line);
+
+ direction = ep + 1;
+ if (strncmp(direction, "smtp-in|", 8) == 0) {
+ direction[7] = '\0';
+ direction += 7;
+#if 0
+ } else if (strncmp(direction, "smtp-out|", 9) == 0) {
+ direction[8] = '\0';
+ direction += 8;
+#endif
+ } else
+ fatalx("Invalid report: invalid direction: %s", line);
+
+ reqid = strtoull(sp, &ep, 16);
+ if (ep[0] != '|' || errno != 0)
+ fatalx("Invalid report: invalid reqid: %s", line);
+ sp = ep + 1;
+
+ lka_report_filter_report(reqid, name, 0, direction, &tv, sp);
+}
diff --git a/smtpd/lka_proc.c b/smtpd/lka_proc.c
deleted file mode 100644
index f90d006f..00000000
--- a/smtpd/lka_proc.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/* $OpenBSD: lka_proc.c,v 1.12 2019/09/30 13:27:12 gilles Exp $ */
-
-/*
- * Copyright (c) 2018 Gilles Chehade <gilles@poolp.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 <netinet/in.h>
-
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-static int inited = 0;
-static struct dict processors;
-
-struct processor_instance {
- char *name;
- struct io *io;
- struct io *errfd;
- int ready;
-};
-
-static void processor_io(struct io *, int, void *);
-static void processor_errfd(struct io *, int, void *);
-void lka_filter_process_response(const char *, const char *);
-
-int
-lka_proc_ready(void)
-{
- void *iter;
- struct processor_instance *pi;
-
- iter = NULL;
- while (dict_iter(&processors, &iter, NULL, (void **)&pi))
- if (!pi->ready)
- return 0;
- return 1;
-}
-
-static void
-lka_proc_config(struct processor_instance *pi)
-{
- io_printf(pi->io, "config|smtpd-version|%s\n", SMTPD_VERSION);
- io_printf(pi->io, "config|smtp-session-timeout|%d\n", SMTPD_SESSION_TIMEOUT);
- io_printf(pi->io, "config|ready\n");
-}
-
-void
-lka_proc_forked(const char *name, int fd)
-{
- struct processor_instance *processor;
-
- if (!inited) {
- dict_init(&processors);
- inited = 1;
- }
-
- processor = xcalloc(1, sizeof *processor);
- processor->name = xstrdup(name);
- processor->io = io_new();
-
- io_set_nonblocking(fd);
-
- io_set_fd(processor->io, fd);
- io_set_callback(processor->io, processor_io, processor->name);
- dict_xset(&processors, name, processor);
-}
-
-void
-lka_proc_errfd(const char *name, int fd)
-{
- struct processor_instance *processor;
-
- processor = dict_xget(&processors, name);
-
- io_set_nonblocking(fd);
-
- processor->errfd = io_new();
- io_set_fd(processor->errfd, fd);
- io_set_callback(processor->errfd, processor_errfd, processor->name);
-
- lka_proc_config(processor);
-}
-
-struct io *
-lka_proc_get_io(const char *name)
-{
- struct processor_instance *processor;
-
- processor = dict_xget(&processors, name);
-
- return processor->io;
-}
-
-static void
-processor_register(const char *name, const char *line)
-{
- struct processor_instance *processor;
-
- processor = dict_xget(&processors, name);
-
- if (strcmp(line, "register|ready") == 0) {
- processor->ready = 1;
- return;
- }
-
- if (strncmp(line, "register|report|", 16) == 0) {
- lka_report_register_hook(name, line+16);
- return;
- }
-
- if (strncmp(line, "register|filter|", 16) == 0) {
- lka_filter_register_hook(name, line+16);
- return;
- }
-
- fatalx("Invalid register line received: %s", line);
-}
-
-static void
-processor_io(struct io *io, int evt, void *arg)
-{
- struct processor_instance *processor;
- const char *name = arg;
- char *line = NULL;
- ssize_t len;
-
- switch (evt) {
- case IO_DATAIN:
- while ((line = io_getline(io, &len)) != NULL) {
- if (strncmp("register|", line, 9) == 0) {
- processor_register(name, line);
- continue;
- }
-
- processor = dict_xget(&processors, name);
- if (!processor->ready)
- fatalx("Non-register message before register|"
- "ready: %s", line);
- else if (strncmp(line, "filter-result|", 14) == 0 ||
- strncmp(line, "filter-dataline|", 16) == 0)
- lka_filter_process_response(name, line);
- else if (strncmp(line, "report|", 7) == 0)
- lka_report_proc(name, line);
- else
- fatalx("Invalid filter message type: %s", line);
- }
- }
-}
-
-static void
-processor_errfd(struct io *io, int evt, void *arg)
-{
- const char *name = arg;
- char *line = NULL;
- ssize_t len;
-
- switch (evt) {
- case IO_DATAIN:
- while ((line = io_getline(io, &len)) != NULL)
- log_warnx("%s: %s", name, line);
- }
-}
diff --git a/smtpd/lka_report.c b/smtpd/lka_report.c
deleted file mode 100644
index 95562e84..00000000
--- a/smtpd/lka_report.c
+++ /dev/null
@@ -1,509 +0,0 @@
-/* $OpenBSD: lka_report.c,v 1.34 2019/10/03 05:42:57 gilles Exp $ */
-
-/*
- * Copyright (c) 2018 Gilles Chehade <gilles@poolp.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 <netinet/in.h>
-
-#include <errno.h>
-#include <event.h>
-#include <imsg.h>
-#include <inttypes.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "smtpd.h"
-#include "log.h"
-
-#define PROTOCOL_VERSION "0.4"
-
-struct reporter_proc {
- TAILQ_ENTRY(reporter_proc) entries;
- const char *name;
-};
-TAILQ_HEAD(reporters, reporter_proc);
-
-static struct dict smtp_in;
-static struct dict smtp_out;
-
-static struct smtp_events {
- const char *event;
-} smtp_events[] = {
- { "link-connect" },
- { "link-disconnect" },
- { "link-greeting" },
- { "link-identify" },
- { "link-tls" },
- { "link-auth" },
-
- { "tx-reset" },
- { "tx-begin" },
- { "tx-mail" },
- { "tx-rcpt" },
- { "tx-envelope" },
- { "tx-data" },
- { "tx-commit" },
- { "tx-rollback" },
-
- { "protocol-client" },
- { "protocol-server" },
-
- { "filter-report" },
- { "filter-response" },
-
- { "timeout" },
-};
-
-static void
-report_smtp_broadcast(uint64_t, const char *, struct timeval *, const char *,
- const char *, ...) __attribute__((__format__ (printf, 5, 6)));
-
-void
-lka_report_init(void)
-{
- struct reporters *tailq;
- size_t i;
-
- dict_init(&smtp_in);
- dict_init(&smtp_out);
-
- for (i = 0; i < nitems(smtp_events); ++i) {
- tailq = xcalloc(1, sizeof (struct reporters));
- TAILQ_INIT(tailq);
- dict_xset(&smtp_in, smtp_events[i].event, tailq);
-
- tailq = xcalloc(1, sizeof (struct reporters));
- TAILQ_INIT(tailq);
- dict_xset(&smtp_out, smtp_events[i].event, tailq);
- }
-}
-
-void
-lka_report_register_hook(const char *name, const char *hook)
-{
- struct dict *subsystem;
- struct reporter_proc *rp;
- struct reporters *tailq;
- void *iter;
- size_t i;
-
- if (strncmp(hook, "smtp-in|", 8) == 0) {
- subsystem = &smtp_in;
- hook += 8;
- }
-#if 0
- /* No smtp-out event has been implemented yet */
- else if (strncmp(hook, "smtp-out|", 9) == 0) {
- subsystem = &smtp_out;
- hook += 9;
- }
-#endif
- else
- fatalx("Invalid message direction: %s", hook);
-
- if (strcmp(hook, "*") == 0) {
- iter = NULL;
- while (dict_iter(subsystem, &iter, NULL, (void **)&tailq)) {
- rp = xcalloc(1, sizeof *rp);
- rp->name = xstrdup(name);
- TAILQ_INSERT_TAIL(tailq, rp, entries);
- }
- return;
- }
-
- for (i = 0; i < nitems(smtp_events); i++)
- if (strcmp(hook, smtp_events[i].event) == 0)
- break;
- if (i == nitems(smtp_events))
- fatalx("Unrecognized report name: %s", hook);
-
- tailq = dict_get(subsystem, hook);
- rp = xcalloc(1, sizeof *rp);
- rp->name = xstrdup(name);
- TAILQ_INSERT_TAIL(tailq, rp, entries);
-}
-
-static void
-report_smtp_broadcast(uint64_t reqid, const char *direction, struct timeval *tv, const char *event,
- const char *format, ...)
-{
- va_list ap;
- struct dict *d;
- struct reporters *tailq;
- struct reporter_proc *rp;
-
- if (strcmp("smtp-in", direction) == 0)
- d = &smtp_in;
-
- else if (strcmp("smtp-out", direction) == 0)
- d = &smtp_out;
-
- else
- fatalx("unexpected direction: %s", direction);
-
- tailq = dict_xget(d, event);
- TAILQ_FOREACH(rp, tailq, entries) {
- if (!lka_filter_proc_in_session(reqid, rp->name))
- continue;
-
- va_start(ap, format);
- if (io_printf(lka_proc_get_io(rp->name),
- "report|%s|%lld.%06ld|%s|%s|%016"PRIx64"%s",
- PROTOCOL_VERSION, tv->tv_sec, tv->tv_usec, direction,
- event, reqid, format[0] != '\n' ? "|" : "") == -1 ||
- io_vprintf(lka_proc_get_io(rp->name), format, ap) == -1)
- fatalx("failed to write to processor");
- va_end(ap);
- }
-}
-
-void
-lka_report_smtp_link_connect(const char *direction, struct timeval *tv, uint64_t reqid, const char *rdns,
- int fcrdns,
- const struct sockaddr_storage *ss_src,
- const struct sockaddr_storage *ss_dest)
-{
- char src[NI_MAXHOST + 5];
- char dest[NI_MAXHOST + 5];
- uint16_t src_port = 0;
- uint16_t dest_port = 0;
- const char *fcrdns_str;
-
- if (ss_src->ss_family == AF_INET)
- src_port = ntohs(((const struct sockaddr_in *)ss_src)->sin_port);
- else if (ss_src->ss_family == AF_INET6)
- src_port = ntohs(((const struct sockaddr_in6 *)ss_src)->sin6_port);
-
- if (ss_dest->ss_family == AF_INET)
- dest_port = ntohs(((const struct sockaddr_in *)ss_dest)->sin_port);
- else if (ss_dest->ss_family == AF_INET6)
- dest_port = ntohs(((const struct sockaddr_in6 *)ss_dest)->sin6_port);
-
- if (strcmp(ss_to_text(ss_src), "local") == 0) {
- (void)snprintf(src, sizeof src, "unix:%s", SMTPD_SOCKET);
- (void)snprintf(dest, sizeof dest, "unix:%s", SMTPD_SOCKET);
- } else {
- (void)snprintf(src, sizeof src, "%s:%d", ss_to_text(ss_src), src_port);
- (void)snprintf(dest, sizeof dest, "%s:%d", ss_to_text(ss_dest), dest_port);
- }
-
- switch (fcrdns) {
- case 1:
- fcrdns_str = "pass";
- break;
- case 0:
- fcrdns_str = "fail";
- break;
- default:
- fcrdns_str = "error";
- break;
- }
-
- report_smtp_broadcast(reqid, direction, tv, "link-connect",
- "%s|%s|%s|%s\n", rdns, fcrdns_str, src, dest);
-}
-
-void
-lka_report_smtp_link_disconnect(const char *direction, struct timeval *tv, uint64_t reqid)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-disconnect", "\n");
-}
-
-void
-lka_report_smtp_link_greeting(const char *direction, uint64_t reqid,
- struct timeval *tv, const char *domain)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-greeting", "%s\n",
- domain);
-}
-
-void
-lka_report_smtp_link_auth(const char *direction, struct timeval *tv, uint64_t reqid,
- const char *username, const char *result)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-auth", "%s|%s\n",
- username, result);
-}
-
-void
-lka_report_smtp_link_identify(const char *direction, struct timeval *tv,
- uint64_t reqid, const char *method, const char *heloname)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-identify", "%s|%s\n",
- method, heloname);
-}
-
-void
-lka_report_smtp_link_tls(const char *direction, struct timeval *tv, uint64_t reqid, const char *ciphers)
-{
- report_smtp_broadcast(reqid, direction, tv, "link-tls", "%s\n",
- ciphers);
-}
-
-void
-lka_report_smtp_tx_reset(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-reset", "%08x\n",
- msgid);
-}
-
-void
-lka_report_smtp_tx_begin(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-begin", "%08x\n",
- msgid);
-}
-
-void
-lka_report_smtp_tx_mail(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
-{
- const char *result;
-
- switch (ok) {
- case 1:
- result = "ok";
- break;
- case 0:
- result = "permfail";
- break;
- default:
- result = "tempfail";
- break;
- }
- report_smtp_broadcast(reqid, direction, tv, "tx-mail", "%08x|%s|%s\n",
- msgid, address, result);
-}
-
-void
-lka_report_smtp_tx_rcpt(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, const char *address, int ok)
-{
- const char *result;
-
- switch (ok) {
- case 1:
- result = "ok";
- break;
- case 0:
- result = "permfail";
- break;
- default:
- result = "tempfail";
- break;
- }
- report_smtp_broadcast(reqid, direction, tv, "tx-rcpt", "%08x|%s|%s\n",
- msgid, address, result);
-}
-
-void
-lka_report_smtp_tx_envelope(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, uint64_t evpid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-envelope",
- "%08x|%016"PRIx64"\n", msgid, evpid);
-}
-
-void
-lka_report_smtp_tx_data(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, int ok)
-{
- const char *result;
-
- switch (ok) {
- case 1:
- result = "ok";
- break;
- case 0:
- result = "permfail";
- break;
- default:
- result = "tempfail";
- break;
- }
- report_smtp_broadcast(reqid, direction, tv, "tx-data", "%08x|%s\n",
- msgid, result);
-}
-
-void
-lka_report_smtp_tx_commit(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid, size_t msgsz)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-commit", "%08x|%zd\n",
- msgid, msgsz);
-}
-
-void
-lka_report_smtp_tx_rollback(const char *direction, struct timeval *tv, uint64_t reqid, uint32_t msgid)
-{
- report_smtp_broadcast(reqid, direction, tv, "tx-rollback", "%08x\n",
- msgid);
-}
-
-void
-lka_report_smtp_protocol_client(const char *direction, struct timeval *tv, uint64_t reqid, const char *command)
-{
- report_smtp_broadcast(reqid, direction, tv, "protocol-client", "%s\n",
- command);
-}
-
-void
-lka_report_smtp_protocol_server(const char *direction, struct timeval *tv, uint64_t reqid, const char *response)
-{
- report_smtp_broadcast(reqid, direction, tv, "protocol-server", "%s\n",
- response);
-}
-
-void
-lka_report_smtp_filter_response(const char *direction, struct timeval *tv, uint64_t reqid,
- int phase, int response, const char *param)
-{
- const char *phase_name;
- const char *response_name;
-
- switch (phase) {
- case FILTER_CONNECT:
- phase_name = "connected";
- break;
- case FILTER_HELO:
- phase_name = "helo";
- break;
- case FILTER_EHLO:
- phase_name = "ehlo";
- break;
- case FILTER_STARTTLS:
- phase_name = "tls";
- break;
- case FILTER_AUTH:
- phase_name = "auth";
- break;
- case FILTER_MAIL_FROM:
- phase_name = "mail-from";
- break;
- case FILTER_RCPT_TO:
- phase_name = "rcpt-to";
- break;
- case FILTER_DATA:
- phase_name = "data";
- break;
- case FILTER_DATA_LINE:
- phase_name = "data-line";
- break;
- case FILTER_RSET:
- phase_name = "rset";
- break;
- case FILTER_QUIT:
- phase_name = "quit";
- break;
- case FILTER_NOOP:
- phase_name = "noop";
- break;
- case FILTER_HELP:
- phase_name = "help";
- break;
- case FILTER_WIZ:
- phase_name = "wiz";
- break;
- case FILTER_COMMIT:
- phase_name = "commit";
- break;
- default:
- phase_name = "";
- }
-
- switch (response) {
- case FILTER_PROCEED:
- response_name = "proceed";
- break;
- case FILTER_JUNK:
- response_name = "junk";
- break;
- case FILTER_REWRITE:
- response_name = "rewrite";
- break;
- case FILTER_REJECT:
- response_name = "reject";
- break;
- case FILTER_DISCONNECT:
- response_name = "disconnect";
- break;
- default:
- response_name = "";
- }
-
- report_smtp_broadcast(reqid, direction, tv, "filter-response",
- "%s|%s%s%s\n", phase_name, response_name, param ? "|" : "",
- param ? param : "");
-}
-
-void
-lka_report_smtp_timeout(const char *direction, struct timeval *tv, uint64_t reqid)
-{
- report_smtp_broadcast(reqid, direction, tv, "timeout", "\n");
-}
-
-void
-lka_report_filter_report(uint64_t reqid, const char *name, int builtin,
- const char *direction, struct timeval *tv, const char *message)
-{
- report_smtp_broadcast(reqid, direction, tv, "filter-report",
- "%s|%s|%s\n", builtin ? "builtin" : "proc",
- name, message);
-}
-
-void
-lka_report_proc(const char *name, const char *line)
-{
- char buffer[LINE_MAX];
- struct timeval tv;
- char *ep, *sp, *direction;
- uint64_t reqid;
-
- if (strlcpy(buffer, line + 7, sizeof(buffer)) >= sizeof(buffer))
- fatalx("Invalid report: line too long: %s", line);
-
- errno = 0;
- tv.tv_sec = strtoll(buffer, &ep, 10);
- if (ep[0] != '.' || errno != 0)
- fatalx("Invalid report: invalid time: %s", line);
- sp = ep + 1;
- tv.tv_usec = strtol(sp, &ep, 10);
- if (ep[0] != '|' || errno != 0)
- fatalx("Invalid report: invalid time: %s", line);
- if (ep - sp != 6)
- fatalx("Invalid report: invalid time: %s", line);
-
- direction = ep + 1;
- if (strncmp(direction, "smtp-in|", 8) == 0) {
- direction[7] = '\0';
- direction += 7;
-#if 0
- } else if (strncmp(direction, "smtp-out|", 9) == 0) {
- direction[8] = '\0';
- direction += 8;
-#endif
- } else
- fatalx("Invalid report: invalid direction: %s", line);
-
- reqid = strtoull(sp, &ep, 16);
- if (ep[0] != '|' || errno != 0)
- fatalx("Invalid report: invalid reqid: %s", line);
- sp = ep + 1;
-
- lka_report_filter_report(reqid, name, 0, direction, &tv, sp);
-}
diff --git a/smtpd/mail.lmtp.c b/smtpd/mail.lmtp.c
index c12561cf..94d19155 100644
--- a/smtpd/mail.lmtp.c
+++ b/smtpd/mail.lmtp.c
@@ -46,15 +46,15 @@ struct session {
int n_rcpts;
};
-static FILE *lmtp_connect(const char *);
-static void lmtp_engine(FILE *, struct session *);
+static int lmtp_connect(const char *);
+static void lmtp_engine(int, struct session *);
static void stream_file(FILE *);
int
main(int argc, char *argv[])
{
int ch;
- FILE *conn;
+ int conn;
const char *destination = "localhost";
struct session session;
@@ -97,7 +97,7 @@ main(int argc, char *argv[])
return (0);
}
-static FILE *
+static int
lmtp_connect_inet(const char *destination)
{
struct addrinfo hints, *res, *res0;
@@ -171,10 +171,10 @@ lmtp_connect_inet(const char *destination)
errx(EX_TEMPFAIL, "%s", cause);
free(destcopy);
- return fdopen(s, "r+");
+ return s;
}
-static FILE *
+static int
lmtp_connect_unix(const char *destination)
{
struct sockaddr_un addr;
@@ -195,10 +195,10 @@ lmtp_connect_unix(const char *destination)
if (connect(s, (struct sockaddr *)&addr, sizeof addr) == -1)
err(EX_TEMPFAIL, "connect");
- return fdopen(s, "r+");
+ return s;
}
-static FILE *
+static int
lmtp_connect(const char *destination)
{
if (destination[0] == '/')
@@ -207,17 +207,30 @@ lmtp_connect(const char *destination)
}
static void
-lmtp_engine(FILE *conn, struct session *session)
+lmtp_engine(int fd_read, struct session *session)
{
+ int fd_write = 0;
+ FILE *file_read = 0;
+ FILE *file_write = 0;
char *line = NULL;
size_t linesize = 0;
ssize_t linelen;
enum phase phase = PHASE_BANNER;
+ if ((fd_write = dup(fd_read)) == -1)
+ err(EX_TEMPFAIL, "dup");
+
+ if ((file_read = fdopen(fd_read, "r")) == NULL)
+ err(EX_TEMPFAIL, "fdopen");
+
+ if ((file_write = fdopen(fd_write, "w")) == NULL)
+ err(EX_TEMPFAIL, "fdopen");
+
do {
- fflush(conn);
- if ((linelen = getline(&line, &linesize, conn)) == -1) {
- if (ferror(conn))
+ fflush(file_write);
+
+ if ((linelen = getline(&line, &linesize, file_read)) == -1) {
+ if (ferror(file_read))
err(EX_TEMPFAIL, "getline");
else
errx(EX_TEMPFAIL, "unexpected EOF from LMTP server");
@@ -241,17 +254,17 @@ lmtp_engine(FILE *conn, struct session *session)
switch (phase) {
case PHASE_BANNER:
- fprintf(conn, "LHLO %s\r\n", session->lhlo);
+ fprintf(file_write, "LHLO %s\r\n", session->lhlo);
phase++;
break;
case PHASE_HELO:
- fprintf(conn, "MAIL FROM:<%s>\r\n", session->mailfrom);
+ fprintf(file_write, "MAIL FROM:<%s>\r\n", session->mailfrom);
phase++;
break;
case PHASE_MAILFROM:
- fprintf(conn, "RCPT TO:<%s>\r\n", session->rcpts[session->n_rcpts - 1]);
+ fprintf(file_write, "RCPT TO:<%s>\r\n", session->rcpts[session->n_rcpts - 1]);
if (session->n_rcpts - 1 == 0) {
phase++;
break;
@@ -260,18 +273,18 @@ lmtp_engine(FILE *conn, struct session *session)
break;
case PHASE_RCPTTO:
- fprintf(conn, "DATA\r\n");
+ fprintf(file_write, "DATA\r\n");
phase++;
break;
case PHASE_DATA:
- stream_file(conn);
- fprintf(conn, ".\r\n");
+ stream_file(file_write);
+ fprintf(file_write, ".\r\n");
phase++;
break;
case PHASE_EOM:
- fprintf(conn, "QUIT\r\n");
+ fprintf(file_write, "QUIT\r\n");
phase++;
break;
diff --git a/smtpd/mail.mboxfile.c b/smtpd/mail.mboxfile.c
index 96fa2e02..c466e566 100644
--- a/smtpd/mail.mboxfile.c
+++ b/smtpd/mail.mboxfile.c
@@ -94,7 +94,7 @@ mboxfile_engine(const char *sender, const char *filename)
if (fflush(fp) == EOF ||
ferror(fp) ||
- fsync(fd) == -1 ||
+ (fsync(fd) == -1 && errno != EINVAL) ||
fclose(fp) == EOF)
err(EX_TEMPFAIL, NULL);
}
diff --git a/smtpd/mta.c b/smtpd/mta.c
index 6c817d00..ea79e2dc 100644
--- a/smtpd/mta.c
+++ b/smtpd/mta.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mta.c,v 1.232 2019/09/20 17:46:05 gilles Exp $ */
+/* $OpenBSD: mta.c,v 1.234 2019/12/21 10:34:07 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -926,6 +926,10 @@ mta_query_smarthost(struct envelope *evp0)
m_create(p_lka, IMSG_MTA_LOOKUP_SMARTHOST, 0, 0, -1);
m_add_id(p_lka, evp->id);
+ if (dispatcher->u.remote.smarthost_domain)
+ m_add_string(p_lka, evp->dest.domain);
+ else
+ m_add_string(p_lka, NULL);
m_add_string(p_lka, dispatcher->u.remote.smarthost);
m_close(p_lka);
@@ -1764,6 +1768,7 @@ mta_relay(struct envelope *e, struct relayhost *relayh)
r = xcalloc(1, sizeof *r);
TAILQ_INIT(&r->tasks);
r->id = generate_uid();
+ r->dispatcher = dispatcher;
r->tls = key.tls;
r->flags = key.flags;
r->domain = key.domain;
diff --git a/smtpd/mta_session.c b/smtpd/mta_session.c
index 31e95a83..c951f60e 100644
--- a/smtpd/mta_session.c
+++ b/smtpd/mta_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mta_session.c,v 1.122 2019/09/20 17:46:05 gilles Exp $ */
+/* $OpenBSD: mta_session.c,v 1.125 2019/12/21 17:43:49 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -129,6 +129,7 @@ struct mta_session {
struct mta_task *task;
struct mta_envelope *currevp;
FILE *datafp;
+ size_t datalen;
size_t failures;
@@ -164,6 +165,38 @@ 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 *);
+#if 0
+static void mta_report_link_auth(struct mta_session *, const char *, const char *);
+#endif
+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;
@@ -172,6 +205,9 @@ 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)
{
@@ -202,6 +238,8 @@ mta_session(struct mta_relay *relay, struct mta_route *route, const char *mxname
s->route = route;
s->mxname = xstrdup(mxname);
+ mta_filter_begin(s);
+
if (relay->flags & RELAY_LMTP)
s->flags |= MTA_LMTP;
switch (relay->tls) {
@@ -350,6 +388,8 @@ mta_free(struct mta_session *s)
log_debug("debug: mta: %p: session done", s);
+ mta_disconnected(s);
+
if (s->ready)
s->relay->nconn_ready -= 1;
@@ -363,8 +403,10 @@ mta_free(struct mta_session *s)
if (s->task)
fatalx("current task should have been deleted already");
- if (s->datafp)
+ if (s->datafp) {
fclose(s->datafp);
+ s->datalen = 0;
+ }
free(s->helo);
relay = s->relay;
@@ -557,16 +599,19 @@ again:
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:
@@ -819,6 +864,7 @@ again:
if (s->datafp) {
fclose(s->datafp);
s->datafp = NULL;
+ s->datalen = 0;
}
mta_send(s, "RSET");
break;
@@ -843,6 +889,7 @@ mta_response(struct mta_session *s, char *line)
struct sockaddr_storage ss;
struct sockaddr *sa;
const char *domain;
+ char *pbuf;
socklen_t sa_len;
char buf[LINE_MAX];
int delivery;
@@ -855,6 +902,16 @@ mta_response(struct mta_session *s, char *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
@@ -954,10 +1011,16 @@ mta_response(struct mta_session *s, char *line)
delivery = IMSG_MTA_DELIVERY_PERMFAIL;
else
delivery = IMSG_MTA_DELIVERY_TEMPFAIL;
+
+ mta_report_tx_mail(s, s->task->msgid, s->task->sender,
+ delivery == IMSG_MTA_DELIVERY_TEMPFAIL ? -1 : 0);
+
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;
@@ -981,6 +1044,8 @@ mta_response(struct mta_session *s, char *line)
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
@@ -1032,6 +1097,23 @@ mta_response(struct mta_session *s, char *line)
}
}
+ switch (line[0]) {
+ case '2':
+ mta_report_tx_rcpt(s,
+ s->task->msgid, e->dest, 1);
+ mta_report_tx_envelope(s,
+ s->task->msgid, e->id);
+ 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
@@ -1040,13 +1122,19 @@ mta_response(struct mta_session *s, char *line)
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;
@@ -1062,6 +1150,14 @@ mta_response(struct mta_session *s, char *line)
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--;
@@ -1083,6 +1179,11 @@ mta_response(struct mta_session *s, char *line)
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);
@@ -1115,7 +1216,7 @@ mta_io(struct io *io, int evt, void *arg)
switch (evt) {
case IO_CONNECTED:
- log_info("%016"PRIx64" mta connected", s->id);
+ mta_connected(s);
if (s->use_smtps) {
io_set_write(io);
@@ -1133,6 +1234,8 @@ mta_io(struct io *io, int evt, void *arg)
s->flags |= MTA_TLS;
mta_tls_verified(s);
+ mta_report_link_tls(s,
+ tls_to_text(io_tls(s->io)));
break;
case IO_DATAIN:
@@ -1147,6 +1250,7 @@ mta_io(struct io *io, int evt, void *arg)
}
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);
@@ -1253,6 +1357,7 @@ mta_io(struct io *io, int evt, void *arg)
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
@@ -1327,6 +1432,13 @@ mta_send(struct mta_session *s, char *fmt, ...)
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);
@@ -1349,7 +1461,7 @@ mta_queue_data(struct mta_session *s)
break;
if (ln[len - 1] == '\n')
ln[len - 1] = '\0';
- io_xprintf(s->io, "%s%s\r\n", *ln == '.' ? "." : "", ln);
+ s->datalen += io_xprintf(s->io, "%s%s\r\n", *ln == '.' ? "." : "", ln);
}
free(ln);
@@ -1639,3 +1751,223 @@ mta_strstate(int state)
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);
+}
+
+#if 0
+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);
+}
+#endif
+
+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);
+}
diff --git a/smtpd/parse.y b/smtpd/parse.y
index 6ef82957..f0fa6c8f 100644
--- a/smtpd/parse.y
+++ b/smtpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.263 2019/09/22 11:49:53 semarie Exp $ */
+/* $OpenBSD: parse.y,v 1.272 2019/12/21 11:07:38 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -105,7 +105,7 @@ static struct ca *sca;
struct dispatcher *dispatcher;
struct rule *rule;
-struct processor *processor;
+struct filter_proc *processor;
struct filter_config *filter_config;
static uint32_t last_dynchain_id = 1;
@@ -174,7 +174,7 @@ typedef struct {
%}
%token ACTION ALIAS ANY ARROW AUTH AUTH_OPTIONAL
-%token BACKUP BOUNCE
+%token BACKUP BOUNCE BYPASS
%token CA CERT CHAIN CHROOT CIPHERS COMMIT COMPRESSION CONNECT
%token DATA DATA_LINE DHE DISCONNECT DOMAIN
%token EHLO ENABLE ENCRYPTION ERROR EXPAND_ONLY
@@ -434,7 +434,7 @@ pki_params_opt pki_params
proc:
PROC STRING STRING {
- if (dict_get(conf->sc_processors_dict, $2)) {
+ if (dict_get(conf->sc_filter_processes_dict, $2)) {
yyerror("processor already exists with that name: %s", $2);
free($2);
free($3);
@@ -443,7 +443,7 @@ PROC STRING STRING {
processor = xcalloc(1, sizeof *processor);
processor->command = $3;
} proc_params {
- dict_set(conf->sc_processors_dict, $2, processor);
+ dict_set(conf->sc_filter_processes_dict, $2, processor);
processor = NULL;
}
;
@@ -818,6 +818,27 @@ HELO STRING {
dispatcher->u.remote.smarthost = strdup(t->t_name);
}
+| DOMAIN tables {
+ struct table *t = $2;
+
+ if (dispatcher->u.remote.smarthost) {
+ yyerror("host mapping already specified for this dispatcher");
+ YYERROR;
+ }
+ if (dispatcher->u.remote.backup) {
+ yyerror("backup and domain are mutually exclusive");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_HASH, K_RELAYHOST)) {
+ yyerror("table \"%s\" may not be used for host lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ dispatcher->u.remote.smarthost = strdup(t->t_name);
+ dispatcher->u.remote.smarthost_domain = 1;
+}
| TLS {
if (dispatcher->u.remote.tls_required == 1) {
yyerror("tls already specified for this dispatcher");
@@ -856,6 +877,45 @@ HELO STRING {
dispatcher->u.remote.auth = strdup(t->t_name);
}
+| FILTER STRING {
+ struct filter_config *fc;
+
+ if (dispatcher->u.remote.filtername) {
+ yyerror("filter already specified for this dispatcher");
+ YYERROR;
+ }
+
+ if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
+ yyerror("no filter exist with that name: %s", $2);
+ free($2);
+ YYERROR;
+ }
+ fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT;
+ dispatcher->u.remote.filtername = $2;
+}
+| FILTER {
+ char buffer[128];
+ char *filtername;
+
+ if (dispatcher->u.remote.filtername) {
+ yyerror("filter already specified for this dispatcher");
+ YYERROR;
+ }
+
+ do {
+ (void)snprintf(buffer, sizeof buffer, "<dynchain:%08x>", last_dynchain_id++);
+ } while (dict_check(conf->sc_filters_dict, buffer));
+
+ filtername = xstrdup(buffer);
+ filter_config = xcalloc(1, sizeof *filter_config);
+ filter_config->filter_type = FILTER_TYPE_CHAIN;
+ filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_OUT;
+ dict_init(&filter_config->chain_procs);
+ dispatcher->u.remote.filtername = filtername;
+} '{' filter_list '}' {
+ dict_set(conf->sc_filters_dict, dispatcher->u.remote.filtername, filter_config);
+ filter_config = NULL;
+}
| SRS {
if (conf->sc_srs_key == NULL) {
yyerror("an srs key is required for srs to be specified in an action");
@@ -1021,7 +1081,7 @@ negation TAG REGEX tables {
YYERROR;
}
- if (!table_check_use(t, T_DYNAMIC|T_LIST, K_CREDENTIALS)) {
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) {
yyerror("table \"%s\" may not be used for auth lookups",
t->t_name);
YYERROR;
@@ -1228,6 +1288,101 @@ negation TAG REGEX tables {
rule->table_from = strdup(t->t_name);
}
+| negation FROM AUTH {
+ struct table *anyhost = table_find(conf, "<anyhost>");
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ rule->flag_from = 1;
+ rule->table_from = strdup(anyhost->t_name);
+ rule->flag_smtp_auth = $1 ? -1 : 1;
+}
+| negation FROM AUTH tables {
+ struct table *anyhost = table_find(conf, "<anyhost>");
+ struct table *t = $4;
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_STRING|K_CREDENTIALS)) {
+ yyerror("table \"%s\" may not be used for from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_from = 1;
+ rule->table_from = strdup(anyhost->t_name);
+ rule->flag_smtp_auth = $1 ? -1 : 1;
+ rule->table_smtp_auth = strdup(t->t_name);
+}
+| negation FROM AUTH REGEX tables {
+ struct table *anyhost = table_find(conf, "<anyhost>");
+ struct table *t = $5;
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_from = 1;
+ rule->table_from = strdup(anyhost->t_name);
+ rule->flag_smtp_auth = $1 ? -1 : 1;
+ rule->flag_smtp_auth_regex = 1;
+ rule->table_smtp_auth = strdup(t->t_name);
+}
+
+| negation FROM MAIL_FROM tables {
+ struct table *anyhost = table_find(conf, "<anyhost>");
+ struct table *t = $4;
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
+ yyerror("table \"%s\" may not be used for from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_from = 1;
+ rule->table_from = strdup(anyhost->t_name);
+ rule->flag_smtp_mail_from = $1 ? -1 : 1;
+ rule->table_smtp_mail_from = strdup(t->t_name);
+}
+| negation FROM MAIL_FROM REGEX tables {
+ struct table *anyhost = table_find(conf, "<anyhost>");
+ struct table *t = $5;
+
+ if (rule->flag_from) {
+ yyerror("from already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for from lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_from = 1;
+ rule->table_from = strdup(anyhost->t_name);
+ rule->flag_smtp_mail_from = $1 ? -1 : 1;
+ rule->flag_smtp_mail_from_regex = 1;
+ rule->table_smtp_mail_from = strdup(t->t_name);
+}
| negation FOR LOCAL {
struct table *t = table_find(conf, "<localnames>");
@@ -1284,6 +1439,47 @@ negation TAG REGEX tables {
rule->flag_for_regex = 1;
rule->table_for = strdup(t->t_name);
}
+| negation FOR RCPT_TO tables {
+ struct table *anyhost = table_find(conf, "<anydestination>");
+ struct table *t = $4;
+
+ if (rule->flag_for) {
+ yyerror("for already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_MAILADDR)) {
+ yyerror("table \"%s\" may not be used for for lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_for = 1;
+ rule->table_for = strdup(anyhost->t_name);
+ rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
+ rule->table_smtp_rcpt_to = strdup(t->t_name);
+}
+| negation FOR RCPT_TO REGEX tables {
+ struct table *anyhost = table_find(conf, "<anydestination>");
+ struct table *t = $5;
+
+ if (rule->flag_for) {
+ yyerror("for already specified for this rule");
+ YYERROR;
+ }
+
+ if (!table_check_use(t, T_DYNAMIC|T_LIST, K_REGEX)) {
+ yyerror("table \"%s\" may not be used for for lookups",
+ t->t_name);
+ YYERROR;
+ }
+
+ rule->flag_for = 1;
+ rule->table_for = strdup(anyhost->t_name);
+ rule->flag_smtp_rcpt_to = $1 ? -1 : 1;
+ rule->flag_smtp_rcpt_to_regex = 1;
+ rule->table_smtp_rcpt_to = strdup(t->t_name);
+}
;
match_options:
@@ -1330,6 +1526,9 @@ filter_action_builtin_nojunk
| JUNK {
filter_config->junk = 1;
}
+| BYPASS {
+ filter_config->bypass = 1;
+}
;
filter_action_builtin_nojunk:
@@ -1579,6 +1778,7 @@ filter_phase_connect
filterel:
STRING {
struct filter_config *fr;
+ struct filter_proc *fp;
size_t i;
if ((fr = dict_get(conf->sc_filters_dict, $1)) == NULL) {
@@ -1601,7 +1801,7 @@ STRING {
}
if (fr->proc) {
- if (dict_check(&filter_config->chain_procs, fr->proc)) {
+ if ((fp = dict_get(&filter_config->chain_procs, fr->proc))) {
yyerror("no proc allowed twice within a filter chain: %s", fr->proc);
free($1);
YYERROR;
@@ -1609,6 +1809,7 @@ STRING {
dict_set(&filter_config->chain_procs, fr->proc, NULL);
}
+ fr->filter_subsystem |= filter_config->filter_subsystem;
filter_config->chain_size += 1;
filter_config->chain = reallocarray(filter_config->chain, filter_config->chain_size, sizeof(char *));
if (filter_config->chain == NULL)
@@ -1624,13 +1825,15 @@ filterel
filter:
FILTER STRING PROC STRING {
+ struct filter_proc *fp;
+
if (dict_get(conf->sc_filters_dict, $2)) {
yyerror("filter already exists with that name: %s", $2);
free($2);
free($4);
YYERROR;
}
- if (! dict_get(conf->sc_processors_dict, $4)) {
+ if ((fp = dict_get(conf->sc_filter_processes_dict, $4)) == NULL) {
yyerror("no processor exist with that name: %s", $4);
free($4);
YYERROR;
@@ -1661,7 +1864,7 @@ FILTER STRING PROC_EXEC STRING {
filter_config->proc = xstrdup($2);
dict_set(conf->sc_filters_dict, $2, filter_config);
} proc_params {
- dict_set(conf->sc_processors_dict, filter_config->proc, processor);
+ dict_set(conf->sc_filter_processes_dict, filter_config->proc, processor);
processor = NULL;
filter_config = NULL;
}
@@ -1836,16 +2039,19 @@ limits_scheduler: opt_limit_scheduler limits_scheduler
opt_sock_listen : FILTER STRING {
+ struct filter_config *fc;
+
if (listen_opts.options & LO_FILTER) {
yyerror("filter already specified");
free($2);
YYERROR;
}
- if (dict_get(conf->sc_filters_dict, $2) == NULL) {
+ if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
yyerror("no filter exist with that name: %s", $2);
free($2);
YYERROR;
}
+ fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
listen_opts.options |= LO_FILTER;
listen_opts.filtername = $2;
}
@@ -1865,6 +2071,7 @@ opt_sock_listen : FILTER STRING {
listen_opts.filtername = xstrdup(buffer);
filter_config = xcalloc(1, sizeof *filter_config);
filter_config->filter_type = FILTER_TYPE_CHAIN;
+ filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
dict_init(&filter_config->chain_procs);
} '{' filter_list '}' {
dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config);
@@ -1875,6 +2082,20 @@ opt_sock_listen : FILTER STRING {
YYERROR;
}
}
+ | TAG STRING {
+ if (listen_opts.options & LO_TAG) {
+ yyerror("tag already specified");
+ YYERROR;
+ }
+ listen_opts.options |= LO_TAG;
+
+ if (strlen($2) >= SMTPD_TAG_SIZE) {
+ yyerror("tag name too long");
+ free($2);
+ YYERROR;
+ }
+ listen_opts.tag = $2;
+ }
;
opt_if_listen : INET4 {
@@ -1957,15 +2178,18 @@ opt_if_listen : INET4 {
listen_opts.port = $2;
}
| FILTER STRING {
+ struct filter_config *fc;
+
if (listen_opts.options & LO_FILTER) {
yyerror("filter already specified");
YYERROR;
}
- if (dict_get(conf->sc_filters_dict, $2) == NULL) {
+ if ((fc = dict_get(conf->sc_filters_dict, $2)) == NULL) {
yyerror("no filter exist with that name: %s", $2);
free($2);
YYERROR;
}
+ fc->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
listen_opts.options |= LO_FILTER;
listen_opts.filtername = $2;
}
@@ -1985,6 +2209,7 @@ opt_if_listen : INET4 {
listen_opts.filtername = xstrdup(buffer);
filter_config = xcalloc(1, sizeof *filter_config);
filter_config->filter_type = FILTER_TYPE_CHAIN;
+ filter_config->filter_subsystem |= FILTER_SUBSYSTEM_SMTP_IN;
dict_init(&filter_config->chain_procs);
} '{' filter_list '}' {
dict_set(conf->sc_filters_dict, listen_opts.filtername, filter_config);
@@ -2338,6 +2563,7 @@ lookup(char *s)
{ "auth-optional", AUTH_OPTIONAL },
{ "backup", BACKUP },
{ "bounce", BOUNCE },
+ { "bypass", BYPASS },
{ "ca", CA },
{ "cert", CERT },
{ "chain", CHAIN },
@@ -2914,7 +3140,6 @@ static void
create_sock_listener(struct listen_opts *lo)
{
struct listener *l = xcalloc(1, sizeof(*l));
- lo->tag = "local";
lo->hostname = conf->sc_hostname;
l->ss.ss_family = AF_LOCAL;
l->ss.ss_len = sizeof(struct sockaddr *);
diff --git a/smtpd/ruleset.c b/smtpd/ruleset.c
index 0468ddb3..869d8115 100644
--- a/smtpd/ruleset.c
+++ b/smtpd/ruleset.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ruleset.c,v 1.45 2019/11/04 00:05:38 gilles Exp $ */
+/* $OpenBSD: ruleset.c,v 1.47 2019/11/25 14:18:33 gilles Exp $ */
/*
* Copyright (c) 2009 Gilles Chehade <gilles@poolp.org>
@@ -84,9 +84,12 @@ ruleset_match_from(struct rule *r, const struct envelope *evp)
}
else {
key = ss_to_text(&evp->ss);
- if (strcmp(key, "local") == 0)
- if (r->flag_from_socket)
+ if (r->flag_from_socket) {
+ if (strcmp(key, "local") == 0)
return MATCH_RESULT(1, r->flag_from);
+ else
+ return r->flag_from < 0 ? 1 : 0;
+ }
}
if (r->flag_from_regex)
service = K_REGEX;
@@ -149,6 +152,8 @@ static int
ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp)
{
int ret;
+ struct table *table;
+ enum table_service service;
if (!r->flag_smtp_auth)
return 1;
@@ -156,14 +161,14 @@ ruleset_match_smtp_auth(struct rule *r, const struct envelope *evp)
if (!(evp->flags & EF_AUTHENTICATED))
ret = 0;
else if (r->table_smtp_auth) {
- /* XXX - not until smtp_session->username is added to envelope */
- /*
- * table = table_find(m->from_table);
- * key = evp->username;
- * return table_match(table, K_CREDENTIALS, key);
- */
- return -1;
+ if (r->flag_smtp_auth_regex)
+ service = K_REGEX;
+ else
+ service = strchr(evp->username, '@') ?
+ K_MAILADDR : K_STRING;
+ table = table_find(env, r->table_smtp_auth);
+ ret = table_match(table, service, evp->username);
}
else
ret = 1;
diff --git a/smtpd/smtp_session.c b/smtpd/smtp_session.c
index 6c405142..373a476a 100644
--- a/smtpd/smtp_session.c
+++ b/smtpd/smtp_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp_session.c,v 1.415 2019/10/04 08:34:29 gilles Exp $ */
+/* $OpenBSD: smtp_session.c,v 1.419 2020/01/03 22:01:29 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -252,6 +252,28 @@ 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;
@@ -686,7 +708,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
{
struct smtp_session *s;
struct smtp_rcpt *rcpt;
- char user[LOGIN_NAME_MAX];
+ char user[SMTPD_MAXMAILADDRSIZE];
char tmp[SMTP_LINE_MAX];
struct msg m;
const char *line, *helo;
@@ -775,7 +797,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
s->tx->msgid = msgid;
s->tx->evp.id = msgid_to_evpid(msgid);
s->tx->rcptcount = 0;
- report_smtp_tx_begin("smtp-in", s->id, s->tx->msgid);
+ smtp_report_tx_begin(s, s->tx->msgid);
smtp_reply(s, "250 %s Ok",
esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
} else {
@@ -844,7 +866,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
m_get_evpid(&m, &evpid);
s->tx->evp.id = evpid;
s->tx->destcount++;
- report_smtp_tx_envelope("smtp-in", s->id, s->tx->msgid, evpid);
+ smtp_report_tx_envelope(s, s->tx->msgid, evpid);
}
else
s->tx->error = TX_ERROR_ENVELOPE;
@@ -940,7 +962,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
"result=ok",
s->id, user);
s->flags |= SF_AUTHENTICATED;
- report_smtp_link_auth("smtp-in", s->id, user, "pass");
+ smtp_report_link_auth(s, user, "pass");
smtp_reply(s, "235 %s Authentication succeeded",
esc_code(ESC_STATUS_OK, ESC_OTHER_STATUS));
}
@@ -949,7 +971,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
"authentication user=%s "
"result=permfail",
s->id, user);
- report_smtp_link_auth("smtp-in", s->id, user, "fail");
+ smtp_report_link_auth(s, user, "fail");
smtp_auth_failure_pause(s);
return;
}
@@ -958,7 +980,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
"authentication user=%s "
"result=tempfail",
s->id, user);
- report_smtp_link_auth("smtp-in", s->id, user, "error");
+ smtp_report_link_auth(s, user, "error");
smtp_reply(s, "421 %s Temporary failure",
esc_code(ESC_STATUS_TEMPFAIL, ESC_OTHER_MAIL_SYSTEM_STATUS));
}
@@ -990,7 +1012,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
if (!strncmp(filter_param, "421", 3))
filter_response = FILTER_DISCONNECT;
- report_smtp_filter_response("smtp-in", s->id, s->filter_phase,
+ smtp_report_filter_response(s, s->filter_phase,
filter_response, filter_param);
smtp_reply(s, "%s", filter_param);
@@ -1014,7 +1036,7 @@ smtp_session_imsg(struct mproc *p, struct imsg *imsg)
/* fallthrough */
case FILTER_REWRITE:
- report_smtp_filter_response("smtp-in", s->id, s->filter_phase,
+ smtp_report_filter_response(s, s->filter_phase,
filter_response,
filter_param == s->filter_param ? NULL : filter_param);
if (s->filter_phase == FILTER_CONNECT) {
@@ -1083,7 +1105,7 @@ smtp_io(struct io *io, int evt, void *arg)
log_info("%016"PRIx64" smtp tls ciphers=%s",
s->id, tls_to_text(io_tls(s->io)));
- report_smtp_link_tls("smtp-in", s->id, tls_to_text(io_tls(s->io)));
+ smtp_report_link_tls(s, tls_to_text(io_tls(s->io)));
s->flags |= SF_SECURE;
s->helo[0] = '\0';
@@ -1176,7 +1198,7 @@ smtp_io(struct io *io, int evt, void *arg)
log_info("%016"PRIx64" smtp disconnected "
"reason=timeout",
s->id);
- report_smtp_timeout("smtp-in", s->id);
+ smtp_report_timeout(s);
smtp_free(s, "timeout");
break;
@@ -1211,20 +1233,20 @@ smtp_command(struct smtp_session *s, char *line)
* These states are special.
*/
if (s->state == STATE_AUTH_INIT) {
- report_smtp_protocol_client("smtp-in", s->id, "********");
+ smtp_report_protocol_client(s, "********");
smtp_rfc4954_auth_plain(s, line);
return;
}
if (s->state == STATE_AUTH_USERNAME || s->state == STATE_AUTH_PASSWORD) {
- report_smtp_protocol_client("smtp-in", s->id, "********");
+ smtp_report_protocol_client(s, "********");
smtp_rfc4954_auth_login(s, line);
return;
}
if (s->state == STATE_HELO && strncasecmp(line, "AUTH PLAIN ", 11) == 0)
- report_smtp_protocol_client("smtp-in", s->id, "AUTH PLAIN ********");
+ smtp_report_protocol_client(s, "AUTH PLAIN ********");
else
- report_smtp_protocol_client("smtp-in", s->id, line);
+ smtp_report_protocol_client(s, line);
/*
@@ -1639,10 +1661,6 @@ smtp_filter_begin(struct smtp_session *s)
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_add_sockaddr(p_lka, (struct sockaddr *)&s->ss);
- m_add_sockaddr(p_lka, (struct sockaddr *)&s->listener->ss);
- m_add_string(p_lka, s->rdns);
- m_add_int(p_lka, s->fcrdns);
m_close(p_lka);
}
@@ -1730,7 +1748,7 @@ 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;
- report_smtp_link_identify("smtp-in", s->id, "HELO", s->helo);
+ smtp_report_link_identify(s, "HELO", s->helo);
smtp_enter_state(s, STATE_HELO);
@@ -1750,7 +1768,7 @@ smtp_proceed_ehlo(struct smtp_session *s, const char *args)
s->flags |= SF_EHLO;
s->flags |= SF_8BITMIME;
- report_smtp_link_identify("smtp-in", s->id, "EHLO", s->helo);
+ 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",
@@ -2037,7 +2055,7 @@ smtp_connected(struct smtp_session *s)
smtp_filter_begin(s);
- report_smtp_link_connect("smtp-in", s->id, s->rdns, s->fcrdns, &s->ss,
+ 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));
@@ -2057,7 +2075,7 @@ smtp_send_banner(struct smtp_session *s)
{
smtp_reply(s, "220 %s ESMTP %s", s->smtpname, SMTPD_NAME);
s->banner_sent = 1;
- report_smtp_link_greeting("smtp-in", s->id, s->smtpname);
+ smtp_report_link_greeting(s, s->smtpname);
}
void
@@ -2097,15 +2115,15 @@ smtp_reply(struct smtp_session *s, char *fmt, ...)
case '2':
if (s->tx) {
if (s->last_cmd == CMD_MAIL_FROM)
- report_smtp_tx_mail("smtp-in", s->id, s->tx->msgid, s->cmd + 10, 1);
+ smtp_report_tx_mail(s, s->tx->msgid, s->cmd + 10, 1);
else if (s->last_cmd == CMD_RCPT_TO)
- report_smtp_tx_rcpt("smtp-in", s->id, s->tx->msgid, s->cmd + 8, 1);
+ smtp_report_tx_rcpt(s, s->tx->msgid, s->cmd + 8, 1);
}
break;
case '3':
if (s->tx) {
if (s->last_cmd == CMD_DATA)
- report_smtp_tx_data("smtp-in", s->id, s->tx->msgid, 1);
+ smtp_report_tx_data(s, s->tx->msgid, 1);
}
break;
case '5':
@@ -2115,13 +2133,13 @@ smtp_reply(struct smtp_session *s, char *fmt, ...)
*/
if (s->tx) {
if (s->last_cmd == CMD_MAIL_FROM)
- report_smtp_tx_mail("smtp-in", s->id, s->tx->msgid,
+ smtp_report_tx_mail(s, s->tx->msgid,
s->cmd + 10, buf[0] == '4' ? -1 : 0);
else if (s->last_cmd == CMD_RCPT_TO)
- report_smtp_tx_rcpt("smtp-in", s->id,
+ 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)
- report_smtp_tx_data("smtp-in", s->id, s->tx->msgid,
+ smtp_report_tx_data(s, s->tx->msgid,
buf[0] == '4' ? -1 : 0);
}
@@ -2159,7 +2177,7 @@ smtp_reply(struct smtp_session *s, char *fmt, ...)
}
io_xprintf(s->io, "%s\r\n", buf);
- report_smtp_protocol_server("smtp-in", s->id, buf);
+ smtp_report_protocol_server(s, buf);
}
static void
@@ -2171,7 +2189,7 @@ smtp_free(struct smtp_session *s, const char * reason)
smtp_tx_free(s->tx);
}
- report_smtp_link_disconnect("smtp-in", s->id);
+ smtp_report_link_disconnect(s);
smtp_filter_end(s);
if (s->flags & SF_SECURE && s->listener->flags & F_SMTPS)
@@ -2335,6 +2353,7 @@ smtp_tx(struct smtp_session *s)
(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;
@@ -2539,8 +2558,8 @@ smtp_tx_commit(struct smtp_tx *tx)
m_add_msgid(p_queue, tx->msgid);
m_close(p_queue);
tree_xset(&wait_queue_commit, tx->session->id, tx->session);
- report_smtp_tx_commit("smtp-in", tx->session->id, tx->msgid, tx->odatalen);
- report_smtp_tx_reset("smtp-in", tx->session->id, tx->msgid);
+ smtp_report_tx_commit(tx->session, tx->msgid, tx->odatalen);
+ smtp_report_tx_reset(tx->session, tx->msgid);
smtp_filter_data_end(tx->session);
}
@@ -2550,8 +2569,8 @@ 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);
- report_smtp_tx_rollback("smtp-in", tx->session->id, tx->msgid);
- report_smtp_tx_reset("smtp-in", tx->session->id, tx->msgid);
+ smtp_report_tx_rollback(tx->session, tx->msgid);
+ smtp_report_tx_reset(tx->session, tx->msgid);
smtp_filter_data_end(tx->session);
}
@@ -2564,7 +2583,7 @@ smtp_tx_dataline(struct smtp_tx *tx, const char *line)
log_trace(TRACE_SMTP, "<<< [MSG] %s", line);
if (!strcmp(line, ".")) {
- report_smtp_protocol_client("smtp-in", tx->session->id, ".");
+ smtp_report_protocol_client(tx->session, ".");
log_trace(TRACE_SMTP, "<<< [EOM]");
if (tx->error)
return 1;
@@ -2944,3 +2963,169 @@ smtp_strstate(int 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)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_mail("smtp-in", s->id, msgid, address, ok);
+}
+
+static void
+smtp_report_tx_rcpt(struct smtp_session *s, uint32_t msgid, const char *address, int ok)
+{
+ if (! SESSION_FILTERED(s))
+ return;
+
+ report_smtp_tx_rcpt("smtp-in", s->id, msgid, address, 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);
+}
diff --git a/smtpd/smtpd-filters.7 b/smtpd/smtpd-filters.7
new file mode 100644
index 00000000..1e1a27ef
--- /dev/null
+++ b/smtpd/smtpd-filters.7
@@ -0,0 +1,646 @@
+.\" $OpenBSD: smtpd-filters.7,v 1.4 2020/01/06 13:42:42 gilles Exp $
+.\"
+.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org>
+.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
+.\" Copyright (c) 2012 Gilles Chehade <gilles@poolp.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.
+.\"
+.\"
+.Dd $Mdocdate: January 6 2020 $
+.Dt FILTERS 7
+.Os
+.Sh NAME
+.Nm filters
+.Nd filtering API for the
+.Xr smtpd 8
+daemon
+.Sh DESCRIPTION
+The
+.Xr smtpd 8
+daemon provides a Simple Mail Transfer Protocol (SMTP) implementation
+that allows an ordinary machine to become Mail eXchangers (MX).
+Many features that are commonly used by MX,
+such as delivery reporting or Spam filtering,
+are outside the scope of SMTP and too complex to fit in
+.Xr smtpd 8 .
+.Pp
+Because an MX needs to provide these features,
+.Xr smtpd 8
+provides an API to extend its behavior through pluggable
+.Nm .
+.Pp
+At runtime,
+.Xr smtpd 8
+can report events to
+.Nm
+and query what it should answer to these events.
+This allows the decision logic to rely on third-party programs.
+.Sh DESIGN
+The
+.Nm
+are programs that run as unique standalone processes,
+they do not share
+.Xr smtpd 8
+memory space.
+They are executed by
+.Xr smtpd 8
+at startup and expected to run in an infinite loop,
+reading events and filtering requests from
+.Xr stdin 4 ,
+writing responses to
+.Xr stdout 4
+and logging to
+.Xr stderr 4 .
+They are not allowed to terminate.
+.Pp
+Because
+.Nm
+are standalone programs that communicate with
+.Xr smtpd 8
+through
+.Xr fd 4 ,
+they may run as different users than
+.Xr smtpd 8
+and may be written in any language.
+The
+.Nm
+must not use blocking I/O,
+they must support answering asynchronously to
+.Xr smtpd 8 .
+.Sh REPORT AND FILTER
+The API relies on two streams,
+report and filter.
+.Pp
+The report stream is a one-way stream which allows
+.Xr smtpd 8
+to inform
+.Nm
+in real-time about events that are occurring in the daemon.
+The report events do not expect an answer from
+.Nm ,
+it is just meant to provide them with informations.
+A filter should be able to replicate the
+.Xr smtpd 8
+state for a session by gathering informations coming from report events.
+No decision is ever taken by the report stream.
+.Pp
+The filter stream is a two-way stream which allows
+.Xr smtpd 8
+to query
+.Nm
+about what it should do with a session at a given phase.
+The filter requests expects an answer from
+.Nm ,
+.Xr smtpd 8
+will not let the session move forward until then.
+A decision must always be taken by the filter stream.
+.Pp
+It is sometimes possible to rely on filter requests to gather information,
+but because a reponse is expected by
+.Xr smtpd 8 ,
+this is more costly than using report events.
+The correct pattern for writing filters is to use the report events to
+create a local state for a session,
+then use filter requests to take decisions based on this state.
+The only case when using filter request instead of report events is correct,
+is when a decision is required for the filter request and there is no need for
+more information than that of the event.
+.Sh PROTOCOL
+The protocol is straightforward,
+it consists of a human-readable line exchanges between
+.Nm
+and
+.Xr smtpd 8
+through
+.Xr fd 4 .
+.Pp
+The protocol begins with a handshake.
+First,
+.Xr smtpd 8
+provides
+.Nm
+with general configuration information in the form of key-value lines:
+.Bd -literal -offset indent
+config|smtpd-version|6.6.1
+config|smtp-session-timeout|300
+config|subsystem|smtp-in
+config|ready
+.Ed
+.Pp
+Then,
+.Nm
+register the stream,
+subsystem and event they want to handle:
+.Bd -literal -offset indent
+register|report|smtp-in|link-connect
+register|ready
+.Ed
+.Pp
+Finally,
+.Xr smtpd 8
+will emit report events and filter requests,
+expecting
+.Nm
+to react accordingly either by responding or not depending on the stream:
+.Bd -literal -offset indent
+report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
+report|0.5|1576147242.200225|smtp-in|link-connect|7641dfb3798eb5bf|mail.openbsd.org|pass|199.185.178.25:31205|45.77.67.80:25
+report|0.5|1576148447.982572|smtp-in|link-connect|7641dfc063102cbd|mail.openbsd.org|pass|199.185.178.25:24786|45.77.67.80:25
+.Ed
+.Pp
+The list of subsystems and events,
+as well as the format of requests and reponses,
+will be documented in the sections below.
+.Sh CONFIGURATION
+During the initial handshake,
+.Xr smtpd 8
+will emit a serie of configuration keys and values.
+The list is meant to be ignored by
+.Nm
+that do not require it and consumed gracefully by filters that do.
+.Pp
+There are currently three keys:
+.Bd -literal -offset indent
+config|smtpd-version|6.6.1
+config|smtp-session-timeout|300
+config|subsystem|smtp-in
+.Ed
+.Pp
+When
+.Xr smtpd 8
+has sent all configuration keys it emits the following line:
+.Bd -literal -offset indent
+config|ready
+.Ed
+.Sh REPORT EVENTS
+There is currently only one subsystem supported in the API:
+smtp-in.
+.Pp
+Each report event is generated by
+.Xr smtpd 8
+as a single line similar to the one below:
+.Bd -literal -offset indent
+report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
+.Ed
+.Pp
+The format consists of a protocol prefix containing the stream,
+the protocol version,
+the timestamp,
+the subsystem,
+the event and the unique session identifier separated by
+.Dq | :
+.Bd -literal -offset indent
+report|0.5|1576146008.006099|smtp-in|link-connect|7641df9771b4ed00
+.Ed
+.Pp
+It is followed by a suffix containing the event-specific parameters,
+also separated by
+.Dq | :
+.Bd -literal -offset indent
+mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
+.Ed
+.Pp
+The list of events and event-specific parameters are provided here for smtp-in:
+.Bl -tag -width Ds
+.It Ic link-connect : Ar rdns Ar fcrdns Ar src Ar dest
+This event is generated upon connection.
+.Pp
+.Ar rdns
+contains the reverse DNS hostname for the remote end or an empty string if none.
+.Pp
+.Ar fcrdns
+contains the string
+.Dq pass
+or
+.Dq fail
+depending on if the remote end validates FCrDNS.
+.Pp
+.Ar src
+holds either the IP address and port from source address,
+in the format
+.Dq address:port
+or the path to a UNIX socket in the format
+.Dq unix:/path .
+.Pp
+.Ar dest
+holds either the IP address and port from destination address,
+in the format
+.Dq address:port
+or the path to a UNIX socket in the format
+.Dq unix:/path .
+.It Ic link-greeting : Ar hostname
+This event is generated upon display of the server banner.
+.Pp
+.Ar hostname
+contains the hostname displayed in the banner.
+.It Ic link-identify : Ar method Ar identity
+This event is generated upon
+.Dq HELO
+or
+.Dq EHLO
+command from the client.
+.Pp
+.Ar method
+contains the string
+.Dq HELO
+or
+.Dq EHLO
+indicating the method used by the client.
+.Pp
+.Ar identity
+contains the identity provided by the client.
+.It Ic link-tls : Ar tls-string
+This event is generated upon successful negotiation of TLS.
+.Pp
+.Ar tls-string
+contains a colon-separated list of TLS properties including the TLS version,
+the cipher suite used by the session and the cipher strenght in bits.
+.It Ic link-disconnect
+This event is generated upon disconnection of the client.
+.It Ic link-auth : Ar username Ar result
+This event is generated upon authentication attempt of the client.
+.Pp
+.Ar username
+contains the username used for the authentication attempt.
+.Pp
+.Ar result
+contains the string
+.Dq pass ,
+.Dq fail
+or
+.Dq error
+depending on the result of the authentication attempt.
+.It Ic tx-reset : Op message-id
+This event is generated when a transaction is reset.
+.Pp
+If reset happend while in a transaction,
+.Ar message-id
+contains the identifier of the transaction being reset.
+.It Ic tx-begin : Ar message-id
+This event is generated when a transaction is initiated.
+.Pp
+.Ar message-id
+contains the identifier for the transaction.
+.It Ic tx-mail : Ar message-id Ar address Ar result
+This event is generated when client emits
+.Dq MAIL FROM .
+.Pp
+.Ar message-id
+contains the identifier for the transaction.
+.Pp
+.Ar address
+contains the e-mail address of the sender.
+The address is normalized and sanitized,
+the protocol
+.Dq <
+and
+.Dq >
+are removed and so are parameters to
+.Dq MAIL FROM .
+.Pp
+.Ar result
+contains
+.Dq ok
+if the sender was accepted,
+.Dq permfail
+if it was rejected
+or
+.Dq tempfail
+if it was rejected for a transient error.
+.It Ic tx-rcpt : Ar message-id Ar address Ar result
+This event is generated when client emits
+.Dq RCPT TO .
+.Pp
+.Ar message-id
+contains the identifier for the transaction.
+.Pp
+.Ar address
+contains the e-mail address of the recipient.
+The address is normalized and sanitized,
+the protocol
+.Dq <
+and
+.Dq >
+are removed and so are parameters to
+.Dq RCPT TO .
+.Pp
+.Ar result
+contains
+.Dq ok
+if the recipient was accepted,
+.Dq permfail
+if it was rejected
+or
+.Dq tempfail
+if it was rejected for a transient error.
+.It Ic tx-envelope : Ar message-id Ar envelope-id
+This event is generated when an envelope is accepted.
+.Pp
+.Ar envelope-id
+contains the unique identifier for the envelope.
+.It Ic tx-data : Ar message-id Ar result
+This event is generated when client has emitted
+.Dq DATA .
+.Pp
+.Ar message-id
+contains the unique identifier for the transaction.
+.Pp
+.Ar result
+contains
+.Dq ok
+if server accepted to process the message,
+.Dq permfail
+if it has not accepted and
+.Dq tempfail
+if a transient error is preventing the processing of message.
+.It Ic tx-commit : Ar message-id Ar message-size
+This event is generated when a transaction has been accepted by the server.
+.Pp
+.Ar message-id
+contains the unique identifier for the SMTP transaction.
+.Pp
+.Ar message-size
+contains the size of the message submitted in the
+.Dq DATA
+phase of the SMTP transaction.
+.It Ic tx-rollback : Ar message-id
+This event is generated when a transaction has been rejected by the server.
+.Pp
+.Ar message-id
+contains the unique identifier for the SMTP transaction.
+.It Ic protocol-client : Ar command
+This event is generated for every command submitted by the client.
+It contains the raw command as received by the server.
+.Pp
+.Ar command
+contains the command emitted by the client to the server.
+.It Ic protocol-server : Ar response
+This event is generated for every response emitted by the server.
+It contains the raw response as emitted by the server.
+.Pp
+.Ar response
+contains the response emitted by the server to the client.
+.It Ic filter-report : Ar filter-kind Ar name Ar message
+This event is generated when a filter emits a report.
+.Pp
+.Ar filter-kind may be either
+.Dq builtin
+or
+.Dq proc
+depending on if the filter is an
+.Xr smtpd 8
+builtin filter or a proc filter implementing the API.
+.Pp
+.Ar name
+is the name of the filter that generated the report.
+.Pp
+.Ar message
+is a filter-specific message.
+.It Ic filter-response : Ar phase Ar response Op param
+This event is generated when a filter responds to a filtering request.
+.Pp
+.Ar phase
+contains the phase name for the request.
+The phases are documented in the next section.
+.Pp
+.Ar response
+contains the response of the filter to the request,
+it is either one of
+.Dq proceed ,
+.Dq report ,
+.Dq reject ,
+.Dq disconnect ,
+.Dq junk or
+.Dq rewrite .
+.Pp
+If specified,
+.Ar param
+is the parameter to the response.
+.It Ic timeout
+This event is generated when a timeout happens for a session.
+.El
+.Sh FILTER REQUESTS
+There is currently only one subsystem supported in the API:
+smtp-in.
+.Pp
+The filter requests allow
+.Xr smtpd 8
+to query
+.Nm
+about what to do with a session at a particular phase.
+In addition,
+they allow
+.Nm
+to alter the content of a message by adding,
+modifying,
+or suppressing lines of input in a way that is similar to what program like
+.Xr sed 1
+or
+.Xr grep 1
+would do.
+.Pp
+Each filter request is generated by
+.Xr smtpd 8
+as a single line similar to the one below:
+.Bd -literal -offset indent
+filter|0.5|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d|mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
+.Ed
+.Pp
+The format consists of a protocol prefix containing the stream,
+the protocol version,
+the timestamp,
+the subsystem,
+the filtering phase,
+the unique session identifier and an opaque token separated by
+.Dq |
+that the filter should provide in its response:
+.Bd -literal -offset indent
+filter|0.5|1576146008.006099|smtp-in|connect|7641df9771b4ed00|1ef1c203cc576e5d
+.Ed
+.Pp
+It is followed by a suffix containing the phase-specific parameters to the
+filter request,
+also separated by
+.Dq | :
+.Bd -literal -offset indent
+mail.openbsd.org|pass|199.185.178.25:33174|45.77.67.80:25
+.Ed
+.Pp
+Unlike with report events,
+.Xr smtpd 8
+expects answers from filter requests and will not allow a session to move
+forward before the filter has instructed
+.Xr smtpd 8
+what to do with it.
+.Pp
+For all phases,
+excepted
+.Dq data-line ,
+the responses must follow the same construct,
+a message type
+.Dq filter-result ,
+followed by the unique session id,
+the opaque token,
+a decision and optional decision-specific parameters:
+.Bd -literal -offset indent
+filter-result|7641df9771b4ed00|1ef1c203cc576e5d|proceed
+filter-result|7641df9771b4ed00|1ef1c203cc576e5d|reject|550 nope
+.Ed
+.Pp
+The possible decisions to a
+.Dq filter-result
+message will be described below.
+.Pp
+For the
+.Dq data-line
+phase,
+.Nm
+are fed with a stream of lines corresponding to the message to filter,
+and terminated by a single dot:
+.Bd -literal -offset indent
+filter|0.5|1576146008.006099|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 1
+filter|0.5|1576146008.006103|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|line 2
+filter|0.5|1576146008.006105|smtp-in|data-line|7641df9771b4ed00|1ef1c203cc576e5d|.
+.Ed
+.Pp
+They are expected to produce an output stream similarly terminate by a single
+dot.
+A filter may inject,
+suppress,
+modify or echo back the lines it receives.
+Ultimately,
+.Xr smtpd 8
+will assume that the message consists of the output from
+.Nm .
+.Pp
+Note that filters may be chained and the lines that are input into a filter
+are the lines that are output from previous filter.
+.Pp
+The response to
+.Dq data-line
+requests use their own construct.
+A
+.Dq filter-dataline
+prefix,
+followed by the unique session identifier,
+the opaque token and the output line as follows:
+.Bd -literal -offset indent
+filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 1
+filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|line 2
+filter-dataline|7641df9771b4ed00|1ef1c203cc576e5d|.
+.Ed
+.Pp
+The list of events and event-specific parameters are provided here for smtp-in:
+.Bl -tag -width Ds
+.It Ic connect : Ar rdns Ar fcrdns Ar src Ar dest
+This request is emitted after connection,
+before the banner is displayed.
+.It Ic helo : Ar identity
+This request is emitted after the client has emitted
+.Dq HELO .
+.It Ic ehlo : Ar identity
+This request is emitted after the client has emitted
+.Dq EHLO .
+.It Ic starttls : Ar tls-string
+This request is emitted after the client has requested
+.Dq STARTTLS .
+.It Ic auth : Ar auth
+This request is emitted after the client has requested
+.Dq AUTH .
+.It Ic mail-from : Ar address
+This request is emitted after the client has requested
+.Dq MAIL FROM .
+.It Ic rcpt-to : Ar address
+This request is emitted after the client has requested
+.Dq RCPT TO .
+.It Ic data
+This request is emitted after the client has requested
+.Dq DATA .
+.It Ic data-line : Ar line
+This request is emitted for each line of input in the
+.Dq DATA
+phase.
+The lines are raw dot-escaped SMTP DATA input,
+terminated with a single dot.
+.It Ic commit
+This request is emitted after the final single dot is received.
+.El
+.Pp
+For every filtering phase,
+excepted
+.Dq data-line ,
+the following decisions may be taken by a filter:
+.Bl -tag -width Ds
+.It Ic proceed
+No action is taken,
+session or transaction may be passed to the next filter.
+.It Ic junk
+The session or transaction is marked as Spam.
+.Xr smtpd 8
+will prepend a
+.Dq X-Spam
+header to the message.
+.It Ic reject Ar error
+The command is rejected with the message
+.Ar error .
+The message must be a valid SMTP message including status code,
+5xx or 4xx.
+.Pp
+Messages starting with a 5xx status result in a permanent failure,
+those starting with a 4xx status result in a temporary failure.
+.Pp
+Messages starting with a 421 status will result in a client disconnect.
+.It Ic disconnect Ar error
+The client is disconnected with the message
+.Ar error .
+The message must be a valid SMTP message including status code,
+5xx or 4xx.
+.Pp
+Messages starting with a 5xx status result in a permanent failure,
+those starting with a 4xx status result in a temporary failure.
+.It Ic rewrite Ar parameter
+The command parameter is rewritten.
+.Pp
+This decision allows a filter to perform a rewrite of client-submitted
+commands before they are processed by the SMTP engine.
+.Ar parameter
+is expected to be a valid SMTP parameter for the command.
+.It Ic report Ar parameter
+Generates a report with
+.Ar parameter
+for this filter.
+.El
+.\".Sh EXAMPLES
+.\"This example filter written in
+.\".Xr sh 1
+.\"will echo back...
+.\".Bd -literal -offset indent
+.\"XXX
+.\".Ed
+.\".Pp
+.\"This example filter will filter...
+.\".Bd -literal -offset indent
+.\"XXX
+.\".Ed
+.\".Pp
+.\"Note that libraries may provide a simpler interface to
+.\".Nm
+.\"that does not require implementing the protocol itself.
+.\".Ed
+.Sh SEE ALSO
+.Xr smtpd 8
+.Sh HISTORY
+.Nm
+first appeared in
+.Ox 6.6 .
diff --git a/smtpd/smtpd.c b/smtpd/smtpd.c
index 01e06c51..a400d9b1 100644
--- a/smtpd/smtpd.c
+++ b/smtpd/smtpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.c,v 1.325 2019/09/03 04:48:20 martijn Exp $ */
+/* $OpenBSD: smtpd.c,v 1.328 2019/12/18 10:00:39 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -91,8 +91,8 @@ static int parent_auth_user(const char *, const char *);
static void load_pki_tree(void);
static void load_pki_keys(void);
-static void fork_processors(void);
-static void fork_processor(const char *, const char *, const char *, const char *, const char *);
+static void fork_filter_processes(void);
+static void fork_filter_process(const char *, const char *, const char *, const char *, const char *, uint32_t);
enum child_type {
CHILD_DAEMON,
@@ -157,7 +157,7 @@ static void
parent_imsg(struct mproc *p, struct imsg *imsg)
{
struct forward_req *fwreq;
- struct processor *processor;
+ struct filter_proc *processor;
struct deliver deliver;
struct child *c;
struct msg m;
@@ -261,7 +261,7 @@ parent_imsg(struct mproc *p, struct imsg *imsg)
m_get_string(&m, &procname);
m_end(&m);
- processor = dict_xget(env->sc_processors_dict, procname);
+ processor = dict_xget(env->sc_filter_processes_dict, procname);
m_create(p_lka, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, processor->errfd);
m_add_string(p_lka, procname);
m_close(p_lka);
@@ -1081,7 +1081,7 @@ smtpd(void) {
offline_timeout.tv_usec = 0;
evtimer_add(&offline_ev, &offline_timeout);
- fork_processors();
+ fork_filter_processes();
purge_task();
@@ -1259,22 +1259,46 @@ purge_task(void)
}
static void
-fork_processors(void)
+fork_filter_processes(void)
{
const char *name;
- struct processor *processor;
void *iter;
+ const char *fn;
+ struct filter_config *fc;
+ struct filter_config *fcs;
+ struct filter_proc *fp;
+ size_t i;
+ /* For each filter chain, assign the registered subsystem to subfilters */
iter = NULL;
- while (dict_iter(env->sc_processors_dict, &iter, &name, (void **)&processor))
- fork_processor(name, processor->command, processor->user, processor->group, processor->chroot);
+ while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) {
+ if (fc->chain) {
+ for (i = 0; i < fc->chain_size; ++i) {
+ fcs = dict_xget(env->sc_filters_dict, fc->chain[i]);
+ fcs->filter_subsystem |= fc->filter_subsystem;
+ }
+ }
+ }
+
+ /* For each filter, assign the registered subsystem to underlying proc */
+ iter = NULL;
+ while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) {
+ if (fc->proc) {
+ fp = dict_xget(env->sc_filter_processes_dict, fc->proc);
+ fp->filter_subsystem |= fc->filter_subsystem;
+ }
+ }
+
+ iter = NULL;
+ while (dict_iter(env->sc_filter_processes_dict, &iter, &name, (void **)&fp))
+ fork_filter_process(name, fp->command, fp->user, fp->group, fp->chroot, fp->filter_subsystem);
}
static void
-fork_processor(const char *name, const char *command, const char *user, const char *group, const char *chroot_path)
+fork_filter_process(const char *name, const char *command, const char *user, const char *group, const char *chroot_path, uint32_t subsystems)
{
pid_t pid;
- struct processor *processor;
+ struct filter_proc *processor;
char buf;
int sp[2], errfd[2];
struct passwd *pw;
@@ -1306,13 +1330,14 @@ fork_processor(const char *name, const char *command, const char *user, const ch
/* parent passes the child fd over to lka */
if (pid > 0) {
- processor = dict_xget(env->sc_processors_dict, name);
+ processor = dict_xget(env->sc_filter_processes_dict, name);
processor->errfd = errfd[1];
child_add(pid, CHILD_PROCESSOR, name);
close(sp[0]);
close(errfd[0]);
m_create(p_lka, IMSG_LKA_PROCESSOR_FORK, 0, 0, sp[1]);
m_add_string(p_lka, name);
+ m_add_u32(p_lka, (uint32_t)subsystems);
m_close(p_lka);
return;
}
@@ -1331,7 +1356,7 @@ fork_processor(const char *name, const char *command, const char *user, const ch
if (setgroups(1, &gr->gr_gid) ||
setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid) ||
setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
- err(1, "fork_processor: cannot drop privileges");
+ err(1, "fork_filter_process: cannot drop privileges");
if (closefrom(STDERR_FILENO + 1) == -1)
err(1, "closefrom");
@@ -2051,6 +2076,9 @@ imsg_to_str(int type)
CASE(IMSG_REPORT_SMTP_LINK_CONNECT);
CASE(IMSG_REPORT_SMTP_LINK_DISCONNECT);
CASE(IMSG_REPORT_SMTP_LINK_TLS);
+ CASE(IMSG_REPORT_SMTP_LINK_GREETING);
+ CASE(IMSG_REPORT_SMTP_LINK_IDENTIFY);
+ CASE(IMSG_REPORT_SMTP_LINK_AUTH);
CASE(IMSG_REPORT_SMTP_TX_RESET);
CASE(IMSG_REPORT_SMTP_TX_BEGIN);
diff --git a/smtpd/smtpd.conf.5 b/smtpd/smtpd.conf.5
index 580d5838..74f3ca6e 100644
--- a/smtpd/smtpd.conf.5
+++ b/smtpd/smtpd.conf.5
@@ -1,4 +1,4 @@
-.\" $OpenBSD: smtpd.conf.5,v 1.226 2019/09/20 18:47:23 jmc Exp $
+.\" $OpenBSD: smtpd.conf.5,v 1.246 2019/12/19 13:34:45 jmc Exp $
.\"
.\" Copyright (c) 2008 Janne Johansson <jj@openbsd.org>
.\" Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
@@ -17,7 +17,7 @@
.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\"
.\"
-.Dd $Mdocdate: September 20 2019 $
+.Dd $Mdocdate: December 19 2019 $
.Dt SMTPD.CONF 5
.Os
.Sh NAME
@@ -228,6 +228,10 @@ Use the mapping
.Ar table
to look up a hostname matching the source address,
to advertise during the HELO phase.
+.It Cm domain Pf < Ar domains Ns >
+Do not perform MX lookups but look up destination domain in
+.Ar domains
+and use matching relay url as relay host.
.It Cm host Ar relay-url
Do not perform MX lookups but relay messages to the relay host described by
.Ar relay-url .
@@ -333,64 +337,26 @@ or using the
.Ic hostname
directive.
.It Ic filter Ar chain-name Ic chain Brq Ar filter-name Op , Ar ...
-Register a chain of filters named
-.Ar chain-name
-and consisting of the filters listed from
+Register a chain of filters
+.Ar chain-name ,
+consisting of the filters listed from
.Ar filter-name .
Filters part of a filter chain are executed in order of declaration for
each phase that they are registered for.
A filter chain may be used in place of a filter for any directive but
filter chains themselves.
-.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic disconnect Ar message
-Register builtin filter
-.Ar filter-name
-matching
-.Ar conditions
-to disconnect session with
-.Ar message .
-Phase and matching conditions are documented in a specific section,
-see
-.Sx BUILTIN FILTERING .
-.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic junk
-Register builtin filter
-.Ar filter-name
-matching
-.Ar conditions
-to mark a session or a transaction as junk.
-Phase and matching conditions are documented in a specific section,
-see
-.Sx BUILTIN FILTERING .
-.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic reject Ar message
-Register builtin filter
-.Ar filter-name
-matching
-.Ar conditions
-to reject session with
-.Ar message .
-Phase and matching conditions are documented in a specific section,
-see
-.Sx BUILTIN FILTERING .
-.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic report Ar message
-Register builtin filter
-.Ar filter-name
-matching
-.Ar conditions
-to report on session with
-.Ar message
-and proceed with the transaction.
-Phase and matching conditions are documented in a specific section,
-see
-.Sx BUILTIN FILTERING .
-.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions Ic rewrite Ar value
-Register builtin filter
-.Ar filter-name
-matching
-.Ar conditions
-to rewrite phase parameter with new
-.Ar value .
-Phase and matching conditions are documented in a specific section,
-see
-.Sx BUILTIN FILTERING .
+.It Ic filter Ar filter-name Ic phase Ar phase-name Ic match Ar conditions decision
+Register a filter
+.Ar filter-name .
+A
+.Ar decision
+about what to do with the mail is taken at phase
+.Ar phase-name
+when matching
+.Ar conditions .
+Phases, matching conditions, and decisions are described in
+.Sx MAIL FILTERING ,
+below.
.It Ic filter Ar filter-name Ic proc Ar proc-name
Register
.Qq proc
@@ -536,17 +502,29 @@ With the
option, clients must also provide a valid certificate
to establish an SMTP session.
.El
-.It Ic listen on Cm socket Op Cm mask-src
+.It Ic listen on Cm socket Op Ar options
Listen for incoming SMTP connections on the Unix domain socket
.Pa /var/run/smtpd.sock .
This is done by default, even if the directive is absent.
-If the
-.Cm mask-src
-option is specified, printing of the HELO name, hostname, and IP
-address of the originating host is suppressed in Received: header lines.
-.\" XXX The option
-.\" Cm filter Ar string
-.\" is parsed, but not implemented, see smtpf_session.c.
+.Pp
+The
+.Ar options
+are as follows:
+.Bl -tag -width Ds
+.It Ic filter Ar name
+Apply filter
+.Ar name
+on connections handled by this listener.
+.It Cm mask-src
+Omit the
+.Sy from
+part when prepending
+.Dq Received
+headers.
+.It Cm tag Ar tag
+Clients connecting to the listener are tagged with the given
+.Ar tag .
+.El
.It Ic match Ar options Cm action Ar name
If at least one mail envelope matches the
.Ar options
@@ -586,11 +564,47 @@ Specify that session may address the regex or regex table
.Ar domain .
.It Xo
.Op Ic \&!
+.Cm for rcpt-to
+.Ar recipient | Pf < Ar recipient Ns >
+.Xc
+Specify that session may address the string or list table
+.Ar recipient .
+.It Xo
+.Op Ic \&!
+.Cm for rcpt-to regex
+.Ar recipient | Pf < Ar recipient Ns >
+.Xc
+Specify that session may address the regex or regex table
+.Ar recipient .
+.It Xo
+.Op Ic \&!
.Cm from any
.Xc
Specify that session may originate from any source.
.It Xo
.Op Ic \&!
+.Cm from auth
+.Xc
+Specify that session may originate from any authenticated user,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
+.Cm from auth
+.Ar user | Pf < Ar user Ns >
+.Xc
+Specify that session may originate from authenticated user or user list
+.Ar user ,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
+.Cm from auth
+.Ar user | Pf < Ar user Ns >
+.Xc
+Specify that session may originate from authenticated regex or regex list
+.Ar user ,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
.Cm from local
.Xc
Specify that session may only originate from a local IP address,
@@ -598,6 +612,22 @@ or from the local enqueuer.
This is the default, and may be omitted.
.It Xo
.Op Ic \&!
+.Cm from mail-from
+.Ar sender | Pf < Ar sender Ns >
+.Xc
+Specify that session may originate from sender or sender list
+.Ar sender ,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
+.Cm from mail-from regex
+.Ar sender | Pf < Ar sender Ns >
+.Xc
+Specify that session may originate from regex or regex list
+.Ar sender ,
+no matter the source IP address.
+.It Xo
+.Op Ic \&!
.Cm from rdns
.Xc
Specify that session may only originate from an IP address that
@@ -650,6 +680,20 @@ In addition, the following transaction options:
Matches transactions which have been authenticated.
.It Xo
.Op Ic \&!
+.Cm auth
+.Ar username | Pf < Ar username Ns >
+.Xc
+Matches transactions which have been authenticated for user or user list
+.Ar username .
+.It Xo
+.Op Ic \&!
+.Cm auth regex
+.Ar username | Pf < Ar username Ns >
+.Xc
+Matches transactions which have been authenticated for regex or regex list
+.Ar username .
+.It Xo
+.Op Ic \&!
.Cm helo
.Ar helo-name | Pf < Ar helo-name Ns >
.Xc
@@ -897,7 +941,7 @@ pairs.
The table must contain at least one key-value pair and may declare
multiple pairs as a comma-separated (whitespace optional) list.
.El
-.Ss BUILTIN FILTERING
+.Ss MAIL FILTERING
In a regular workflow,
.Xr smtpd 8
may accept or reject a message based only on the content of envelopes.
@@ -913,7 +957,7 @@ then decide if a session is allowed to move forward.
With filtering,
a session may be interrupted at any phase before an envelope is complete.
A message may also be rejected after being submitted,
-disregarding if the envelope was accepted or not.
+regardless of whether the envelope was accepted or not.
.Pp
The following phases are currently supported:
.Bl -column mail-from -offset indent
@@ -926,8 +970,9 @@ The following phases are currently supported:
.It commit Ta after message is fully is submitted
.El
.Pp
-At each phase,
-multiple criterias may be checked:
+At each phase, various conditions may be matched.
+The fcrdns, rdns, and src data are available in all phases,
+but other data must have been already submitted before they are available.
.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
.It fcrdns Ta forward-confirmed reverse DNS is valid
.It rdns Ta session has a reverse DNS
@@ -938,23 +983,20 @@ multiple criterias may be checked:
.It rcpt-to Pf < Ar table Ns > Ta recipient address is in table
.El
.Pp
-All criterias from previous phases are available to subsequent phases,
-so while the helo criteria is not available before the helo or ehlo phase,
-the fcrdns criteria is available in all phases.
-.Pp
-Criterias may all be negated by prefixing them with an exclamation mark:
+These conditions may all be negated by prefixing them with an exclamation mark:
.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
-.It ! fcrdns Ta forward-confirmed reverse DNS is invalid
+.It !fcrdns Ta forward-confirmed reverse DNS is invalid
.El
.Pp
-Any criteria using a table may indicate that tables hold regex by
+Any conditions using a table may indicate that tables hold regex by
prefixing the table name with the keyword regex.
.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
.It helo regex Pf < Ar table Ns > Ta helo name matches a regex in table
.El
-Finally,
-four decisions may be taken:
+.Pp
+Finally, a number of decisions may be taken:
.Bl -column XXXXXXXXXXXXXXXXXXXXX -offset indent
+.It bypass Ta the session or transaction bypasses filters
.It disconnect Ar message Ta the session is disconnected with message
.It junk Ta the session or transaction is junked
.It reject Ar message Ta the command is rejected with message
@@ -964,7 +1006,7 @@ four decisions may be taken:
Decisions that involve a message require that the message be RFC valid,
meaning that they should either start with a 4xx or 5xx status code.
Descisions can be taken at any phase,
-however junking can only happen before a message is committed.
+though junking can only happen before a message is committed.
.Ss FORMAT SPECIFIERS
Some configuration directives support expansion of their parameters at runtime.
Such directives (for example
@@ -1076,8 +1118,8 @@ action "local_mail" mbox alias <aliases>
action "outbound" relay host smtp+tls://bob@smtp.example.com \e
auth <secrets>
-match for local action "local_mail"
-match for any action "outbound"
+match from local for local action "local_mail"
+match from local for any action "outbound"
.Ed
.Pp
In this second example,
@@ -1117,26 +1159,40 @@ match for any action "outbound"
match auth from any for any action "outbound"
.Ed
.Pp
-For sites that wish to sign messages using DKIM, the
-.Sy dkimproxy
-package may be used as a filter.
-The following example is the same as the default configuration,
-but all outgoing mail is passed to dkimproxy_out on port 10027
-for signing.
-The signed messages are received on port 10028 and tagged for relaying.
+For sites that wish to sign messages using DKIM,
+the following example uses
+.Sy opensmtpd-filter-dkimsign
+for DKIM signing:
.Bd -literal -offset indent
table aliases file:/etc/mail/aliases
-listen on lo0
-listen on lo0 port 10028 tag DKIM
+filter "dkimsign" proc-exec "filter-dkimsign -d <domain> -s <selector> \e
+ -k /etc/mail/dkim/private.key" user _dkimsign group _dkimsign
+
+listen on socket filter "dkimsign"
+listen on lo0 filter "dkimsign"
action "local_mail" mbox alias <aliases>
action "outbound" relay
-action "relay_dkim" relay host smtp://127.0.0.1:10027
match for local action "local_mail"
-match tag DKIM for any action "outbound"
-match for any action "relay_dkim"
+match for any action "outbound"
+.Ed
+.Pp
+Alternatively, the
+.Sy opensmtpd-filter-rspamd
+package may be used to provide integration with
+.Sy rspamd ,
+a third-party daemon which provides multiple antispam features
+as well as DKIM signing.
+As well as configuring
+.Sy rspamd
+itself,
+it requires use of the
+.Cm proc-exec
+keyword:
+.Bd -literal -offset indent
+filter "rspamd" proc-exec "filter-rspamd"
.Ed
.Pp
Sites that accept non-local messages may be able to cut down on the
diff --git a/smtpd/smtpd.h b/smtpd/smtpd.h
index 6ce2ecf7..27aed0d0 100644
--- a/smtpd/smtpd.h
+++ b/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.642 2019/11/03 23:58:51 gilles Exp $ */
+/* $OpenBSD: smtpd.h,v 1.649 2019/12/21 10:40:20 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -490,6 +490,7 @@ struct envelope {
char smtpname[HOST_NAME_MAX+1];
char helo[HOST_NAME_MAX+1];
char hostname[HOST_NAME_MAX+1];
+ char username[SMTPD_MAXMAILADDRSIZE];
char errorline[LINE_MAX];
struct sockaddr_storage ss;
@@ -587,7 +588,7 @@ struct smtpd {
size_t sc_scheduler_max_msg_batch_size;
size_t sc_scheduler_max_schedule;
- struct dict *sc_processors_dict;
+ struct dict *sc_filter_processes_dict;
int sc_ttl;
#define MAX_BOUNCE_WARN 4
@@ -1033,22 +1034,29 @@ enum lka_resp_status {
LKA_PERMFAIL
};
-struct processor {
+enum filter_type {
+ FILTER_TYPE_BUILTIN,
+ FILTER_TYPE_PROC,
+ FILTER_TYPE_CHAIN,
+};
+
+enum filter_subsystem {
+ FILTER_SUBSYSTEM_SMTP_IN = 1<<0,
+ FILTER_SUBSYSTEM_SMTP_OUT = 1<<1,
+};
+
+struct filter_proc {
const char *command;
const char *user;
const char *group;
const char *chroot;
int errfd;
-};
-
-enum filter_type {
- FILTER_TYPE_BUILTIN,
- FILTER_TYPE_PROC,
- FILTER_TYPE_CHAIN,
+ enum filter_subsystem filter_subsystem;
};
struct filter_config {
char *name;
+ enum filter_subsystem filter_subsystem;
enum filter_type filter_type;
enum filter_phase phase;
char *reject;
@@ -1056,6 +1064,7 @@ struct filter_config {
char *rewrite;
char *report;
uint8_t junk;
+ uint8_t bypass;
char *proc;
const char **chain;
@@ -1163,6 +1172,8 @@ struct dispatcher_remote {
char *mail_from;
char *smarthost;
+ int smarthost_domain;
+
char *auth;
int tls_required;
int tls_noverify;
@@ -1170,6 +1181,8 @@ struct dispatcher_remote {
int backup;
char *backupmx;
+ char *filtername;
+
int srs;
};
@@ -1335,7 +1348,7 @@ int lka(void);
/* lka_proc.c */
int lka_proc_ready(void);
-void lka_proc_forked(const char *, int);
+void lka_proc_forked(const char *, uint32_t, int);
void lka_proc_errfd(const char *, int);
struct io *lka_proc_get_io(const char *);
@@ -1374,7 +1387,7 @@ void lka_filter_init(void);
void lka_filter_register_hook(const char *, const char *);
void lka_filter_ready(void);
int lka_filter_proc_in_session(uint64_t, const char *);
-void lka_filter_begin(uint64_t, const char *, const struct sockaddr_storage *, const struct sockaddr_storage *, const char *, int);
+void lka_filter_begin(uint64_t, const char *);
void lka_filter_end(uint64_t);
void lka_filter_protocol(uint64_t, enum filter_phase, const char *);
void lka_filter_data_begin(uint64_t);
diff --git a/smtpd/smtpd/Makefile b/smtpd/smtpd/Makefile
index eaa03696..7caacd0d 100644
--- a/smtpd/smtpd/Makefile
+++ b/smtpd/smtpd/Makefile
@@ -24,8 +24,6 @@ SRCS+= ioev.c
SRCS+= limit.c
SRCS+= lka.c
SRCS+= lka_filter.c
-SRCS+= lka_proc.c
-SRCS+= lka_report.c
SRCS+= lka_session.c
SRCS+= log.c
SRCS+= mailaddr.c
diff --git a/smtpd/spfwalk.c b/smtpd/spfwalk.c
index b643552d..97104eb9 100644
--- a/smtpd/spfwalk.c
+++ b/smtpd/spfwalk.c
@@ -28,27 +28,36 @@
#include <err.h>
#include <errno.h>
#include <event.h>
+#include <imsg.h>
+#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>
-#define LINE_MAX 1024
#include "smtpd-defines.h"
#include "smtpd-api.h"
#include "unpack_dns.h"
#include "parser.h"
+struct target {
+ void (*dispatch)(struct dns_rr *, struct target *);
+ int cidr4;
+ int cidr6;
+};
+
int spfwalk(int, struct parameter *);
-static void dispatch_txt(struct dns_rr *);
-static void dispatch_mx(struct dns_rr *);
-static void dispatch_a(struct dns_rr *);
-static void dispatch_aaaa(struct dns_rr *);
-static void lookup_record(int, const char *, void (*)(struct dns_rr *));
+static void dispatch_txt(struct dns_rr *, struct target *);
+static void dispatch_mx(struct dns_rr *, struct target *);
+static void dispatch_a(struct dns_rr *, struct target *);
+static void dispatch_aaaa(struct dns_rr *, struct target *);
+static void lookup_record(int, const char *, struct target *);
static void dispatch_record(struct asr_result *, void *);
static ssize_t parse_txt(const char *, size_t, char *, size_t);
+static int parse_target(char *, struct target *);
+void *xmalloc(size_t size);
int ip_v4 = 0;
int ip_v6 = 0;
@@ -59,6 +68,7 @@ struct dict seen;
int
spfwalk(int argc, struct parameter *argv)
{
+ struct target tgt;
const char *ip_family = NULL;
char *line = NULL;
size_t linesize = 0;
@@ -81,12 +91,15 @@ spfwalk(int argc, struct parameter *argv)
dict_init(&seen);
event_init();
+ tgt.cidr4 = tgt.cidr6 = -1;
+ tgt.dispatch = dispatch_txt;
+
while ((linelen = getline(&line, &linesize, stdin)) != -1) {
while (linelen-- > 0 && isspace(line[linelen]))
line[linelen] = '\0';
if (linelen > 0)
- lookup_record(T_TXT, line, dispatch_txt);
+ lookup_record(T_TXT, line, &tgt);
}
free(line);
@@ -100,20 +113,23 @@ spfwalk(int argc, struct parameter *argv)
}
void
-lookup_record(int type, const char *record, void (*cb)(struct dns_rr *))
+lookup_record(int type, const char *record, struct target *tgt)
{
struct asr_query *as;
+ struct target *ntgt;
as = res_query_async(record, C_IN, type, NULL);
if (as == NULL)
err(1, "res_query_async");
- event_asr_run(as, dispatch_record, cb);
+ ntgt = xmalloc(sizeof(*ntgt));
+ *ntgt = *tgt;
+ event_asr_run(as, dispatch_record, (void *)ntgt);
}
void
dispatch_record(struct asr_result *ar, void *arg)
{
- void (*cb)(struct dns_rr *) = arg;
+ struct target *tgt = arg;
struct unpack pack;
struct dns_header h;
struct dns_query q;
@@ -121,7 +137,7 @@ dispatch_record(struct asr_result *ar, void *arg)
/* best effort */
if (ar->ar_h_errno && ar->ar_h_errno != NO_DATA)
- return;
+ goto end;
unpack_init(&pack, ar->ar_data, ar->ar_datalen);
unpack_header(&pack, &h);
@@ -130,20 +146,23 @@ dispatch_record(struct asr_result *ar, void *arg)
for (; h.ancount; h.ancount--) {
unpack_rr(&pack, &rr);
/**/
- cb(&rr);
+ tgt->dispatch(&rr, tgt);
}
+end:
+ free(tgt);
}
void
-dispatch_txt(struct dns_rr *rr)
+dispatch_txt(struct dns_rr *rr, struct target *tgt)
{
+ char buf[4096];
+ char *argv[512];
+ char buf2[512];
+ struct target ltgt;
struct in6_addr ina;
- char buf[4096];
- char buf2[512];
- char *in = buf;
- char *argv[512];
- char **ap = argv;
- char *end;
+ char **ap = argv;
+ char *in = buf;
+ char *record, *end;
ssize_t n;
if (rr->rr_type != T_TXT)
@@ -173,6 +192,8 @@ dispatch_txt(struct dns_rr *rr)
if (**ap == '+' || **ap == '?')
(*ap)++;
+ ltgt.cidr4 = ltgt.cidr6 = -1;
+
if (strncasecmp("ip4:", *ap, 4) == 0) {
if ((ip_v4 == 1 || ip_both == 1) &&
inet_net_pton(AF_INET, *(ap) + 4,
@@ -190,35 +211,50 @@ dispatch_txt(struct dns_rr *rr)
if (strcasecmp("a", *ap) == 0) {
print_dname(rr->rr_dname, buf2, sizeof(buf2));
buf2[strlen(buf2) - 1] = '\0';
- lookup_record(T_A, buf2, dispatch_a);
- lookup_record(T_AAAA, buf2, dispatch_aaaa);
+ ltgt.dispatch = dispatch_a;
+ lookup_record(T_A, buf2, &ltgt);
+ ltgt.dispatch = dispatch_aaaa;
+ lookup_record(T_AAAA, buf2, &ltgt);
continue;
}
if (strncasecmp("a:", *ap, 2) == 0) {
- lookup_record(T_A, *(ap) + 2, dispatch_a);
- lookup_record(T_AAAA, *(ap) + 2, dispatch_aaaa);
+ record = *(ap) + 2;
+ if (parse_target(record, &ltgt) < 0)
+ continue;
+ ltgt.dispatch = dispatch_a;
+ lookup_record(T_A, record, &ltgt);
+ ltgt.dispatch = dispatch_aaaa;
+ lookup_record(T_AAAA, record, &ltgt);
continue;
}
if (strncasecmp("exists:", *ap, 7) == 0) {
- lookup_record(T_A, *(ap) + 7, dispatch_a);
+ ltgt.dispatch = dispatch_a;
+ lookup_record(T_A, *(ap) + 7, &ltgt);
continue;
}
if (strncasecmp("include:", *ap, 8) == 0) {
- lookup_record(T_TXT, *(ap) + 8, dispatch_txt);
+ ltgt.dispatch = dispatch_txt;
+ lookup_record(T_TXT, *(ap) + 8, &ltgt);
continue;
}
if (strncasecmp("redirect=", *ap, 9) == 0) {
- lookup_record(T_TXT, *(ap) + 9, dispatch_txt);
+ ltgt.dispatch = dispatch_txt;
+ lookup_record(T_TXT, *(ap) + 9, &ltgt);
continue;
}
if (strcasecmp("mx", *ap) == 0) {
print_dname(rr->rr_dname, buf2, sizeof(buf2));
buf2[strlen(buf2) - 1] = '\0';
- lookup_record(T_MX, buf2, dispatch_mx);
+ ltgt.dispatch = dispatch_mx;
+ lookup_record(T_MX, buf2, &ltgt);
continue;
}
- if (strncasecmp("mx:", *ap, 2) == 0) {
- lookup_record(T_MX, *(ap) + 2, dispatch_mx);
+ if (strncasecmp("mx:", *ap, 3) == 0) {
+ record = *(ap) + 3;
+ if (parse_target(record, &ltgt) < 0)
+ continue;
+ ltgt.dispatch = dispatch_mx;
+ lookup_record(T_MX, record, &ltgt);
continue;
}
}
@@ -226,9 +262,10 @@ dispatch_txt(struct dns_rr *rr)
}
void
-dispatch_mx(struct dns_rr *rr)
+dispatch_mx(struct dns_rr *rr, struct target *tgt)
{
char buf[512];
+ struct target ltgt;
if (rr->rr_type != T_MX)
return;
@@ -237,12 +274,16 @@ dispatch_mx(struct dns_rr *rr)
buf[strlen(buf) - 1] = '\0';
if (buf[strlen(buf) - 1] == '.')
buf[strlen(buf) - 1] = '\0';
- lookup_record(T_A, buf, dispatch_a);
- lookup_record(T_AAAA, buf, dispatch_aaaa);
+
+ ltgt = *tgt;
+ ltgt.dispatch = dispatch_a;
+ lookup_record(T_A, buf, &ltgt);
+ ltgt.dispatch = dispatch_aaaa;
+ lookup_record(T_AAAA, buf, &ltgt);
}
void
-dispatch_a(struct dns_rr *rr)
+dispatch_a(struct dns_rr *rr, struct target *tgt)
{
char buffer[512];
const char *ptr;
@@ -251,12 +292,16 @@ dispatch_a(struct dns_rr *rr)
return;
if ((ptr = inet_ntop(AF_INET, &rr->rr.in_a.addr,
- buffer, sizeof buffer)))
- printf("%s\n", ptr);
+ buffer, sizeof buffer))) {
+ if (tgt->cidr4 >= 0)
+ printf("%s/%d\n", ptr, tgt->cidr4);
+ else
+ printf("%s\n", ptr);
+ }
}
void
-dispatch_aaaa(struct dns_rr *rr)
+dispatch_aaaa(struct dns_rr *rr, struct target *tgt)
{
char buffer[512];
const char *ptr;
@@ -265,11 +310,15 @@ dispatch_aaaa(struct dns_rr *rr)
return;
if ((ptr = inet_ntop(AF_INET6, &rr->rr.in_aaaa.addr6,
- buffer, sizeof buffer)))
- printf("%s\n", ptr);
+ buffer, sizeof buffer))) {
+ if (tgt->cidr6 >= 0)
+ printf("%s/%d\n", ptr, tgt->cidr6);
+ else
+ printf("%s\n", ptr);
+ }
}
-static ssize_t
+ssize_t
parse_txt(const char *rdata, size_t rdatalen, char *dst, size_t dstsz)
{
size_t len;
@@ -303,3 +352,33 @@ parse_txt(const char *rdata, size_t rdatalen, char *dst, size_t dstsz)
return r;
}
+
+int
+parse_target(char *record, struct target *tgt)
+{
+ const char *err;
+ char *m4, *m6;
+
+ m4 = record;
+ strsep(&m4, "/");
+ if (m4 == NULL)
+ return 0;
+
+ m6 = m4;
+ strsep(&m6, "/");
+
+ if (*m4) {
+ tgt->cidr4 = strtonum(m4, 0, 32, &err);
+ if (err)
+ return tgt->cidr4 = -1;
+ }
+
+ if (m6 == NULL)
+ return 0;
+
+ tgt->cidr6 = strtonum(m6, 0, 128, &err);
+ if (err)
+ return tgt->cidr6 = -1;
+
+ return 0;
+}
diff --git a/smtpd/to.c b/smtpd/to.c
index fffeb59d..6b35804e 100644
--- a/smtpd/to.c
+++ b/smtpd/to.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: to.c,v 1.43 2019/09/19 16:00:59 gilles Exp $ */
+/* $OpenBSD: to.c,v 1.44 2019/11/12 20:21:46 gilles Exp $ */
/*
* Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
@@ -477,7 +477,7 @@ rule_to_text(struct rule *r)
(void)strlcat(buf, " !", sizeof buf);
if (r->flag_from_socket)
(void)strlcat(buf, " from socket", sizeof buf);
- if (r->flag_from_rdns) {
+ else if (r->flag_from_rdns) {
(void)strlcat(buf, " from rdns", sizeof buf);
if (r->table_from) {
(void)strlcat(buf, " ", sizeof buf);