summaryrefslogtreecommitdiff
path: root/usr.sbin/smtpd
diff options
context:
space:
mode:
authorGilles Chehade <gilles@cvs.openbsd.org>2018-11-01 10:13:26 +0000
committerGilles Chehade <gilles@cvs.openbsd.org>2018-11-01 10:13:26 +0000
commitd997c59f310385d16d9f257bbcb311a26f51ccf2 (patch)
tree90a33525969cf0a42e346de904f3dc7489273c29 /usr.sbin/smtpd
parentb470850cbf610d8e60b6d6326dd2d94a81b155c2 (diff)
allow smtpd to fork processes at startup and maintain a socketpair with
them. ok jung@, eric@
Diffstat (limited to 'usr.sbin/smtpd')
-rw-r--r--usr.sbin/smtpd/config.c6
-rw-r--r--usr.sbin/smtpd/lka.c13
-rw-r--r--usr.sbin/smtpd/lka_proc.c88
-rw-r--r--usr.sbin/smtpd/parse.y63
-rw-r--r--usr.sbin/smtpd/smtpd.c103
-rw-r--r--usr.sbin/smtpd/smtpd.h19
-rw-r--r--usr.sbin/smtpd/smtpd/Makefile3
7 files changed, 284 insertions, 11 deletions
diff --git a/usr.sbin/smtpd/config.c b/usr.sbin/smtpd/config.c
index 88d78d30eeb..1d148f9c61b 100644
--- a/usr.sbin/smtpd/config.c
+++ b/usr.sbin/smtpd/config.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: config.c,v 1.42 2018/07/03 01:34:43 mortimer Exp $ */
+/* $OpenBSD: config.c,v 1.43 2018/11/01 10:13:25 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -88,6 +88,7 @@ config_default(void)
conf->sc_ssl_dict = calloc(1, sizeof(*conf->sc_ssl_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_dispatcher_bounce = calloc(1, sizeof(*conf->sc_dispatcher_bounce));
limits = calloc(1, sizeof(*limits));
@@ -100,6 +101,7 @@ config_default(void)
conf->sc_ssl_dict == NULL ||
conf->sc_limits_dict == NULL ||
conf->sc_mda_wrappers == NULL ||
+ conf->sc_processors_dict == NULL ||
conf->sc_dispatcher_bounce == NULL ||
limits == NULL)
goto error;
@@ -111,6 +113,7 @@ config_default(void)
dict_init(conf->sc_ssl_dict);
dict_init(conf->sc_tables_dict);
dict_init(conf->sc_limits_dict);
+ dict_init(conf->sc_processors_dict);
limit_mta_set_defaults(limits);
@@ -149,6 +152,7 @@ error:
free(conf->sc_ssl_dict);
free(conf->sc_limits_dict);
free(conf->sc_mda_wrappers);
+ free(conf->sc_processors_dict);
free(conf->sc_dispatcher_bounce);
free(limits);
free(conf);
diff --git a/usr.sbin/smtpd/lka.c b/usr.sbin/smtpd/lka.c
index 8f67210f935..e763e16ac2f 100644
--- a/usr.sbin/smtpd/lka.c
+++ b/usr.sbin/smtpd/lka.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lka.c,v 1.207 2018/07/25 16:00:48 eric Exp $ */
+/* $OpenBSD: lka.c,v 1.208 2018/11/01 10:13:25 gilles Exp $ */
/*
* Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
@@ -79,7 +79,7 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
struct msg m;
union lookup lk;
char buf[LINE_MAX];
- const char *tablename, *username, *password, *label;
+ const char *tablename, *username, *password, *label, *procname;
uint64_t reqid;
int v;
@@ -389,6 +389,15 @@ lka_imsg(struct mproc *p, struct imsg *imsg)
(ret == 1) ? IMSG_CTL_OK : IMSG_CTL_FAIL,
imsg->hdr.peerid, 0, -1, NULL, 0);
return;
+
+ case IMSG_LKA_PROCESSOR_FORK:
+ m_msg(&m, imsg);
+ m_get_string(&m, &procname);
+ m_end(&m);
+
+ lka_proc_forked(procname, imsg->fd);
+ return;
+
}
errx(1, "lka_imsg: unexpected %s imsg", imsg_to_str(imsg->hdr.type));
diff --git a/usr.sbin/smtpd/lka_proc.c b/usr.sbin/smtpd/lka_proc.c
new file mode 100644
index 00000000000..a18da64667d
--- /dev/null
+++ b/usr.sbin/smtpd/lka_proc.c
@@ -0,0 +1,88 @@
+/* $OpenBSD: lka_proc.c,v 1.1 2018/11/01 10:13:25 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 {
+ const char *name;
+ struct io *io;
+};
+
+static void processor_io(struct io *, int, void *);
+
+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 = name;
+ processor->io = io_new();
+ io_set_fd(processor->io, fd);
+ io_set_callback(processor->io, processor_io, processor);
+ dict_xset(&processors, name, processor);
+}
+
+
+static void
+processor_io(struct io *io, int evt, void *arg)
+{
+ struct processor_instance *processor = arg;
+ char *line = NULL;
+ ssize_t len;
+
+ log_trace(TRACE_IO, "processor: %p: %s %s", processor, io_strevent(evt),
+ io_strio(io));
+
+ switch (evt) {
+ case IO_DATAIN:
+ nextline:
+ line = io_getline(processor->io, &len);
+ /* No complete line received */
+ if (line == NULL)
+ return;
+
+ goto nextline;
+ }
+}
diff --git a/usr.sbin/smtpd/parse.y b/usr.sbin/smtpd/parse.y
index 366ff71d59a..a9c6855ad6e 100644
--- a/usr.sbin/smtpd/parse.y
+++ b/usr.sbin/smtpd/parse.y
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.y,v 1.223 2018/11/01 00:18:44 sashan Exp $ */
+/* $OpenBSD: parse.y,v 1.224 2018/11/01 10:13:25 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;
enum listen_options {
LO_FAMILY = 0x000001,
@@ -173,10 +173,11 @@ typedef struct {
%token ACTION ALIAS ANY ARROW AUTH AUTH_OPTIONAL
%token BACKUP BOUNCE
-%token CA CERT CIPHERS COMPRESSION
+%token CA CERT CHROOT CIPHERS COMPRESSION
%token DHE DOMAIN
%token ENCRYPTION ERROR EXPAND_ONLY
%token FILTER FOR FORWARD_ONLY FROM
+%token GROUP
%token HELO HELO_SRC HOST HOSTNAME HOSTNAMES
%token INCLUDE INET4 INET6
%token JUNK
@@ -185,7 +186,7 @@ typedef struct {
%token MAIL_FROM MAILDIR MASK_SRC MASQUERADE MATCH MAX_MESSAGE_SIZE MAX_DEFERRED MBOX MDA MTA MX
%token NO_DSN NO_VERIFY
%token ON
-%token PKI PORT
+%token PKI PORT PROC
%token QUEUE
%token RCPT_TO RECIPIENT RECEIVEDAUTH RELAY REJECT
%token SCHEDULER SENDER SENDERS SMTP SMTPS SOCKET SRC SUB_ADDR_DELIM
@@ -210,6 +211,7 @@ grammar : /* empty */
| grammar mda '\n'
| grammar mta '\n'
| grammar pki '\n'
+ | grammar proc '\n'
| grammar queue '\n'
| grammar scheduler '\n'
| grammar smtp '\n'
@@ -428,6 +430,56 @@ pki_params_opt pki_params
;
+
+proc:
+PROC STRING STRING {
+ if (dict_get(conf->sc_processors_dict, $2)) {
+ yyerror("processor already exists with that name: %s", $2);
+ free($2);
+ free($3);
+ YYERROR;
+ }
+ processor = xcalloc(1, sizeof *processor);
+ processor->command = $3;
+} proc_params {
+ dict_set(conf->sc_processors_dict, $2, processor);
+ processor = NULL;
+}
+;
+
+proc_params_opt:
+USER STRING {
+ if (processor->user) {
+ yyerror("user already specified for this processor");
+ free($2);
+ YYERROR;
+ }
+ processor->user = $2;
+}
+| GROUP STRING {
+ if (processor->group) {
+ yyerror("group already specified for this processor");
+ free($2);
+ YYERROR;
+ }
+ processor->group = $2;
+}
+| CHROOT STRING {
+ if (processor->chroot) {
+ yyerror("chroot already specified for this processor");
+ free($2);
+ YYERROR;
+ }
+ processor->chroot = $2;
+}
+;
+
+proc_params:
+proc_params_opt proc_params
+| /* empty */
+;
+
+
queue:
QUEUE COMPRESSION {
conf->sc_queue_flags |= QUEUE_COMPRESSION;
@@ -1607,6 +1659,7 @@ lookup(char *s)
{ "bounce", BOUNCE },
{ "ca", CA },
{ "cert", CERT },
+ { "chroot", CHROOT },
{ "ciphers", CIPHERS },
{ "compression", COMPRESSION },
{ "dhe", DHE },
@@ -1617,6 +1670,7 @@ lookup(char *s)
{ "for", FOR },
{ "forward-only", FORWARD_ONLY },
{ "from", FROM },
+ { "group", GROUP },
{ "helo", HELO },
{ "helo-src", HELO_SRC },
{ "host", HOST },
@@ -1647,6 +1701,7 @@ lookup(char *s)
{ "on", ON },
{ "pki", PKI },
{ "port", PORT },
+ { "proc", PROC },
{ "queue", QUEUE },
{ "rcpt-to", RCPT_TO },
{ "received-auth", RECEIVEDAUTH },
diff --git a/usr.sbin/smtpd/smtpd.c b/usr.sbin/smtpd/smtpd.c
index bc94e8ee55c..0ec428ced9b 100644
--- a/usr.sbin/smtpd/smtpd.c
+++ b/usr.sbin/smtpd/smtpd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.c,v 1.303 2018/09/04 13:04:42 gilles Exp $ */
+/* $OpenBSD: smtpd.c,v 1.304 2018/11/01 10:13:25 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -34,6 +34,7 @@
#include <event.h>
#include <fcntl.h>
#include <fts.h>
+#include <grp.h>
#include <imsg.h>
#include <inttypes.h>
#include <login_cap.h>
@@ -89,9 +90,13 @@ 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 *);
+
enum child_type {
CHILD_DAEMON,
CHILD_MDA,
+ CHILD_PROCESSOR,
CHILD_ENQUEUE_OFFLINE,
};
@@ -382,6 +387,14 @@ parent_sig_handler(int sig, short event, void *p)
goto skip;
switch (child->type) {
+ case CHILD_PROCESSOR:
+ if (fail) {
+ log_warnx("warn: lost processor: %s %s",
+ child->title, cause);
+ parent_shutdown();
+ }
+ break;
+
case CHILD_DAEMON:
if (fail)
log_warnx("warn: lost child: %s %s",
@@ -1053,6 +1066,8 @@ smtpd(void) {
offline_timeout.tv_usec = 0;
evtimer_add(&offline_ev, &offline_timeout);
+ fork_processors();
+
purge_task();
if (pledge("stdio rpath wpath cpath fattr flock tmppath "
@@ -1229,6 +1244,88 @@ purge_task(void)
}
static void
+fork_processors(void)
+{
+ const char *name;
+ struct processor *processor;
+ void *iter;
+
+ iter = NULL;
+ while (dict_iter(env->sc_processors_dict, &iter, &name, (void **)&processor))
+ fork_processor(name, processor->command, processor->user, processor->group, processor->chroot);
+}
+
+static void
+fork_processor(const char *name, const char *command, const char *user, const char *group, const char *chroot_path)
+{
+ pid_t pid;
+ int sp[2];
+ struct passwd *pw;
+ struct group *gr;
+
+ log_debug("debug: smtpd: forking processor %s %s user=%s,group=%s", name, command, user, group);
+
+ if (user == NULL)
+ user = SMTPD_USER;
+ if ((pw = getpwnam(user)) == NULL)
+ err(1, "getpwnam");
+
+ if (group) {
+ if ((gr = getgrnam(group)) == NULL)
+ err(1, "getgrnam");
+ }
+ else {
+ if ((gr = getgrgid(pw->pw_gid)) == NULL)
+ err(1, "getgrgid");
+ }
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
+ err(1, "socketpair");
+
+ if ((pid = fork()) < 0)
+ err(1, "fork");
+
+ /* parent passes the child fd over to lka */
+ if (pid > 0) {
+ child_add(pid, CHILD_PROCESSOR, name);
+ close(sp[0]);
+ m_create(p_lka, IMSG_LKA_PROCESSOR_FORK, 0, 0, sp[1]);
+ m_add_string(p_lka, name);
+ m_close(p_lka);
+ return;
+ }
+
+ close(sp[1]);
+ dup2(sp[0], STDIN_FILENO);
+ dup2(sp[0], STDOUT_FILENO);
+
+ if (chroot_path) {
+ if (chroot(chroot_path) != 0 || chdir("/") != 0)
+ err(1, "chroot: %s", chroot_path);
+ }
+
+ 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");
+
+ if (closefrom(STDERR_FILENO + 1) < 0)
+ err(1, "closefrom");
+ if (setsid() < 0)
+ err(1, "setsid");
+ if (signal(SIGPIPE, SIG_DFL) == SIG_ERR ||
+ signal(SIGINT, SIG_DFL) == SIG_ERR ||
+ signal(SIGTERM, SIG_DFL) == SIG_ERR ||
+ signal(SIGCHLD, SIG_DFL) == SIG_ERR ||
+ signal(SIGHUP, SIG_DFL) == SIG_ERR)
+ err(1, "signal");
+
+ execle(command, name, (char *)NULL, NULL);
+ perror("execle");
+ _exit(1);
+}
+
+static void
forkmda(struct mproc *p, uint64_t id, struct deliver *deliver)
{
char ebuf[128], sfn[32];
@@ -1725,6 +1822,8 @@ proc_title(enum smtp_proc_type proc)
return "klondike";
case PROC_CLIENT:
return "client";
+ case PROC_PROCESSOR:
+ return "processor";
}
return "unknown";
}
@@ -1905,6 +2004,8 @@ imsg_to_str(int type)
CASE(IMSG_SMTP_EVENT_ROLLBACK);
CASE(IMSG_SMTP_EVENT_DISCONNECT);
+ CASE(IMSG_LKA_PROCESSOR_FORK);
+
CASE(IMSG_CA_PRIVENC);
CASE(IMSG_CA_PRIVDEC);
default:
diff --git a/usr.sbin/smtpd/smtpd.h b/usr.sbin/smtpd/smtpd.h
index 777bc11efda..1224ec712e7 100644
--- a/usr.sbin/smtpd/smtpd.h
+++ b/usr.sbin/smtpd/smtpd.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: smtpd.h,v 1.563 2018/10/31 16:32:12 gilles Exp $ */
+/* $OpenBSD: smtpd.h,v 1.564 2018/11/01 10:13:25 gilles Exp $ */
/*
* Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
@@ -302,6 +302,8 @@ enum imsg_type {
IMSG_SMTP_EVENT_ROLLBACK,
IMSG_SMTP_EVENT_DISCONNECT,
+ IMSG_LKA_PROCESSOR_FORK,
+
IMSG_CA_PRIVENC,
IMSG_CA_PRIVDEC
};
@@ -314,7 +316,7 @@ enum smtp_proc_type {
PROC_SCHEDULER,
PROC_PONY,
PROC_CA,
-
+ PROC_PROCESSOR,
PROC_CLIENT,
};
@@ -531,6 +533,8 @@ struct smtpd {
size_t sc_scheduler_max_msg_batch_size;
size_t sc_scheduler_max_schedule;
+ struct dict *sc_processors_dict;
+
int sc_ttl;
#define MAX_BOUNCE_WARN 4
time_t sc_bounce_warn[MAX_BOUNCE_WARN];
@@ -976,6 +980,13 @@ enum lka_resp_status {
LKA_PERMFAIL
};
+struct processor {
+ const char *command;
+ const char *user;
+ const char *group;
+ const char *chroot;
+};
+
enum ca_resp_status {
CA_OK,
CA_FAIL
@@ -1220,6 +1231,10 @@ int limit_mta_set(struct mta_limits *, const char*, int64_t);
int lka(void);
+/* lka_proc.c */
+void lka_proc_forked(const char *, int);
+
+
/* lka_session.c */
void lka_session(uint64_t, struct envelope *);
void lka_session_forward_reply(struct forward_req *, int);
diff --git a/usr.sbin/smtpd/smtpd/Makefile b/usr.sbin/smtpd/smtpd/Makefile
index 4beb5967af1..511128c5328 100644
--- a/usr.sbin/smtpd/smtpd/Makefile
+++ b/usr.sbin/smtpd/smtpd/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.93 2018/08/31 07:28:27 eric Exp $
+# $OpenBSD: Makefile,v 1.94 2018/11/01 10:13:25 gilles Exp $
.PATH: ${.CURDIR}/..
@@ -22,6 +22,7 @@ SRCS+= iobuf.c
SRCS+= ioev.c
SRCS+= limit.c
SRCS+= lka.c
+SRCS+= lka_proc.c
SRCS+= lka_session.c
SRCS+= log.c
SRCS+= mailaddr.c