summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGilles Chehade <gilles@cvs.openbsd.org>2011-08-27 22:32:43 +0000
committerGilles Chehade <gilles@cvs.openbsd.org>2011-08-27 22:32:43 +0000
commite5d3ea52670446cab24de04bb472a473cf0de2a4 (patch)
tree9c0b8035e5bd6edf55a7449e16e4bafe381f9148
parent1d2663c2aae172f35137c1fc6ca4f60fe6905c2e (diff)
initial support for a session-time filtering API
currently only HELO/EHLO, MAIL, RCPT are supported, however ... I have voluntarily disabled filters at smtpd.conf level so people don't play with it until the API has stabilized a bit discussed with several people in private, no one opposed the feature
-rw-r--r--usr.sbin/smtpd/filter.c252
-rw-r--r--usr.sbin/smtpd/filter.h91
-rw-r--r--usr.sbin/smtpd/lka_session.c3
-rw-r--r--usr.sbin/smtpd/mfa.c139
-rw-r--r--usr.sbin/smtpd/mfa_session.c308
-rw-r--r--usr.sbin/smtpd/parse.y76
-rw-r--r--usr.sbin/smtpd/smtp.c7
-rw-r--r--usr.sbin/smtpd/smtp_session.c67
-rw-r--r--usr.sbin/smtpd/smtpd.c65
-rw-r--r--usr.sbin/smtpd/smtpd.h48
-rw-r--r--usr.sbin/smtpd/smtpd/Makefile4
-rw-r--r--usr.sbin/smtpd/smtpfilter.c125
12 files changed, 1055 insertions, 130 deletions
diff --git a/usr.sbin/smtpd/filter.c b/usr.sbin/smtpd/filter.c
new file mode 100644
index 00000000000..45f6f4fa084
--- /dev/null
+++ b/usr.sbin/smtpd/filter.c
@@ -0,0 +1,252 @@
+/* $OpenBSD: filter.c,v 1.1 2011/08/27 22:32:41 gilles Exp $ */
+
+/*
+ * Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+
+#include <err.h>
+#include <event.h>
+#include <fcntl.h>
+#include <imsg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "filter.h"
+
+static struct filter_internals {
+ struct event ev;
+ struct imsgbuf ibuf;
+
+ int (*helo_cb)(u_int64_t, struct filter_helo *, void *);
+ void *helo_cb_arg;
+
+ int (*ehlo_cb)(u_int64_t, struct filter_helo *, void *);
+ void *ehlo_cb_arg;
+
+ int (*mail_cb)(u_int64_t, struct filter_mail *, void *);
+ void *mail_cb_arg;
+
+ int (*rcpt_cb)(u_int64_t, struct filter_rcpt *, void *);
+ void *rcpt_cb_arg;
+
+ int (*data_cb)(u_int64_t, struct filter_data *, void *);
+ void *data_cb_arg;
+
+ int (*quit_cb)(u_int64_t, struct filter_quit *, void *);
+ void *quit_cb_arg;
+} fi;
+
+static void filter_handler(int, short, void *);
+static void filter_register_callback(enum filter_type, void *, void *);
+
+void
+filter_init(void)
+{
+ bzero(&fi, sizeof (fi));
+
+ imsg_init(&fi.ibuf, 0);
+
+ event_init();
+ event_set(&fi.ev, 0, EV_READ, filter_handler, (void *)&fi);
+ event_add(&fi.ev, NULL);
+}
+
+void
+filter_loop(void)
+{
+ if (event_dispatch() < 0)
+ errx(1, "event_dispatch");
+}
+
+void
+filter_register_helo_callback(int (*cb)(u_int64_t, struct filter_helo *, void *), void *cb_arg)
+{
+ filter_register_callback(FILTER_HELO, cb, cb_arg);
+}
+
+void
+filter_register_ehlo_callback(int (*cb)(u_int64_t, struct filter_helo *, void *), void *cb_arg)
+{
+ filter_register_callback(FILTER_EHLO, cb, cb_arg);
+}
+
+void
+filter_register_mail_callback(int (*cb)(u_int64_t, struct filter_mail *, void *), void *cb_arg)
+{
+ filter_register_callback(FILTER_MAIL, cb, cb_arg);
+}
+
+void
+filter_register_rcpt_callback(int (*cb)(u_int64_t, struct filter_rcpt *, void *), void *cb_arg)
+{
+ filter_register_callback(FILTER_RCPT, cb, cb_arg);
+}
+
+void
+filter_register_data_callback(int (*cb)(u_int64_t, struct filter_data *, void *), void *cb_arg)
+{
+ filter_register_callback(FILTER_DATA, cb, cb_arg);
+}
+
+void
+filter_register_quit_callback(int (*cb)(u_int64_t, struct filter_quit *, void *), void *cb_arg)
+{
+ filter_register_callback(FILTER_QUIT, cb, cb_arg);
+}
+
+static void
+filter_register_callback(enum filter_type type, void *cb, void *cb_arg)
+{
+ switch (type) {
+ case FILTER_HELO:
+ fi.helo_cb = cb;
+ fi.helo_cb_arg = cb_arg;
+ break;
+
+ case FILTER_EHLO:
+ fi.ehlo_cb = cb;
+ fi.ehlo_cb_arg = cb_arg;
+ break;
+
+ case FILTER_MAIL:
+ fi.mail_cb = cb;
+ fi.mail_cb_arg = cb_arg;
+ break;
+
+ case FILTER_RCPT:
+ fi.rcpt_cb = cb;
+ fi.rcpt_cb_arg = cb_arg;
+ break;
+
+ case FILTER_DATA:
+ fi.data_cb = cb;
+ fi.data_cb_arg = cb_arg;
+ break;
+
+ case FILTER_QUIT:
+ fi.quit_cb = cb;
+ fi.quit_cb_arg = cb_arg;
+ break;
+ }
+}
+
+static void
+filter_handler(int fd, short event, void *p)
+{
+ struct imsg imsg;
+ ssize_t n;
+ short evflags = EV_READ;
+ struct filter_msg fm;
+
+ if (event & EV_READ) {
+ n = imsg_read(&fi.ibuf);
+ if (n == -1)
+ err(1, "imsg_read");
+ if (n == 0) {
+ event_del(&fi.ev);
+ event_loopexit(NULL);
+ return;
+ }
+ }
+
+ if (event & EV_WRITE) {
+ if (msgbuf_write(&fi.ibuf.w) == -1)
+ err(1, "msgbuf_write");
+ if (fi.ibuf.w.queued)
+ evflags |= EV_WRITE;
+ }
+
+ for (;;) {
+ n = imsg_get(&fi.ibuf, &imsg);
+ if (n == -1)
+ errx(1, "imsg_get");
+ if (n == 0)
+ break;
+
+ if ((imsg.hdr.len - IMSG_HEADER_SIZE)
+ != sizeof(fm))
+ errx(1, "corrupted imsg");
+
+ memcpy(&fm, imsg.data, sizeof (fm));
+ if (fm.version != FILTER_API_VERSION)
+ errx(1, "API version mismatch");
+
+ switch (imsg.hdr.type) {
+ case FILTER_HELO:
+ if (fi.helo_cb == NULL)
+ goto ignore;
+ fm.code = fi.helo_cb(fm.cl_id, &fm.u.helo,
+ fi.helo_cb_arg);
+ break;
+ case FILTER_EHLO:
+ if (fi.ehlo_cb == NULL)
+ goto ignore;
+ fm.code = fi.ehlo_cb(fm.cl_id, &fm.u.helo,
+ fi.ehlo_cb_arg);
+ break;
+ case FILTER_MAIL:
+ if (fi.mail_cb == NULL)
+ goto ignore;
+ fm.code = fi.mail_cb(fm.cl_id, &fm.u.mail,
+ fi.mail_cb_arg);
+ break;
+ case FILTER_RCPT:
+ if (fi.rcpt_cb == NULL)
+ goto ignore;
+ fm.code = fi.rcpt_cb(fm.cl_id, &fm.u.rcpt,
+ fi.rcpt_cb_arg);
+ break;
+ case FILTER_DATA:
+ if (fi.data_cb == NULL)
+ goto ignore;
+ fm.code = fi.data_cb(fm.cl_id, &fm.u.data,
+ fi.data_cb_arg);
+ break;
+ case FILTER_QUIT:
+ if (fi.quit_cb == NULL)
+ goto ignore;
+ fm.code = fi.quit_cb(fm.cl_id, &fm.u.quit,
+ fi.quit_cb_arg);
+ break;
+ default:
+ errx(1, "unsupported imsg");
+ }
+
+ if (! fm.code)
+ fm.code = -1;
+
+ imsg_compose(&fi.ibuf, imsg.hdr.type, 0, 0, -1, &fm, sizeof fm);
+ evflags |= EV_WRITE;
+ imsg_free(&imsg);
+ }
+
+ event_set(&fi.ev, 0, evflags, filter_handler, &fi);
+ event_add(&fi.ev, NULL);
+ return;
+
+ignore:
+ imsg_free(&imsg);
+ fm.code = 0;
+ imsg_compose(&fi.ibuf, imsg.hdr.type, 0, 0, -1, &fm, sizeof fm);
+ evflags |= EV_WRITE;
+ event_set(&fi.ev, 0, evflags, filter_handler, &fi);
+ event_add(&fi.ev, NULL);
+}
diff --git a/usr.sbin/smtpd/filter.h b/usr.sbin/smtpd/filter.h
new file mode 100644
index 00000000000..bbd5cdc59ab
--- /dev/null
+++ b/usr.sbin/smtpd/filter.h
@@ -0,0 +1,91 @@
+/* $OpenBSD: filter.h,v 1.1 2011/08/27 22:32:41 gilles Exp $ */
+
+/*
+ * Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#define FILTER_API_VERSION 50
+
+#if !defined(MAX_LINE_SIZE)
+#define MAX_LINE_SIZE 1024
+#endif
+
+#if !defined(MAX_LOCALPART_SIZE)
+#define MAX_LOCALPART_SIZE 128
+#endif
+
+#if !defined(MAX_DOMAINPART_SIZE)
+#define MAX_DOMAINPART_SIZE (MAX_LINE_SIZE-MAX_LOCALPART_SIZE)
+#endif
+
+enum filter_type {
+ FILTER_HELO,
+ FILTER_EHLO,
+ FILTER_MAIL,
+ FILTER_RCPT,
+ FILTER_DATA,
+ FILTER_QUIT,
+};
+
+struct filter_helo {
+ char buffer[1024];
+};
+
+struct filter_mail {
+ char user[MAX_LOCALPART_SIZE];
+ char domain[MAX_DOMAINPART_SIZE];
+};
+
+struct filter_rcpt {
+ char user[MAX_LOCALPART_SIZE];
+ char domain[MAX_DOMAINPART_SIZE];
+};
+
+struct filter_data {
+ char data[4096];
+};
+
+struct filter_quit {
+ char data[4096];
+};
+
+union filter_union {
+ struct filter_helo helo;
+ struct filter_mail mail;
+ struct filter_rcpt rcpt;
+ struct filter_data data;
+ struct filter_quit quit;
+};
+
+struct filter_msg {
+ u_int64_t id; /* set by smtpd(8) */
+ u_int64_t cl_id; /* set by smtpd(8) */
+ int8_t code;
+ u_int8_t version;
+ enum filter_type type;
+ union filter_union u;
+};
+
+/**/
+void filter_init(void);
+void filter_loop(void);
+
+void filter_register_helo_callback(int (*)(u_int64_t, struct filter_helo *, void *), void *);
+void filter_register_ehlo_callback(int (*)(u_int64_t, struct filter_helo *, void *), void *);
+void filter_register_mail_callback(int (*)(u_int64_t, struct filter_mail *, void *), void *);
+void filter_register_rcpt_callback(int (*)(u_int64_t, struct filter_rcpt *, void *), void *);
+void filter_register_data_callback(int (*)(u_int64_t, struct filter_data *, void *), void *);
+void filter_register_quit_callback(int (*)(u_int64_t, struct filter_quit *, void *), void *);
diff --git a/usr.sbin/smtpd/lka_session.c b/usr.sbin/smtpd/lka_session.c
index 1f7c202040f..a3cd1a0ccd2 100644
--- a/usr.sbin/smtpd/lka_session.c
+++ b/usr.sbin/smtpd/lka_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka_session.c,v 1.8 2011/07/04 19:44:31 gilles Exp $ */
+/* $OpenBSD: lka_session.c,v 1.9 2011/08/27 22:32:41 gilles Exp $ */
/*
* Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org>
@@ -651,5 +651,4 @@ lka_session_cmp(struct lka_session *s1, struct lka_session *s2)
return 0;
}
-
SPLAY_GENERATE(lkatree, lka_session, nodes, lka_session_cmp);
diff --git a/usr.sbin/smtpd/mfa.c b/usr.sbin/smtpd/mfa.c
index a0ddf14cc20..c6696de8bb1 100644
--- a/usr.sbin/smtpd/mfa.c
+++ b/usr.sbin/smtpd/mfa.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: mfa.c,v 1.59 2011/05/16 21:05:52 gilles Exp $ */
+/* $OpenBSD: mfa.c,v 1.60 2011/08/27 22:32:41 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -18,14 +18,17 @@
*/
#include <sys/types.h>
+#include <sys/wait.h>
#include <sys/queue.h>
#include <sys/tree.h>
#include <sys/param.h>
#include <sys/socket.h>
+#include <errno.h>
#include <event.h>
#include <imsg.h>
#include <pwd.h>
+#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -37,20 +40,27 @@
static void mfa_imsg(struct imsgev *, struct imsg *);
static void mfa_shutdown(void);
static void mfa_sig_handler(int, short, void *);
+static void mfa_test_helo(struct envelope *);
static void mfa_test_mail(struct envelope *);
static void mfa_test_rcpt(struct envelope *);
static void mfa_test_rcpt_resume(struct submit_status *);
static int mfa_strip_source_route(char *, size_t);
+static int mfa_fork_filter(struct filter *);
+void mfa_session(struct submit_status *, enum session_state);
static void
mfa_imsg(struct imsgev *iev, struct imsg *imsg)
{
+ struct filter *filter;
+
if (iev->proc == PROC_SMTP) {
switch (imsg->hdr.type) {
+ case IMSG_MFA_HELO:
+ mfa_test_helo(imsg->data);
+ return;
case IMSG_MFA_MAIL:
mfa_test_mail(imsg->data);
return;
-
case IMSG_MFA_RCPT:
mfa_test_rcpt(imsg->data);
return;
@@ -74,6 +84,29 @@ mfa_imsg(struct imsgev *iev, struct imsg *imsg)
if (iev->proc == PROC_PARENT) {
switch (imsg->hdr.type) {
+ case IMSG_CONF_START:
+ env->sc_filters = calloc(1, sizeof *env->sc_filters);
+ if (env->sc_filters == NULL)
+ fatal(NULL);
+ TAILQ_INIT(env->sc_filters);
+ return;
+
+ case IMSG_CONF_FILTER:
+ filter = calloc(1, sizeof *filter);
+ if (filter == NULL)
+ fatal(NULL);
+ memcpy(filter, (struct filter *)imsg->data, sizeof (*filter));
+ TAILQ_INSERT_TAIL(env->sc_filters, filter, f_entry);
+ return;
+
+ case IMSG_CONF_END:
+ TAILQ_FOREACH(filter, env->sc_filters, f_entry) {
+ log_info("forking filter: %s", filter->name);
+ if (! mfa_fork_filter(filter))
+ fatalx("could not fork filter");
+ }
+ return;
+
case IMSG_CTL_VERBOSE:
log_verbose(*(int *)imsg->data);
return;
@@ -91,6 +124,11 @@ mfa_sig_handler(int sig, short event, void *p)
case SIGTERM:
mfa_shutdown();
break;
+
+ case SIGCHLD:
+ fatalx("unexpected SIGCHLD");
+ break;
+
default:
fatalx("mfa_sig_handler: unexpected signal");
}
@@ -99,6 +137,17 @@ mfa_sig_handler(int sig, short event, void *p)
static void
mfa_shutdown(void)
{
+ pid_t pid;
+ struct filter *filter;
+
+ TAILQ_FOREACH(filter, env->sc_filters, f_entry) {
+ kill(filter->pid, SIGTERM);
+ }
+
+ do {
+ pid = waitpid(WAIT_MYPGRP, NULL, 0);
+ } while (pid != -1 || (pid == -1 && errno == EINTR));
+
log_info("mail filter exiting");
_exit(0);
}
@@ -112,6 +161,7 @@ mfa(void)
struct event ev_sigint;
struct event ev_sigterm;
+ struct event ev_sigchld;
struct peer peers[] = {
{ PROC_PARENT, imsg_dispatch },
@@ -131,13 +181,11 @@ mfa(void)
purge_config(PURGE_EVERYTHING);
+ if ((env->sc_pw = getpwnam(SMTPD_FILTER_USER)) == NULL)
+ if ((env->sc_pw = getpwnam(SMTPD_USER)) == NULL)
+ fatalx("unknown user " SMTPD_FILTER_USER);
pw = env->sc_pw;
- if (chroot(pw->pw_dir) == -1)
- fatal("mfa: chroot");
- if (chdir("/") == -1)
- fatal("mfa: chdir(\"/\")");
-
smtpd_process = PROC_MFA;
setproctitle("%s", env->sc_title[smtpd_process]);
@@ -149,10 +197,15 @@ mfa(void)
imsg_callback = mfa_imsg;
event_init();
+ SPLAY_INIT(&env->mfa_sessions);
+ TAILQ_INIT(env->sc_filters);
+
signal_set(&ev_sigint, SIGINT, mfa_sig_handler, NULL);
signal_set(&ev_sigterm, SIGTERM, mfa_sig_handler, NULL);
+ signal_set(&ev_sigchld, SIGCHLD, mfa_sig_handler, NULL);
signal_add(&ev_sigint, NULL);
signal_add(&ev_sigterm, NULL);
+ signal_add(&ev_sigchld, NULL);
signal(SIGPIPE, SIG_IGN);
signal(SIGHUP, SIG_IGN);
@@ -166,7 +219,20 @@ mfa(void)
return (0);
}
-void
+static void
+mfa_test_helo(struct envelope *e)
+{
+ struct submit_status ss;
+
+ ss.id = e->session_id;
+ ss.code = 530;
+ ss.envelope = *e;
+
+ mfa_session(&ss, S_HELO);
+ return;
+}
+
+static void
mfa_test_mail(struct envelope *e)
{
struct submit_status ss;
@@ -187,18 +253,13 @@ mfa_test_mail(struct envelope *e)
goto refuse;
}
- /* Current policy is to allow all well-formed addresses. */
- goto accept;
+ mfa_session(&ss, S_MAIL_MFA);
+ return;
refuse:
imsg_compose_event(env->sc_ievs[PROC_SMTP], IMSG_MFA_MAIL, 0, 0, -1, &ss,
sizeof(ss));
return;
-
-accept:
- ss.code = 250;
- imsg_compose_event(env->sc_ievs[PROC_LKA], IMSG_LKA_MAIL, 0,
- 0, -1, &ss, sizeof(ss));
}
static void
@@ -220,10 +281,9 @@ mfa_test_rcpt(struct envelope *e)
! valid_domainpart(ss.u.maddr.domain))
goto refuse;
- imsg_compose_event(env->sc_ievs[PROC_LKA], IMSG_LKA_RULEMATCH, 0, 0, -1,
- &ss, sizeof(ss));
-
+ mfa_session(&ss, S_RCPT_MFA);
return;
+
refuse:
imsg_compose_event(env->sc_ievs[PROC_SMTP], IMSG_MFA_RCPT, 0, 0, -1, &ss,
sizeof(ss));
@@ -257,3 +317,46 @@ mfa_strip_source_route(char *buf, size_t len)
return 0;
}
+
+static int
+mfa_fork_filter(struct filter *filter)
+{
+ pid_t pid;
+ int sockpair[2];
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sockpair) < 0)
+ return 0;
+
+ session_socket_blockmode(sockpair[0], BM_NONBLOCK);
+ session_socket_blockmode(sockpair[1], BM_NONBLOCK);
+
+ filter->ibuf = calloc(1, sizeof(struct imsgbuf));
+ if (filter->ibuf == NULL)
+ goto err;
+
+ pid = fork();
+ if (pid == -1)
+ goto err;
+
+ if (pid == 0) {
+ /* filter */
+ dup2(sockpair[0], 0);
+
+ closefrom(3);
+
+ execl(filter->path, filter->name, NULL);
+ exit(1);
+ }
+
+ /* in parent */
+ close(sockpair[0]);
+ imsg_init(filter->ibuf, sockpair[1]);
+
+ return 1;
+
+err:
+ free(filter->ibuf);
+ close(sockpair[0]);
+ close(sockpair[1]);
+ return 0;
+}
diff --git a/usr.sbin/smtpd/mfa_session.c b/usr.sbin/smtpd/mfa_session.c
new file mode 100644
index 00000000000..6b1228e6433
--- /dev/null
+++ b/usr.sbin/smtpd/mfa_session.c
@@ -0,0 +1,308 @@
+/* $OpenBSD: mfa_session.c,v 1.1 2011/08/27 22:32:41 gilles Exp $ */
+
+/*
+ * Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <event.h>
+#include <imsg.h>
+#include <resolv.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "smtpd.h"
+#include "log.h"
+
+void mfa_session(struct submit_status *, enum session_state);
+struct mfa_session *mfa_session_init(struct submit_status *, enum session_state);
+struct mfa_session *mfa_session_find(u_int64_t);
+struct mfa_session *mfa_session_xfind(u_int64_t);
+int mfa_session_proceed(struct mfa_session *);
+void mfa_session_pickup(struct mfa_session *);
+void mfa_session_fail(struct mfa_session *);
+void mfa_session_destroy(struct mfa_session *);
+void mfa_session_done(struct mfa_session *);
+
+void mfa_session_imsg(int, short, void *);
+
+void
+mfa_session(struct submit_status *ss, enum session_state state)
+{
+ struct mfa_session *ms;
+
+ ms = mfa_session_init(ss, state);
+ if (ms->filter == NULL) {
+ mfa_session_done(ms);
+ return;
+ }
+ if (! mfa_session_proceed(ms))
+ mfa_session_fail(ms);
+}
+
+struct mfa_session *
+mfa_session_init(struct submit_status *ss, enum session_state state)
+{
+ struct mfa_session *ms;
+
+ ms = calloc(1, sizeof(*ms));
+ if (ms == NULL)
+ fatal("mfa_session_init: calloc");
+
+ ms->id = generate_uid();
+ ms->ss = *ss;
+ ms->ss.code = 250;
+ ms->state = state;
+ ms->filter = TAILQ_FIRST(env->sc_filters);
+
+ SPLAY_INSERT(mfatree, &env->mfa_sessions, ms);
+
+ return ms;
+}
+
+
+int
+mfa_session_proceed(struct mfa_session *ms)
+{
+ struct filter_msg fm;
+
+ fm.id = ms->id;
+ fm.cl_id = ms->ss.id;
+ fm.version = FILTER_API_VERSION;
+
+ switch (ms->state) {
+ case S_HELO:
+ fm.type = FILTER_HELO;
+ if (strlcpy(fm.u.helo.buffer, ms->ss.envelope.delivery.helo,
+ sizeof(fm.u.helo.buffer)) >= sizeof(fm.u.helo.buffer))
+ fatalx("mfa_session_proceed: HELO: truncation");
+ break;
+
+ case S_MAIL_MFA:
+ fm.type = FILTER_MAIL;
+ if (strlcpy(fm.u.mail.user, ms->ss.u.maddr.user,
+ sizeof(ms->ss.u.maddr.user)) >= sizeof(ms->ss.u.maddr.user))
+ fatalx("mfa_session_proceed: MAIL: user truncation");
+ if (strlcpy(fm.u.mail.domain, ms->ss.u.maddr.domain,
+ sizeof(ms->ss.u.maddr.domain)) >= sizeof(ms->ss.u.maddr.domain))
+ fatalx("mfa_session_proceed: MAIL: domain truncation");
+ break;
+
+ case S_RCPT_MFA:
+ fm.type = FILTER_RCPT;
+ if (strlcpy(fm.u.mail.user, ms->ss.u.maddr.user,
+ sizeof(ms->ss.u.maddr.user)) >= sizeof(ms->ss.u.maddr.user))
+ fatalx("mfa_session_proceed: RCPT: user truncation");
+ if (strlcpy(fm.u.mail.domain, ms->ss.u.maddr.domain,
+ sizeof(ms->ss.u.maddr.domain)) >= sizeof(ms->ss.u.maddr.domain))
+ fatalx("mfa_session_proceed: RCPT: domain truncation");
+ break;
+
+ default:
+ fatalx("mfa_session_proceed: no such state");
+ }
+
+ imsg_compose(ms->filter->ibuf, fm.type, 0, 0, -1,
+ &fm, sizeof(fm));
+ event_set(&ms->filter->ev, ms->filter->ibuf->fd, EV_READ|EV_WRITE, mfa_session_imsg, ms->filter);
+ event_add(&ms->filter->ev, NULL);
+ return 1;
+}
+
+void
+mfa_session_pickup(struct mfa_session *ms)
+{
+ if (ms->fm.code == -1) {
+ mfa_session_fail(ms);
+ return;
+ }
+
+ ms->filter = TAILQ_NEXT(ms->filter, f_entry);
+ if (ms->filter == NULL)
+ mfa_session_done(ms);
+ else
+ mfa_session_proceed(ms);
+}
+
+void
+mfa_session_done(struct mfa_session *ms)
+{
+ enum imsg_type imsg_type;
+
+ switch (ms->state) {
+ case S_HELO:
+ imsg_type = IMSG_MFA_HELO;
+ break;
+ case S_MAIL_MFA:
+ if (ms->ss.code != 530) {
+ imsg_compose_event(env->sc_ievs[PROC_LKA], IMSG_LKA_MAIL, 0,
+ 0, -1, &ms->ss, sizeof(ms->ss));
+ mfa_session_destroy(ms);
+ return;
+ }
+ imsg_type = IMSG_MFA_MAIL;
+ break;
+ case S_RCPT_MFA:
+ if (ms->ss.code != 530) {
+ imsg_compose_event(env->sc_ievs[PROC_LKA], IMSG_LKA_RULEMATCH,
+ 0, 0, -1, &ms->ss, sizeof(ms->ss));
+ mfa_session_destroy(ms);
+ return;
+ }
+ imsg_type = IMSG_MFA_RCPT;
+ break;
+ default:
+ fatalx("mfa_session_done: unsupported state");
+ }
+
+ imsg_compose_event(env->sc_ievs[PROC_SMTP], imsg_type, 0, 0,
+ -1, &ms->ss, sizeof(struct submit_status));
+ mfa_session_destroy(ms);
+}
+
+struct mfa_session *
+mfa_session_find(u_int64_t id)
+{
+ struct mfa_session key;
+
+ key.id = id;
+ return SPLAY_FIND(mfatree, &env->mfa_sessions, &key);
+}
+
+struct mfa_session *
+mfa_session_xfind(u_int64_t id)
+{
+ struct mfa_session *ms;
+
+ ms = mfa_session_find(id);
+ if (ms == NULL)
+ fatalx("mfa_session_xfind: mfa session missing");
+
+ return ms;
+}
+
+void
+mfa_session_fail(struct mfa_session *ms)
+{
+ ms->ss.code = 530;
+ mfa_session_done(ms);
+}
+
+void
+mfa_session_destroy(struct mfa_session *ms)
+{
+ SPLAY_REMOVE(mfatree, &env->mfa_sessions, ms);
+ free(ms);
+}
+
+void
+mfa_session_imsg(int fd, short event, void *p)
+{
+ struct filter *filter = p;
+ struct mfa_session *ms;
+ struct imsg imsg;
+ ssize_t n;
+ struct filter_msg fm;
+ short evflags = EV_READ;
+
+ if (event & EV_READ) {
+ n = imsg_read(filter->ibuf);
+ if (n == -1)
+ fatal("imsg_read");
+ if (n == 0) {
+ event_del(&filter->ev);
+ event_loopexit(NULL);
+ return;
+ }
+ }
+
+ if (event & EV_WRITE) {
+ if (msgbuf_write(&filter->ibuf->w) == -1)
+ fatal("msgbuf_write");
+ if (filter->ibuf->w.queued)
+ evflags |= EV_WRITE;
+ }
+
+ for (;;) {
+ n = imsg_get(filter->ibuf, &imsg);
+ if (n == -1)
+ fatalx("imsg_get");
+ if (n == 0)
+ break;
+
+ if ((imsg.hdr.len - IMSG_HEADER_SIZE)
+ != sizeof(fm))
+ fatalx("corrupted imsg");
+
+ memcpy(&fm, imsg.data, sizeof (fm));
+ if (fm.version != FILTER_API_VERSION)
+ fatalx("API version mismatch");
+
+ switch (imsg.hdr.type) {
+ case FILTER_HELO:
+ case FILTER_EHLO:
+ case FILTER_MAIL:
+ case FILTER_RCPT:
+ case FILTER_DATA:
+ ms = mfa_session_xfind(fm.id);
+
+ /* overwrite filter code */
+ ms->fm.code = fm.code;
+
+ /* success, overwrite */
+ if (fm.code == 1)
+ ms->fm = fm;
+
+ mfa_session_pickup(ms);
+ break;
+ default:
+ fatalx("unsupported imsg");
+ }
+ imsg_free(&imsg);
+ }
+ event_set(&filter->ev, filter->ibuf->fd, evflags,
+ mfa_session_imsg, filter);
+ event_add(&filter->ev, NULL);
+}
+
+int
+mfa_session_cmp(struct mfa_session *s1, struct mfa_session *s2)
+{
+ /*
+ * do not return u_int64_t's
+ */
+ if (s1->id < s2->id)
+ return -1;
+
+ if (s1->id > s2->id)
+ return 1;
+
+ return 0;
+}
+
+SPLAY_GENERATE(mfatree, mfa_session, nodes, mfa_session_cmp);
diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y
index 4348e8ca63b..921d8027766 100644
--- a/usr.sbin/smtpd/parse.y
+++ b/usr.sbin/smtpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.76 2011/06/09 17:41:52 gilles Exp $ */
+/* $OpenBSD: parse.y,v 1.77 2011/08/27 22:32:41 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -123,7 +123,7 @@ typedef struct {
%token DNS DB PLAIN EXTERNAL DOMAIN CONFIG SOURCE
%token RELAY VIA DELIVER TO MAILDIR MBOX HOSTNAME
%token ACCEPT REJECT INCLUDE NETWORK ERROR MDA FROM FOR
-%token ARROW ENABLE AUTH TLS LOCAL VIRTUAL TAG ALIAS
+%token ARROW ENABLE AUTH TLS LOCAL VIRTUAL TAG ALIAS FILTER
%token <v.string> STRING
%token <v.number> NUMBER
%type <v.map> map
@@ -379,7 +379,39 @@ main : QUEUE INTERVAL interval {
YYERROR;
}
free($2);
+ }/*
+ | FILTER STRING STRING {
+ struct filter *filter;
+ struct filter *tmp;
+
+ filter = calloc(1, sizeof (*filter));
+ if (filter == NULL ||
+ strlcpy(filter->name, $2, sizeof (filter->name))
+ >= sizeof (filter->name) ||
+ strlcpy(filter->path, $3, sizeof (filter->path))
+ >= sizeof (filter->path)) {
+ free(filter);
+ free($2);
+ free($3);
+ YYERROR;
+ }
+
+ TAILQ_FOREACH(tmp, conf->sc_filters, f_entry) {
+ if (strcasecmp(filter->name, tmp->name) == 0)
+ break;
+ }
+ if (tmp == NULL)
+ TAILQ_INSERT_TAIL(conf->sc_filters, filter, f_entry);
+ else {
+ yyerror("ambiguous filter name: %s", filter->name);
+ free($2);
+ free($3);
+ YYERROR;
+ }
+ free($2);
+ free($3);
}
+ */
;
maptype : SINGLE { map->m_type = T_SINGLE; }
@@ -1195,6 +1227,7 @@ lookup(char *s)
{ "enable", ENABLE },
{ "expire", EXPIRE },
{ "external", EXTERNAL },
+ { "filter", FILTER },
{ "for", FOR },
{ "from", FROM },
{ "hash", HASH },
@@ -1561,34 +1594,26 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
conf->sc_maxsize = SIZE_MAX;
- if ((conf->sc_maps = calloc(1, sizeof(*conf->sc_maps))) == NULL) {
- log_warn("cannot allocate memory");
- return (-1);
- }
- if ((conf->sc_rules = calloc(1, sizeof(*conf->sc_rules))) == NULL) {
- log_warn("cannot allocate memory");
- free(conf->sc_maps);
- return (-1);
- }
- if ((conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners))) == NULL) {
- log_warn("cannot allocate memory");
- free(conf->sc_maps);
- free(conf->sc_rules);
- return (-1);
- }
- if ((conf->sc_ssl = calloc(1, sizeof(*conf->sc_ssl))) == NULL) {
- log_warn("cannot allocate memory");
- free(conf->sc_maps);
- free(conf->sc_rules);
- free(conf->sc_listeners);
- return (-1);
- }
- if ((m = calloc(1, sizeof(*m))) == NULL) {
+ conf->sc_maps = calloc(1, sizeof(*conf->sc_maps));
+ conf->sc_rules = calloc(1, sizeof(*conf->sc_rules));
+ conf->sc_listeners = calloc(1, sizeof(*conf->sc_listeners));
+ conf->sc_ssl = calloc(1, sizeof(*conf->sc_ssl));
+ conf->sc_filters = calloc(1, sizeof(*conf->sc_filters));
+ m = calloc(1, sizeof(*m));
+
+ if (conf->sc_maps == NULL ||
+ conf->sc_rules == NULL ||
+ conf->sc_listeners == NULL ||
+ conf->sc_ssl == NULL ||
+ conf->sc_filters == NULL ||
+ m == NULL) {
log_warn("cannot allocate memory");
free(conf->sc_maps);
free(conf->sc_rules);
free(conf->sc_listeners);
free(conf->sc_ssl);
+ free(conf->sc_filters);
+ free(m);
return (-1);
}
@@ -1601,6 +1626,7 @@ parse_config(struct smtpd *x_conf, const char *filename, int opts)
TAILQ_INIT(conf->sc_listeners);
TAILQ_INIT(conf->sc_maps);
TAILQ_INIT(conf->sc_rules);
+ TAILQ_INIT(conf->sc_filters);
SPLAY_INIT(conf->sc_ssl);
SPLAY_INIT(&conf->sc_sessions);
diff --git a/usr.sbin/smtpd/smtp.c b/usr.sbin/smtpd/smtp.c
index b3821026cf6..d3c749f6fb8 100644
--- a/usr.sbin/smtpd/smtp.c
+++ b/usr.sbin/smtpd/smtp.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp.c,v 1.86 2011/05/16 21:05:52 gilles Exp $ */
+/* $OpenBSD: smtp.c,v 1.87 2011/08/27 22:32:41 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -79,9 +79,10 @@ smtp_imsg(struct imsgev *iev, struct imsg *imsg)
if (iev->proc == PROC_MFA) {
switch (imsg->hdr.type) {
- case IMSG_MFA_RCPT:
+ case IMSG_MFA_HELO:
case IMSG_MFA_MAIL:
- log_debug("smtp: got imsg_mfa_mail/rcpt");
+ case IMSG_MFA_RCPT:
+ log_debug("smtp: got imsg_mfa_helo/mail/rcpt/quit");
ss = imsg->data;
s = session_lookup(ss->id);
if (s == NULL)
diff --git a/usr.sbin/smtpd/smtp_session.c b/usr.sbin/smtpd/smtp_session.c
index ba904a04ea4..4351d98311d 100644
--- a/usr.sbin/smtpd/smtp_session.c
+++ b/usr.sbin/smtpd/smtp_session.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtp_session.c,v 1.142 2011/05/16 21:05:52 gilles Exp $ */
+/* $OpenBSD: smtp_session.c,v 1.143 2011/08/27 22:32:41 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -329,12 +329,12 @@ session_rfc5321_helo_handler(struct session *s, char *args)
return 1;
}
+ s->s_msg.session_id = s->s_id;
s->s_state = S_HELO;
s->s_flags &= F_SECURE|F_AUTHENTICATED;
- session_respond(s, "250 %s Hello %s [%s], pleased to meet you",
- env->sc_hostname, args, ss_to_text(&s->s_ss));
-
+ session_imsg(s, PROC_MFA, IMSG_MFA_HELO, 0, 0, -1, &s->s_msg,
+ sizeof(s->s_msg));
return 1;
}
@@ -352,29 +352,14 @@ session_rfc5321_ehlo_handler(struct session *s, char *args)
return 1;
}
+ s->s_msg.session_id = s->s_id;
s->s_state = S_HELO;
s->s_flags &= F_SECURE|F_AUTHENTICATED;
s->s_flags |= F_EHLO;
s->s_flags |= F_8BITMIME;
- session_respond(s, "250-%s Hello %s [%s], pleased to meet you",
- env->sc_hostname, args, ss_to_text(&s->s_ss));
-
- /* unconditionnal extensions go first */
- session_respond(s, "250-8BITMIME");
- session_respond(s, "250-ENHANCEDSTATUSCODES");
-
- /* XXX - we also want to support reading SIZE from MAIL parameters */
- session_respond(s, "250-SIZE %zu", env->sc_maxsize);
-
- if (ADVERTISE_TLS(s))
- session_respond(s, "250-STARTTLS");
-
- if (ADVERTISE_AUTH(s))
- session_respond(s, "250-AUTH PLAIN LOGIN");
-
- session_respond(s, "250 HELP");
-
+ session_imsg(s, PROC_MFA, IMSG_MFA_HELO, 0, 0, -1, &s->s_msg,
+ sizeof(s->s_msg));
return 1;
}
@@ -416,7 +401,6 @@ session_rfc5321_mail_handler(struct session *s, char *args)
s->rcptcount = 0;
s->s_state = S_MAIL_MFA;
- s->s_msg.session_id = s->s_id;
s->s_msg.delivery.id = 0;
s->s_msg.delivery.ss = s->s_ss;
@@ -447,7 +431,6 @@ session_rfc5321_rcpt_handler(struct session *s, char *args)
}
s->s_state = S_RCPT_MFA;
-
session_imsg(s, PROC_MFA, IMSG_MFA_RCPT, 0, 0, -1, &s->s_msg,
sizeof(s->s_msg));
return 1;
@@ -456,10 +439,8 @@ session_rfc5321_rcpt_handler(struct session *s, char *args)
static int
session_rfc5321_quit_handler(struct session *s, char *args)
{
- session_respond(s, "221 2.0.0 %s Closing connection", env->sc_hostname);
-
s->s_flags |= F_QUIT;
-
+ session_respond(s, "221 2.0.0 %s Closing connection", env->sc_hostname);
return 1;
}
@@ -630,6 +611,36 @@ session_pickup(struct session *s, struct submit_status *ss)
s->s_state = S_HELO;
break;
+ case S_HELO:
+ if (ss == NULL)
+ fatalx("bad ss at S_HELO");
+ if (ss->code != 250) {
+ s->s_state = S_GREETED;
+ session_respond(s, "%d Helo rejected", ss->code);
+ return;
+ }
+
+ session_respond(s, "250%c%s Hello %s [%s], pleased to meet you",
+ (s->s_flags & F_EHLO) ? '-' : ' ',
+ env->sc_hostname, s->s_msg.delivery.helo, ss_to_text(&s->s_ss));
+
+ if (s->s_flags & F_EHLO) {
+ /* unconditionnal extensions go first */
+ session_respond(s, "250-8BITMIME");
+ session_respond(s, "250-ENHANCEDSTATUSCODES");
+
+ /* XXX - we also want to support reading SIZE from MAIL parameters */
+ session_respond(s, "250-SIZE %zu", env->sc_maxsize);
+
+ if (ADVERTISE_TLS(s))
+ session_respond(s, "250-STARTTLS");
+
+ if (ADVERTISE_AUTH(s))
+ session_respond(s, "250-AUTH PLAIN LOGIN");
+ session_respond(s, "250 HELP");
+ }
+ break;
+
case S_MAIL_MFA:
if (ss == NULL)
fatalx("bad ss at S_MAIL_MFA");
@@ -839,7 +850,6 @@ session_read_data(struct session *s, char *line)
0, 0, -1, &s->s_msg, sizeof(s->s_msg));
s->s_state = S_DONE;
}
-
return;
}
@@ -1037,7 +1047,6 @@ session_readline(struct session *s)
s->s_flags |= F_QUIT;
free(line);
free(line2);
-
return NULL;
}
diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c
index f98313ba1c1..5c5412818a7 100644
--- a/usr.sbin/smtpd/smtpd.c
+++ b/usr.sbin/smtpd/smtpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.c,v 1.127 2011/08/26 14:39:47 chl Exp $ */
+/* $OpenBSD: smtpd.c,v 1.128 2011/08/27 22:32:41 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -84,12 +84,10 @@ int __b64_pton(char const *, unsigned char *, size_t);
static void
parent_imsg(struct imsgev *iev, struct imsg *imsg)
{
- struct smtpd newenv, *saveenv;
struct forward_req *fwreq;
- struct reload *reload;
struct auth *auth;
struct auth_backend *auth_backend;
- int fd, r;
+ int fd;
if (iev->proc == PROC_SMTP) {
switch (imsg->hdr.type) {
@@ -147,33 +145,6 @@ parent_imsg(struct imsgev *iev, struct imsg *imsg)
if (iev->proc == PROC_CONTROL) {
switch (imsg->hdr.type) {
- case IMSG_CONF_RELOAD:
- reload = imsg->data;
- reload->ret = 0;
- saveenv = env;
- env = &newenv;
- r = parse_config(&newenv, saveenv->sc_conffile, 0);
- env = saveenv;
- if (r == 0) {
- strlcpy(env->sc_hostname, newenv.sc_hostname,
- sizeof env->sc_hostname);
- env->sc_listeners = newenv.sc_listeners;
- env->sc_maps = newenv.sc_maps;
- env->sc_rules = newenv.sc_rules;
- env->sc_rules = newenv.sc_rules;
- env->sc_ssl = newenv.sc_ssl;
-
- parent_send_config_client_certs();
- parent_send_config_ruleset(PROC_MFA);
- parent_send_config_ruleset(PROC_LKA);
- imsg_compose_event(env->sc_ievs[PROC_SMTP],
- IMSG_CONF_RELOAD, 0, 0, -1, NULL, 0);
- reload->ret = 1;
- }
- imsg_compose_event(iev, IMSG_CONF_RELOAD, 0, 0, -1,
- reload, sizeof *reload);
- return;
-
case IMSG_CTL_VERBOSE:
log_verbose(*(int *)imsg->data);
@@ -230,6 +201,7 @@ parent_send_config(int fd, short event, void *p)
{
parent_send_config_listeners();
parent_send_config_client_certs();
+ parent_send_config_ruleset(PROC_MFA);
parent_send_config_ruleset(PROC_LKA);
}
@@ -317,25 +289,34 @@ parent_send_config_ruleset(int proc)
struct rule *r;
struct map *m;
struct mapel *mapel;
+ struct filter *f;
log_debug("parent_send_config_ruleset: reloading rules and maps");
imsg_compose_event(env->sc_ievs[proc], IMSG_CONF_START,
0, 0, -1, NULL, 0);
-
- TAILQ_FOREACH(m, env->sc_maps, m_entry) {
- imsg_compose_event(env->sc_ievs[proc], IMSG_CONF_MAP,
- 0, 0, -1, m, sizeof(*m));
- TAILQ_FOREACH(mapel, &m->m_contents, me_entry) {
+
+ if (proc == PROC_MFA) {
+ TAILQ_FOREACH(f, env->sc_filters, f_entry) {
+ imsg_compose_event(env->sc_ievs[proc], IMSG_CONF_FILTER,
+ 0, 0, -1, f, sizeof(*f));
+ }
+ }
+ else {
+ TAILQ_FOREACH(m, env->sc_maps, m_entry) {
+ imsg_compose_event(env->sc_ievs[proc], IMSG_CONF_MAP,
+ 0, 0, -1, m, sizeof(*m));
+ TAILQ_FOREACH(mapel, &m->m_contents, me_entry) {
imsg_compose_event(env->sc_ievs[proc], IMSG_CONF_MAP_CONTENT,
0, 0, -1, mapel, sizeof(*mapel));
+ }
}
- }
- TAILQ_FOREACH(r, env->sc_rules, r_entry) {
- imsg_compose_event(env->sc_ievs[proc], IMSG_CONF_RULE,
- 0, 0, -1, r, sizeof(*r));
- imsg_compose_event(env->sc_ievs[proc], IMSG_CONF_RULE_SOURCE,
- 0, 0, -1, &r->r_sources->m_name, sizeof(r->r_sources->m_name));
+ TAILQ_FOREACH(r, env->sc_rules, r_entry) {
+ imsg_compose_event(env->sc_ievs[proc], IMSG_CONF_RULE,
+ 0, 0, -1, r, sizeof(*r));
+ imsg_compose_event(env->sc_ievs[proc], IMSG_CONF_RULE_SOURCE,
+ 0, 0, -1, &r->r_sources->m_name, sizeof(r->r_sources->m_name));
+ }
}
imsg_compose_event(env->sc_ievs[proc], IMSG_CONF_END,
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index b0fc7d8bc6a..7e554503a52 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.233 2011/08/17 20:35:11 gilles Exp $ */
+/* $OpenBSD: smtpd.h,v 1.234 2011/08/27 22:32:41 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@openbsd.org>
@@ -21,6 +21,8 @@
#define nitems(_a) (sizeof((_a)) / sizeof((_a)[0]))
#endif
+#include "filter.h"
+
#define IMSG_SIZE_CHECK(p) do { \
if (IMSG_DATA_SIZE(&imsg) != sizeof(*p)) \
fatalx("bad length imsg received"); \
@@ -35,13 +37,14 @@
#define MAX_HOPS_COUNT 100
-/* sizes include the tailing '\0' */
-#define MAX_LINE_SIZE 1024
-#define MAX_LOCALPART_SIZE 128
-#define MAX_DOMAINPART_SIZE MAXHOSTNAMELEN
#define MAX_TAG_SIZE 32
+/* SYNC WITH filter.h */
+//#define MAX_LINE_SIZE 1024
+//#define MAX_LOCALPART_SIZE 128
+//#define MAX_DOMAINPART_SIZE MAXHOSTNAMELEN
/* return and forward path size */
+#define MAX_FILTER_NAME 32
#define MAX_PATH_SIZE 256
#define MAX_RULEBUFFER_LEN 256
@@ -49,6 +52,7 @@
#define SMTPD_QUEUE_MAXINTERVAL (4 * 60 * 60)
#define SMTPD_QUEUE_EXPIRY (4 * 24 * 60 * 60)
#define SMTPD_USER "_smtpd"
+#define SMTPD_FILTER_USER "_smtpdmfa"
#define SMTPD_SOCKET "/var/run/smtpd.sock"
#define SMTPD_BANNER "220 %s ESMTP OpenSMTPD"
#define SMTPD_SESSION_TIMEOUT 300
@@ -130,6 +134,7 @@ enum imsg_type {
IMSG_CONF_MAP_CONTENT,
IMSG_CONF_RULE,
IMSG_CONF_RULE_SOURCE,
+ IMSG_CONF_FILTER,
IMSG_CONF_END,
IMSG_CONF_RELOAD,
IMSG_LKA_MAIL,
@@ -138,8 +143,11 @@ enum imsg_type {
IMSG_LKA_RULEMATCH,
IMSG_MDA_SESS_NEW,
IMSG_MDA_DONE,
- IMSG_MFA_RCPT,
+
+ IMSG_MFA_HELO,
IMSG_MFA_MAIL,
+ IMSG_MFA_RCPT,
+ IMSG_MFA_DISCONNECT,
IMSG_QUEUE_CREATE_MESSAGE,
IMSG_QUEUE_SUBMIT_ENVELOPE,
@@ -337,7 +345,6 @@ struct mailaddr {
char domain[MAX_DOMAINPART_SIZE];
};
-
enum delivery_type {
D_INVALID = 0,
D_MDA,
@@ -453,7 +460,7 @@ enum child_type {
CHILD_INVALID,
CHILD_DAEMON,
CHILD_MDA,
- CHILD_ENQUEUE_OFFLINE
+ CHILD_ENQUEUE_OFFLINE,
};
struct child {
@@ -628,6 +635,8 @@ struct smtpd {
struct ramqueue sc_rqueue;
struct queue_backend *sc_queue;
+ TAILQ_HEAD(filterlist, filter) *sc_filters;
+
TAILQ_HEAD(listenerlist, listener) *sc_listeners;
TAILQ_HEAD(maplist, map) *sc_maps, *sc_maps_reload;
TAILQ_HEAD(rulelist, rule) *sc_rules, *sc_rules_reload;
@@ -635,6 +644,7 @@ struct smtpd {
SPLAY_HEAD(ssltree, ssl) *sc_ssl;
SPLAY_HEAD(childtree, child) children;
SPLAY_HEAD(lkatree, lka_session) lka_sessions;
+ SPLAY_HEAD(mfatree, mfa_session) mfa_sessions;
SPLAY_HEAD(mtatree, mta_session) mta_sessions;
LIST_HEAD(mdalist, mda_session) mda_sessions;
@@ -819,6 +829,25 @@ struct lka_session {
struct submit_status ss;
};
+struct filter {
+ TAILQ_ENTRY(filter) f_entry;
+ pid_t pid;
+ struct event ev;
+ struct imsgbuf *ibuf;
+ char name[MAX_FILTER_NAME];
+ char path[MAXPATHLEN];
+};
+
+struct mfa_session {
+ SPLAY_ENTRY(mfa_session) nodes;
+ u_int64_t id;
+
+ enum session_state state;
+ struct submit_status ss;
+ struct filter *filter;
+ struct filter_msg fm;
+};
+
enum mta_state {
MTA_INVALID_STATE,
MTA_INIT,
@@ -1046,7 +1075,8 @@ pid_t mda(void);
/* mfa.c */
pid_t mfa(void);
-
+int mfa_session_cmp(struct mfa_session *, struct mfa_session *);
+SPLAY_PROTOTYPE(mfatree, mfa_session, nodes, mfa_session_cmp);
/* mta.c */
pid_t mta(void);
diff --git a/usr.sbin/smtpd/smtpd/Makefile b/usr.sbin/smtpd/smtpd/Makefile
index 758e3c00dc8..0c4fab2109f 100644
--- a/usr.sbin/smtpd/smtpd/Makefile
+++ b/usr.sbin/smtpd/smtpd/Makefile
@@ -1,11 +1,11 @@
-# $OpenBSD: Makefile,v 1.28 2011/05/21 18:43:08 gilles Exp $
+# $OpenBSD: Makefile,v 1.29 2011/08/27 22:32:42 gilles Exp $
PROG= smtpd
SRCS= aliases.c auth_backend.c bounce.c client.c \
config.c control.c dns.c expand.c forward.c \
lka.c lka_session.c log.c map.c map_backend.c \
map_backend_db.c map_backend_stdio.c \
- mda.c mfa.c mta.c parse.y \
+ mda.c mfa.c mfa_session.c mta.c parse.y \
queue.c queue_shared.c ruleset.c runner.c smtp.c \
smtp_session.c smtpd.c ssl.c ssl_privsep.c util.c asr.c \
print.c pack.c dname.c res_random.c sockaddr.c \
diff --git a/usr.sbin/smtpd/smtpfilter.c b/usr.sbin/smtpd/smtpfilter.c
new file mode 100644
index 00000000000..391d47d949e
--- /dev/null
+++ b/usr.sbin/smtpd/smtpfilter.c
@@ -0,0 +1,125 @@
+/* $OpenBSD: smtpfilter.c,v 1.1 2011/08/27 22:32:41 gilles Exp $ */
+
+/*
+ * Copyright (c) 2011 Gilles Chehade <gilles@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+
+#include "filter.h"
+
+int helo_cb(u_int64_t, struct filter_helo *, void *);
+int ehlo_cb(u_int64_t, struct filter_helo *, void *);
+int mail_cb(u_int64_t, struct filter_mail *, void *);
+int rcpt_cb(u_int64_t, struct filter_rcpt *, void *);
+int data_cb(u_int64_t, struct filter_data *, void *);
+
+
+/*
+ * *ALL* of the callbacks follow the same principle:
+ *
+ * First parameter is a unique identifier that has been assigned to the
+ * client session when it connected. The same client session entering a
+ * second callback will enter it with the same id.
+ *
+ * Second parameter is a callback-specific structure filled with all of
+ * the information necessary to handle filtering at the callback stage.
+ * Some of the callbacks allow envelope or data rewriting.
+ *
+ * Third parameter is a filter-specific argument. It can be NULL, or it
+ * can be anything the developer wants, typically a structure allocated
+ * in main() to keep various states.
+ * A different paramter can be registered for each callback.
+ *
+ * RETURN VALUES:
+ *
+ * callbacks return 1 if the test succeesd, 0 if it fails.
+ * once a test fails, the entire envelope is rejected.
+ *
+ *
+ * -- gilles@
+ *
+ */
+
+int
+helo_cb(u_int64_t id, struct filter_helo *helo, void *mystuff)
+{
+ return 1;
+}
+
+int
+mail_cb(u_int64_t id, struct filter_mail *mail, void *mystuff)
+{
+ return 1;
+}
+
+int
+rcpt_cb(u_int64_t id, struct filter_rcpt *rcpt, void *mystuff)
+{
+ if (rcpt->user[0] == 'a')
+ return 0;
+ return 1;
+}
+
+
+/*
+ * Not all callbacks need to be implemented !
+ */
+
+int
+main(int argc, char *argv[])
+{
+ void *mystuff;
+
+ /*
+ * this MUST be the first think your filter does.
+ * do not do anything before that call: NOTHING.
+ */
+ filter_init();
+
+
+ /*
+ * NOW you can allocate your structures, read a configuration
+ * file or do anything required to prepare your filter before
+ * it starts handling requests.
+ */
+ mystuff = malloc(42);
+
+
+ /*
+ * we only need to register for callbacks we're interested in
+ */
+ filter_register_helo_callback(helo_cb, mystuff);
+ filter_register_mail_callback(mail_cb, mystuff);
+ filter_register_rcpt_callback(rcpt_cb, mystuff);
+
+ /*
+ * filter_register_ehlo_callback(helo_cb, mystuff);
+ * filter_register_rcpt_callback(rcpt_cb, mystuff);
+ * filter_register_data_callback(data_cb, mystuff);
+ * filter_register_quit_callback(data_cb, mystuff);
+ */
+
+ /*
+ * finally, enter the filter_loop().
+ * it will not return unless a critical failure happens.
+ * do not call exit() from a callback.
+ */
+ filter_loop();
+
+ return 0;
+}