diff options
Diffstat (limited to 'usr.sbin/smtpd/mfa.c')
-rw-r--r-- | usr.sbin/smtpd/mfa.c | 139 |
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; +} |