summaryrefslogtreecommitdiff
path: root/usr.sbin/smtpd/mfa.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/smtpd/mfa.c')
-rw-r--r--usr.sbin/smtpd/mfa.c139
1 files changed, 121 insertions, 18 deletions
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;
+}