xref: /openbsd-src/usr.sbin/smtpd/smtpd.c (revision 16b0c81bb5f6edbf5168189a20f4f8992d1e7bb5)
1*16b0c81bSclaudio /*	$OpenBSD: smtpd.c,v 1.354 2024/11/21 13:22:21 claudio Exp $	*/
21f3c2bcfSsobrado 
33ef9cbf7Sgilles /*
465c4fdfbSgilles  * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org>
53ef9cbf7Sgilles  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6e5b07014Sgilles  * Copyright (c) 2009 Jacek Masiulaniec <jacekm@dobremiasto.net>
73ef9cbf7Sgilles  *
83ef9cbf7Sgilles  * Permission to use, copy, modify, and distribute this software for any
93ef9cbf7Sgilles  * purpose with or without fee is hereby granted, provided that the above
103ef9cbf7Sgilles  * copyright notice and this permission notice appear in all copies.
113ef9cbf7Sgilles  *
123ef9cbf7Sgilles  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
133ef9cbf7Sgilles  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
143ef9cbf7Sgilles  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
153ef9cbf7Sgilles  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
163ef9cbf7Sgilles  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
173ef9cbf7Sgilles  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
183ef9cbf7Sgilles  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
193ef9cbf7Sgilles  */
203ef9cbf7Sgilles 
213ef9cbf7Sgilles #include <sys/wait.h>
223ef9cbf7Sgilles #include <sys/stat.h>
233ef9cbf7Sgilles 
2465c4fdfbSgilles #include <bsd_auth.h>
2514f5466bSeric #include <dirent.h>
263ef9cbf7Sgilles #include <errno.h>
2765c4fdfbSgilles #include <fcntl.h>
285f4bd98fSgilles #include <fts.h>
29a93e09f5Sgilles #include <grp.h>
301c6ac251Seric #include <inttypes.h>
313ef9cbf7Sgilles #include <paths.h>
32b88ab68dSeric #include <poll.h>
333ef9cbf7Sgilles #include <pwd.h>
343ef9cbf7Sgilles #include <signal.h>
35ccfb4053Seric #include <syslog.h>
363ef9cbf7Sgilles #include <stdlib.h>
373ef9cbf7Sgilles #include <string.h>
38eb268927Sgilles #include <sysexits.h>
390dcffd0dSop #include <time.h>
40eed85469Seric #include <tls.h>
413ef9cbf7Sgilles #include <unistd.h>
423ef9cbf7Sgilles 
433ef9cbf7Sgilles #include "smtpd.h"
445eb8dddaSgilles #include "log.h"
4565c4fdfbSgilles #include "ssl.h"
463ef9cbf7Sgilles 
47b88ab68dSeric #define SMTPD_MAXARG 32
48b88ab68dSeric 
4965c4fdfbSgilles static void parent_imsg(struct mproc *, struct imsg *);
50be925435Sgilles static void usage(void);
51b88ab68dSeric static int smtpd(void);
5243962b9cSeric static void parent_shutdown(void);
53be925435Sgilles static void parent_send_config(int, short, void *);
5465c4fdfbSgilles static void parent_send_config_lka(void);
551a5b831aSmartijn static void parent_send_config_dispatcher(void);
5676eb97c5Sreyk static void parent_send_config_ca(void);
57be925435Sgilles static void parent_sig_handler(int, short, void *);
5865c4fdfbSgilles static void forkmda(struct mproc *, uint64_t, struct deliver *);
5965c4fdfbSgilles static int parent_forward_open(char *, char *, uid_t, gid_t);
603a305da9Seric static struct child *child_add(pid_t, int, const char *);
61b88ab68dSeric static struct mproc *start_child(int, char **, char *);
62b88ab68dSeric static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int);
63b88ab68dSeric static void setup_peers(struct mproc *, struct mproc *);
64b88ab68dSeric static void setup_done(struct mproc *);
65b88ab68dSeric static void setup_proc(void);
66b88ab68dSeric static struct mproc *setup_peer(enum smtp_proc_type, pid_t, int);
67b88ab68dSeric static int imsg_wait(struct imsgbuf *, struct imsg *, int);
6834abc65bSjacekm 
6914f5466bSeric static void	offline_scan(int, short, void *);
700228dab0Smillert static int	offline_add(char *, uid_t, gid_t);
7114f5466bSeric static void	offline_done(void);
720228dab0Smillert static int	offline_enqueue(char *, uid_t, gid_t);
739a5146a5Seric 
74b35655ddSgilles static void	purge_task(void);
7565c4fdfbSgilles static int	parent_auth_user(const char *, const char *);
761c3ac238Seric static void	load_pki_tree(void);
77c52bd758Sreyk static void	load_pki_keys(void);
78591aa05bSeric 
795d1bf438Sgilles static void	fork_filter_processes(void);
805d1bf438Sgilles static void	fork_filter_process(const char *, const char *, const char *, const char *, const char *, uint32_t);
81a93e09f5Sgilles 
823a305da9Seric enum child_type {
833a305da9Seric 	CHILD_DAEMON,
843a305da9Seric 	CHILD_MDA,
85a93e09f5Sgilles 	CHILD_PROCESSOR,
863a305da9Seric 	CHILD_ENQUEUE_OFFLINE,
873a305da9Seric };
883a305da9Seric 
893a305da9Seric struct child {
903a305da9Seric 	pid_t			 pid;
913a305da9Seric 	enum child_type		 type;
923a305da9Seric 	const char		*title;
933a305da9Seric 	int			 mda_out;
9465c4fdfbSgilles 	uint64_t		 mda_id;
953a305da9Seric 	char			*path;
96719ffb95Seric 	char			*cause;
973a305da9Seric };
98591aa05bSeric 
9914f5466bSeric struct offline {
10014f5466bSeric 	TAILQ_ENTRY(offline)	 entry;
1010228dab0Smillert 	uid_t			 uid;
1020228dab0Smillert 	gid_t			 gid;
1039a5146a5Seric 	char			*path;
1049a5146a5Seric };
1059a5146a5Seric 
10614f5466bSeric #define OFFLINE_READMAX		20
10714f5466bSeric #define OFFLINE_QUEUEMAX	5
10814f5466bSeric static size_t			offline_running = 0;
10914f5466bSeric TAILQ_HEAD(, offline)		offline_q;
11014f5466bSeric 
111299c4efeSeric static struct event		config_ev;
11214f5466bSeric static struct event		offline_ev;
11314f5466bSeric static struct timeval		offline_timeout;
11414f5466bSeric 
115b35655ddSgilles static pid_t			purge_pid = -1;
1169a5146a5Seric 
11734abc65bSjacekm extern char	**environ;
11865c4fdfbSgilles void		(*imsg_callback)(struct mproc *, struct imsg *);
11965c4fdfbSgilles 
12065c4fdfbSgilles enum smtp_proc_type	smtpd_process;
121e4d36f12Seric 
122e4d36f12Seric struct smtpd	*env = NULL;
1233ef9cbf7Sgilles 
12465c4fdfbSgilles struct mproc	*p_control = NULL;
12565c4fdfbSgilles struct mproc	*p_lka = NULL;
12665c4fdfbSgilles struct mproc	*p_parent = NULL;
12765c4fdfbSgilles struct mproc	*p_queue = NULL;
12865c4fdfbSgilles struct mproc	*p_scheduler = NULL;
1291a5b831aSmartijn struct mproc	*p_dispatcher = NULL;
13076eb97c5Sreyk struct mproc	*p_ca = NULL;
13165c4fdfbSgilles 
132945f2d46Seric const char	*backend_queue = "fs";
133945f2d46Seric const char	*backend_scheduler = "ramqueue";
1349ed3223cSgilles const char	*backend_stat = "ram";
135945f2d46Seric 
13665c4fdfbSgilles int	profiling = 0;
13765c4fdfbSgilles int	debug = 0;
138299c4efeSeric int	foreground = 0;
1395894db6eSeric int	control_socket = -1;
14021312c12Sgilles 
1413a305da9Seric struct tree	 children;
1423a305da9Seric 
143be925435Sgilles static void
14465c4fdfbSgilles parent_imsg(struct mproc *p, struct imsg *imsg)
145ed1929b6Sjacekm {
146ed1929b6Sjacekm 	struct forward_req	*fwreq;
14726bbc7b9Sgilles 	struct filter_proc	*processor;
14865c4fdfbSgilles 	struct deliver		 deliver;
149719ffb95Seric 	struct child		*c;
15065c4fdfbSgilles 	struct msg		 m;
15165c4fdfbSgilles 	const void		*data;
152dbc3cea0Smartijn 	const char		*username, *password, *cause, *procname;
15365c4fdfbSgilles 	uint64_t		 reqid;
15465c4fdfbSgilles 	size_t			 sz;
155719ffb95Seric 	void			*i;
15665c4fdfbSgilles 	int			 fd, n, v, ret;
157ed1929b6Sjacekm 
1586ba78a98Sgilles 	if (imsg == NULL)
15943962b9cSeric 		fatalx("process %s socket closed", p->name);
1606ba78a98Sgilles 
161ed1929b6Sjacekm 	switch (imsg->hdr.type) {
162aa1d5973Seric 	case IMSG_LKA_OPEN_FORWARD:
16372bef77dSsunil 		CHECK_IMSG_DATA_SIZE(imsg, sizeof *fwreq);
164ed1929b6Sjacekm 		fwreq = imsg->data;
16565c4fdfbSgilles 		fd = parent_forward_open(fwreq->user, fwreq->directory,
16665c4fdfbSgilles 		    fwreq->uid, fwreq->gid);
167ed1929b6Sjacekm 		fwreq->status = 0;
16865c4fdfbSgilles 		if (fd == -1 && errno != ENOENT) {
16965c4fdfbSgilles 			if (errno == EAGAIN)
17065c4fdfbSgilles 				fwreq->status = -1;
17165c4fdfbSgilles 		}
17265c4fdfbSgilles 		else
173ed1929b6Sjacekm 			fwreq->status = 1;
174aa1d5973Seric 		m_compose(p, IMSG_LKA_OPEN_FORWARD, 0, 0, fd,
17565c4fdfbSgilles 		    fwreq, sizeof *fwreq);
17665c4fdfbSgilles 		return;
17765c4fdfbSgilles 
17865c4fdfbSgilles 	case IMSG_LKA_AUTHENTICATE:
17965c4fdfbSgilles 		/*
18065c4fdfbSgilles 		 * If we reached here, it means we want root to lookup
18165c4fdfbSgilles 		 * system user.
18265c4fdfbSgilles 		 */
18365c4fdfbSgilles 		m_msg(&m, imsg);
18465c4fdfbSgilles 		m_get_id(&m, &reqid);
18565c4fdfbSgilles 		m_get_string(&m, &username);
18665c4fdfbSgilles 		m_get_string(&m, &password);
18765c4fdfbSgilles 		m_end(&m);
18865c4fdfbSgilles 
18965c4fdfbSgilles 		ret = parent_auth_user(username, password);
19065c4fdfbSgilles 
191299c4efeSeric 		m_create(p, IMSG_LKA_AUTHENTICATE, 0, 0, -1);
19265c4fdfbSgilles 		m_add_id(p, reqid);
19365c4fdfbSgilles 		m_add_int(p, ret);
19465c4fdfbSgilles 		m_close(p);
195ed1929b6Sjacekm 		return;
196ed1929b6Sjacekm 
197aa1d5973Seric 	case IMSG_MDA_FORK:
19865c4fdfbSgilles 		m_msg(&m, imsg);
19965c4fdfbSgilles 		m_get_id(&m, &reqid);
20065c4fdfbSgilles 		m_get_data(&m, &data, &sz);
20165c4fdfbSgilles 		m_end(&m);
20265c4fdfbSgilles 		if (sz != sizeof(deliver))
20365c4fdfbSgilles 			fatalx("expected deliver");
20465c4fdfbSgilles 		memmove(&deliver, data, sz);
20565c4fdfbSgilles 		forkmda(p, reqid, &deliver);
206ed1929b6Sjacekm 		return;
207719ffb95Seric 
208aa1d5973Seric 	case IMSG_MDA_KILL:
20965c4fdfbSgilles 		m_msg(&m, imsg);
21065c4fdfbSgilles 		m_get_id(&m, &reqid);
21165c4fdfbSgilles 		m_get_string(&m, &cause);
21265c4fdfbSgilles 		m_end(&m);
21365c4fdfbSgilles 
214719ffb95Seric 		i = NULL;
215719ffb95Seric 		while ((n = tree_iter(&children, &i, NULL, (void**)&c)))
216719ffb95Seric 			if (c->type == CHILD_MDA &&
21765c4fdfbSgilles 			    c->mda_id == reqid &&
218719ffb95Seric 			    c->cause == NULL)
219719ffb95Seric 				break;
220719ffb95Seric 		if (!n) {
221299c4efeSeric 			log_debug("debug: smtpd: "
22265c4fdfbSgilles 			    "kill request: proc not found");
223719ffb95Seric 			return;
224719ffb95Seric 		}
22565c4fdfbSgilles 
226118c16f3Sgilles 		c->cause = xstrdup(cause);
227299c4efeSeric 		log_debug("debug: smtpd: kill requested for %u: %s",
228719ffb95Seric 		    c->pid, c->cause);
229719ffb95Seric 		kill(c->pid, SIGTERM);
230719ffb95Seric 		return;
231ed1929b6Sjacekm 
232ed1929b6Sjacekm 	case IMSG_CTL_VERBOSE:
23365c4fdfbSgilles 		m_msg(&m, imsg);
23465c4fdfbSgilles 		m_get_int(&m, &v);
23565c4fdfbSgilles 		m_end(&m);
236f24248b7Sreyk 		log_trace_verbose(v);
23765c4fdfbSgilles 		return;
238ed1929b6Sjacekm 
23957d312f7Seric 	case IMSG_CTL_PROFILE:
24065c4fdfbSgilles 		m_msg(&m, imsg);
24165c4fdfbSgilles 		m_get_int(&m, &v);
24265c4fdfbSgilles 		m_end(&m);
24357d312f7Seric 		profiling = v;
244ed1929b6Sjacekm 		return;
245dbc3cea0Smartijn 
246dbc3cea0Smartijn 	case IMSG_LKA_PROCESSOR_ERRFD:
247dbc3cea0Smartijn 		m_msg(&m, imsg);
248dbc3cea0Smartijn 		m_get_string(&m, &procname);
249dbc3cea0Smartijn 		m_end(&m);
250dbc3cea0Smartijn 
2515d1bf438Sgilles 		processor = dict_xget(env->sc_filter_processes_dict, procname);
252dbc3cea0Smartijn 		m_create(p_lka, IMSG_LKA_PROCESSOR_ERRFD, 0, 0, processor->errfd);
253dbc3cea0Smartijn 		m_add_string(p_lka, procname);
254dbc3cea0Smartijn 		m_close(p_lka);
255dbc3cea0Smartijn 		return;
256ed1929b6Sjacekm 	}
257ed1929b6Sjacekm 
258ff01b044Seric 	fatalx("parent_imsg: unexpected %s imsg from %s",
25976eb97c5Sreyk 	    imsg_to_str(imsg->hdr.type), proc_title(p->proc));
260ed1929b6Sjacekm }
261ed1929b6Sjacekm 
262be925435Sgilles static void
2633ef9cbf7Sgilles usage(void)
2643ef9cbf7Sgilles {
2653ef9cbf7Sgilles 	extern char	*__progname;
2663ef9cbf7Sgilles 
267f573e212Sjmc 	fprintf(stderr, "usage: %s [-dFhnv] [-D macro=value] "
2687ea7d21fSeric 	    "[-f file] [-P system] [-T trace]\n", __progname);
2693ef9cbf7Sgilles 	exit(1);
2703ef9cbf7Sgilles }
2713ef9cbf7Sgilles 
272be925435Sgilles static void
27343962b9cSeric parent_shutdown(void)
2743ef9cbf7Sgilles {
2753ef9cbf7Sgilles 	pid_t pid;
2763ef9cbf7Sgilles 
27743962b9cSeric 	mproc_clear(p_ca);
2781a5b831aSmartijn 	mproc_clear(p_dispatcher);
27943962b9cSeric 	mproc_clear(p_control);
28043962b9cSeric 	mproc_clear(p_lka);
28143962b9cSeric 	mproc_clear(p_scheduler);
28243962b9cSeric 	mproc_clear(p_queue);
2833ef9cbf7Sgilles 
2843ef9cbf7Sgilles 	do {
28534abc65bSjacekm 		pid = waitpid(WAIT_MYPGRP, NULL, 0);
2863ef9cbf7Sgilles 	} while (pid != -1 || (pid == -1 && errno == EINTR));
2873ef9cbf7Sgilles 
288eb351e24Seric 	unlink(SMTPD_SOCKET);
289eb351e24Seric 
29043962b9cSeric 	log_info("Exiting");
29143962b9cSeric 	exit(0);
2923ef9cbf7Sgilles }
2933ef9cbf7Sgilles 
294be925435Sgilles static void
2953ef9cbf7Sgilles parent_send_config(int fd, short event, void *p)
2963ef9cbf7Sgilles {
29765c4fdfbSgilles 	parent_send_config_lka();
2981a5b831aSmartijn 	parent_send_config_dispatcher();
29976eb97c5Sreyk 	parent_send_config_ca();
3001c3ac238Seric 	purge_config(PURGE_PKI);
3019aa27f7fSgilles }
3029aa27f7fSgilles 
303be925435Sgilles static void
3041a5b831aSmartijn parent_send_config_dispatcher(void)
3059aa27f7fSgilles {
3061a5b831aSmartijn 	log_debug("debug: parent_send_config: configuring dispatcher process");
3071a5b831aSmartijn 	m_compose(p_dispatcher, IMSG_CONF_START, 0, 0, -1, NULL, 0);
3081a5b831aSmartijn 	m_compose(p_dispatcher, IMSG_CONF_END, 0, 0, -1, NULL, 0);
3093ef9cbf7Sgilles }
3103ef9cbf7Sgilles 
31165c4fdfbSgilles void
31204fee684Stb parent_send_config_lka(void)
31365c4fdfbSgilles {
31465c4fdfbSgilles 	log_debug("debug: parent_send_config_ruleset: reloading");
31565c4fdfbSgilles 	m_compose(p_lka, IMSG_CONF_START, 0, 0, -1, NULL, 0);
31665c4fdfbSgilles 	m_compose(p_lka, IMSG_CONF_END, 0, 0, -1, NULL, 0);
3179aa27f7fSgilles }
3189aa27f7fSgilles 
319be925435Sgilles static void
32076eb97c5Sreyk parent_send_config_ca(void)
32176eb97c5Sreyk {
32276eb97c5Sreyk 	log_debug("debug: parent_send_config: configuring ca process");
32376eb97c5Sreyk 	m_compose(p_ca, IMSG_CONF_START, 0, 0, -1, NULL, 0);
32476eb97c5Sreyk 	m_compose(p_ca, IMSG_CONF_END, 0, 0, -1, NULL, 0);
32576eb97c5Sreyk }
32676eb97c5Sreyk 
32776eb97c5Sreyk static void
3283ef9cbf7Sgilles parent_sig_handler(int sig, short event, void *p)
3293ef9cbf7Sgilles {
33034abc65bSjacekm 	struct child	*child;
33143962b9cSeric 	int		 status, fail;
33234abc65bSjacekm 	pid_t		 pid;
33334abc65bSjacekm 	char		*cause;
3343ef9cbf7Sgilles 
3353ef9cbf7Sgilles 	switch (sig) {
3363ef9cbf7Sgilles 	case SIGTERM:
3373ef9cbf7Sgilles 	case SIGINT:
33843962b9cSeric 		log_debug("debug: got signal %d", sig);
33943962b9cSeric 		parent_shutdown();
34043962b9cSeric 		/* NOT REACHED */
34143962b9cSeric 
3423ef9cbf7Sgilles 	case SIGCHLD:
3433ef9cbf7Sgilles 		do {
344aa48e8d1Smillert 			int len;
345eb268927Sgilles 			enum mda_resp_status mda_status;
346eb268927Sgilles 			int mda_sysexit;
347aa48e8d1Smillert 
3483ef9cbf7Sgilles 			pid = waitpid(-1, &status, WNOHANG);
34934abc65bSjacekm 			if (pid <= 0)
35034abc65bSjacekm 				continue;
35134abc65bSjacekm 
35234abc65bSjacekm 			fail = 0;
35334abc65bSjacekm 			if (WIFSIGNALED(status)) {
35434abc65bSjacekm 				fail = 1;
355aa48e8d1Smillert 				len = asprintf(&cause, "terminated; signal %d",
35634abc65bSjacekm 				    WTERMSIG(status));
357eb268927Sgilles 				mda_status = MDA_TEMPFAIL;
358eb268927Sgilles 				mda_sysexit = 0;
35934abc65bSjacekm 			} else if (WIFEXITED(status)) {
36034abc65bSjacekm 				if (WEXITSTATUS(status) != 0) {
36134abc65bSjacekm 					fail = 1;
362aa48e8d1Smillert 					len = asprintf(&cause,
363aa48e8d1Smillert 					    "exited abnormally");
364eb268927Sgilles 					mda_sysexit = WEXITSTATUS(status);
365eb268927Sgilles 					if (mda_sysexit == EX_OSERR ||
366eb268927Sgilles 					    mda_sysexit == EX_TEMPFAIL)
367eb268927Sgilles 						mda_status = MDA_TEMPFAIL;
368eb268927Sgilles 					else
369eb268927Sgilles 						mda_status = MDA_PERMFAIL;
370eb268927Sgilles 				} else {
371aa48e8d1Smillert 					len = asprintf(&cause, "exited okay");
372eb268927Sgilles 					mda_status = MDA_OK;
373eb268927Sgilles 					mda_sysexit = 0;
374eb268927Sgilles 				}
37534abc65bSjacekm 			} else
376cb6e8661Sgilles 				/* WIFSTOPPED or WIFCONTINUED */
377cb6e8661Sgilles 				continue;
37834abc65bSjacekm 
379aa48e8d1Smillert 			if (len == -1)
380aa48e8d1Smillert 				fatal("asprintf");
381aa48e8d1Smillert 
382591aa05bSeric 			if (pid == purge_pid)
383591aa05bSeric 				purge_pid = -1;
384591aa05bSeric 
3853a305da9Seric 			child = tree_pop(&children, pid);
386591aa05bSeric 			if (child == NULL)
387591aa05bSeric 				goto skip;
388591aa05bSeric 
38934abc65bSjacekm 			switch (child->type) {
390a93e09f5Sgilles 			case CHILD_PROCESSOR:
391a93e09f5Sgilles 				if (fail) {
392a93e09f5Sgilles 					log_warnx("warn: lost processor: %s %s",
393a93e09f5Sgilles 					    child->title, cause);
394a93e09f5Sgilles 					parent_shutdown();
395a93e09f5Sgilles 				}
396a93e09f5Sgilles 				break;
397a93e09f5Sgilles 
39834abc65bSjacekm 			case CHILD_DAEMON:
39934abc65bSjacekm 				if (fail)
40082614934Seric 					log_warnx("warn: lost child: %s %s",
4013a305da9Seric 					    child->title, cause);
40234abc65bSjacekm 				break;
40334abc65bSjacekm 
40434abc65bSjacekm 			case CHILD_MDA:
40511dbc40fSjacekm 				if (WIFSIGNALED(status) &&
40611dbc40fSjacekm 				    WTERMSIG(status) == SIGALRM) {
407aa48e8d1Smillert 					char *tmp;
408aa48e8d1Smillert 					if (asprintf(&tmp,
409aa48e8d1Smillert 					    "terminated; timeout") != -1) {
41011dbc40fSjacekm 						free(cause);
411aa48e8d1Smillert 						cause = tmp;
412aa48e8d1Smillert 					}
41311dbc40fSjacekm 				}
414719ffb95Seric 				else if (child->cause &&
415719ffb95Seric 				    WIFSIGNALED(status) &&
416719ffb95Seric 				    WTERMSIG(status) == SIGTERM) {
417719ffb95Seric 					free(cause);
418719ffb95Seric 					cause = child->cause;
419719ffb95Seric 					child->cause = NULL;
420719ffb95Seric 				}
421719ffb95Seric 				free(child->cause);
4221c6ac251Seric 				log_debug("debug: smtpd: mda process done "
4231c6ac251Seric 				    "for session %016"PRIx64 ": %s",
4241c6ac251Seric 				    child->mda_id, cause);
425eb268927Sgilles 
4261a5b831aSmartijn 				m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0,
427299c4efeSeric 				    child->mda_out);
4281a5b831aSmartijn 				m_add_id(p_dispatcher, child->mda_id);
4291a5b831aSmartijn 				m_add_int(p_dispatcher, mda_status);
4301a5b831aSmartijn 				m_add_int(p_dispatcher, mda_sysexit);
4311a5b831aSmartijn 				m_add_string(p_dispatcher, cause);
4321a5b831aSmartijn 				m_close(p_dispatcher);
433eb268927Sgilles 
43434abc65bSjacekm 				break;
43534abc65bSjacekm 
43634abc65bSjacekm 			case CHILD_ENQUEUE_OFFLINE:
43734abc65bSjacekm 				if (fail)
4387e8a7d6bSeric 					log_warnx("warn: smtpd: "
4397e8a7d6bSeric 					    "couldn't enqueue offline "
4407e8a7d6bSeric 					    "message %s; smtpctl %s",
4417e8a7d6bSeric 					    child->path, cause);
442d44f36d1Seric 				else
443d44f36d1Seric 					unlink(child->path);
444d44f36d1Seric 				free(child->path);
44514f5466bSeric 				offline_done();
4463ef9cbf7Sgilles 				break;
44734abc65bSjacekm 
4483ef9cbf7Sgilles 			default:
449c5ad255fSeric 				fatalx("smtpd: unexpected child type");
4503ef9cbf7Sgilles 			}
4513a305da9Seric 			free(child);
452591aa05bSeric     skip:
45334abc65bSjacekm 			free(cause);
4543ef9cbf7Sgilles 		} while (pid > 0 || (pid == -1 && errno == EINTR));
4553ef9cbf7Sgilles 
4563ef9cbf7Sgilles 		break;
4573ef9cbf7Sgilles 	default:
458c5ad255fSeric 		fatalx("smtpd: unexpected signal");
4593ef9cbf7Sgilles 	}
4603ef9cbf7Sgilles }
4613ef9cbf7Sgilles 
4623ef9cbf7Sgilles int
4633ef9cbf7Sgilles main(int argc, char *argv[])
4643ef9cbf7Sgilles {
46565c4fdfbSgilles 	int		 c, i;
466f98e7cc0Seric 	int		 opts, flags;
4673ef9cbf7Sgilles 	const char	*conffile = CONF_FILE;
468b88ab68dSeric 	int		 save_argc = argc;
469b88ab68dSeric 	char		**save_argv = argv;
470b88ab68dSeric 	char		*rexec = NULL;
471942f9647Sgilles 	struct smtpd	*conf;
4723ef9cbf7Sgilles 
473f98e7cc0Seric 	flags = 0;
4743ef9cbf7Sgilles 	opts = 0;
4753ef9cbf7Sgilles 	debug = 0;
476f24248b7Sreyk 	tracing = 0;
4773ef9cbf7Sgilles 
478f24248b7Sreyk 	log_init(1, LOG_MAIL);
4793ef9cbf7Sgilles 
480ff01b044Seric 	if ((conf = config_default()) == NULL)
481ff01b044Seric 		fatal("config_default");
482ff01b044Seric 	env = conf;
483ff01b044Seric 
48414f5466bSeric 	TAILQ_INIT(&offline_q);
4859a5146a5Seric 
486b88ab68dSeric 	while ((c = getopt(argc, argv, "B:dD:hnP:f:FT:vx:")) != -1) {
4873ef9cbf7Sgilles 		switch (c) {
488945f2d46Seric 		case 'B':
489945f2d46Seric 			if (strstr(optarg, "queue=") == optarg)
490945f2d46Seric 				backend_queue = strchr(optarg, '=') + 1;
491945f2d46Seric 			else if (strstr(optarg, "scheduler=") == optarg)
492945f2d46Seric 				backend_scheduler = strchr(optarg, '=') + 1;
4939ed3223cSgilles 			else if (strstr(optarg, "stat=") == optarg)
4949ed3223cSgilles 				backend_stat = strchr(optarg, '=') + 1;
495945f2d46Seric 			else
49665c4fdfbSgilles 				log_warnx("warn: "
49765c4fdfbSgilles 				    "invalid backend specifier %s",
4987e8a7d6bSeric 				    optarg);
499945f2d46Seric 			break;
5003ef9cbf7Sgilles 		case 'd':
501299c4efeSeric 			foreground = 1;
502833159efSsunil 			foreground_log = 1;
5033ef9cbf7Sgilles 			break;
5043ef9cbf7Sgilles 		case 'D':
5053ef9cbf7Sgilles 			if (cmdline_symset(optarg) < 0)
50665c4fdfbSgilles 				log_warnx("warn: "
50765c4fdfbSgilles 				    "could not parse macro definition %s",
5083ef9cbf7Sgilles 				    optarg);
5093ef9cbf7Sgilles 			break;
5107ea7d21fSeric 		case 'h':
51198cf77ddSjung 			log_info("version: " SMTPD_NAME " " SMTPD_VERSION);
5127ea7d21fSeric 			usage();
5137ea7d21fSeric 			break;
5143ef9cbf7Sgilles 		case 'n':
5153ef9cbf7Sgilles 			debug = 2;
5163ef9cbf7Sgilles 			opts |= SMTPD_OPT_NOACTION;
5173ef9cbf7Sgilles 			break;
5183ef9cbf7Sgilles 		case 'f':
5193ef9cbf7Sgilles 			conffile = optarg;
5203ef9cbf7Sgilles 			break;
521833159efSsunil 		case 'F':
522833159efSsunil 			foreground = 1;
523833159efSsunil 			break;
524833159efSsunil 
525bfe8e0bcSeric 		case 'T':
526bfe8e0bcSeric 			if (!strcmp(optarg, "imsg"))
527f24248b7Sreyk 				tracing |= TRACE_IMSG;
528b6d81129Seric 			else if (!strcmp(optarg, "io"))
529f24248b7Sreyk 				tracing |= TRACE_IO;
530b6d81129Seric 			else if (!strcmp(optarg, "smtp"))
531f24248b7Sreyk 				tracing |= TRACE_SMTP;
532417409c3Sgilles 			else if (!strcmp(optarg, "filters"))
533f24248b7Sreyk 				tracing |= TRACE_FILTERS;
534998b3f5fSeric 			else if (!strcmp(optarg, "mta") ||
535df2a7d50Seric 			    !strcmp(optarg, "transfer"))
536f24248b7Sreyk 				tracing |= TRACE_MTA;
537df2a7d50Seric 			else if (!strcmp(optarg, "bounce") ||
538df2a7d50Seric 			    !strcmp(optarg, "bounces"))
539f24248b7Sreyk 				tracing |= TRACE_BOUNCE;
5404744da7eSgilles 			else if (!strcmp(optarg, "scheduler"))
541f24248b7Sreyk 				tracing |= TRACE_SCHEDULER;
5420cf935dfSgilles 			else if (!strcmp(optarg, "lookup"))
543f24248b7Sreyk 				tracing |= TRACE_LOOKUP;
544df2a7d50Seric 			else if (!strcmp(optarg, "stat") ||
545df2a7d50Seric 			    !strcmp(optarg, "stats"))
546f24248b7Sreyk 				tracing |= TRACE_STAT;
54765c4fdfbSgilles 			else if (!strcmp(optarg, "rules"))
548f24248b7Sreyk 				tracing |= TRACE_RULES;
549299c4efeSeric 			else if (!strcmp(optarg, "mproc"))
550f24248b7Sreyk 				tracing |= TRACE_MPROC;
55159a46edcSgilles 			else if (!strcmp(optarg, "expand"))
552f24248b7Sreyk 				tracing |= TRACE_EXPAND;
553df2a7d50Seric 			else if (!strcmp(optarg, "table") ||
554df2a7d50Seric 			    !strcmp(optarg, "tables"))
555f24248b7Sreyk 				tracing |= TRACE_TABLES;
556299c4efeSeric 			else if (!strcmp(optarg, "queue"))
557f24248b7Sreyk 				tracing |= TRACE_QUEUE;
558b6d81129Seric 			else if (!strcmp(optarg, "all"))
559f24248b7Sreyk 				tracing |= ~TRACE_DEBUG;
56065c4fdfbSgilles 			else if (!strcmp(optarg, "profstat"))
56165c4fdfbSgilles 				profiling |= PROFILE_TOSTAT;
56265c4fdfbSgilles 			else if (!strcmp(optarg, "profile-imsg"))
56365c4fdfbSgilles 				profiling |= PROFILE_IMSG;
56465c4fdfbSgilles 			else if (!strcmp(optarg, "profile-queue"))
56565c4fdfbSgilles 				profiling |= PROFILE_QUEUE;
566bfe8e0bcSeric 			else
5677e8a7d6bSeric 				log_warnx("warn: unknown trace flag \"%s\"",
5687e8a7d6bSeric 				    optarg);
569bfe8e0bcSeric 			break;
570f98e7cc0Seric 		case 'P':
571f98e7cc0Seric 			if (!strcmp(optarg, "smtp"))
572f98e7cc0Seric 				flags |= SMTPD_SMTP_PAUSED;
573f98e7cc0Seric 			else if (!strcmp(optarg, "mta"))
574f98e7cc0Seric 				flags |= SMTPD_MTA_PAUSED;
575f98e7cc0Seric 			else if (!strcmp(optarg, "mda"))
576f98e7cc0Seric 				flags |= SMTPD_MDA_PAUSED;
577f98e7cc0Seric 			break;
5783ef9cbf7Sgilles 		case 'v':
579f24248b7Sreyk 			tracing |=  TRACE_DEBUG;
5803ef9cbf7Sgilles 			break;
581b88ab68dSeric 		case 'x':
582b88ab68dSeric 			rexec = optarg;
583b88ab68dSeric 			break;
5843ef9cbf7Sgilles 		default:
5853ef9cbf7Sgilles 			usage();
5863ef9cbf7Sgilles 		}
5873ef9cbf7Sgilles 	}
5883ef9cbf7Sgilles 
5893ef9cbf7Sgilles 	argv += optind;
5903ef9cbf7Sgilles 	argc -= optind;
5913ef9cbf7Sgilles 
5921f49c3f6Sgilles 	if (argc || *argv)
5931f49c3f6Sgilles 		usage();
5941f49c3f6Sgilles 
595d632a6afStim 	env->sc_opts |= opts;
596d632a6afStim 
597942f9647Sgilles 	if (parse_config(conf, conffile, opts))
5983ef9cbf7Sgilles 		exit(1);
5993ef9cbf7Sgilles 
600953aae25Sderaadt 	if (strlcpy(env->sc_conffile, conffile, PATH_MAX)
601953aae25Sderaadt 	    >= PATH_MAX)
602ff01b044Seric 		fatalx("config file exceeds PATH_MAX");
603374cd968Sgilles 
604e4d36f12Seric 	if (env->sc_opts & SMTPD_OPT_NOACTION) {
6050f7ce8c3Ssunil 		if (env->sc_queue_key &&
6060f7ce8c3Ssunil 		    crypto_setup(env->sc_queue_key,
6070f7ce8c3Ssunil 		    strlen(env->sc_queue_key)) == 0) {
6080f7ce8c3Ssunil 			fatalx("crypto_setup:"
6090f7ce8c3Ssunil 			    "invalid key for queue encryption");
6100f7ce8c3Ssunil 		}
6111c3ac238Seric 		load_pki_tree();
612c52bd758Sreyk 		load_pki_keys();
6133ef9cbf7Sgilles 		fprintf(stderr, "configuration OK\n");
6143ef9cbf7Sgilles 		exit(0);
6153ef9cbf7Sgilles 	}
6163ef9cbf7Sgilles 
617f98e7cc0Seric 	env->sc_flags |= flags;
618f98e7cc0Seric 
6193ef9cbf7Sgilles 	/* check for root privileges */
6203ef9cbf7Sgilles 	if (geteuid())
621ff01b044Seric 		fatalx("need root privileges");
6223ef9cbf7Sgilles 
623f24248b7Sreyk 	log_init(foreground_log, LOG_MAIL);
624f24248b7Sreyk 	log_trace_verbose(tracing);
625b88ab68dSeric 	load_pki_tree();
626b88ab68dSeric 	load_pki_keys();
6273f522ce8Sgilles 
628b88ab68dSeric 	log_debug("debug: using \"%s\" queue backend", backend_queue);
629b88ab68dSeric 	log_debug("debug: using \"%s\" scheduler backend", backend_scheduler);
630b88ab68dSeric 	log_debug("debug: using \"%s\" stat backend", backend_stat);
631b88ab68dSeric 
632b88ab68dSeric 	if (env->sc_hostname[0] == '\0')
633ff01b044Seric 		fatalx("machine does not have a hostname set");
634b88ab68dSeric 	env->sc_uptime = time(NULL);
635b88ab68dSeric 
636b88ab68dSeric 	if (rexec == NULL) {
637b88ab68dSeric 		smtpd_process = PROC_PARENT;
6389ed3223cSgilles 
639bf397b35Seric 		if (env->sc_queue_flags & QUEUE_ENCRYPTION) {
640bf397b35Seric 			if (env->sc_queue_key == NULL) {
641bf397b35Seric 				char	*password;
642bf397b35Seric 
643bf397b35Seric 				password = getpass("queue key: ");
644bf397b35Seric 				if (password == NULL)
645ff01b044Seric 					fatal("getpass");
646bf397b35Seric 
647bf397b35Seric 				env->sc_queue_key = strdup(password);
6488fbd7fcbSdoug 				explicit_bzero(password, strlen(password));
649bf397b35Seric 				if (env->sc_queue_key == NULL)
650ff01b044Seric 					fatal("strdup");
651bf397b35Seric 			}
652bf397b35Seric 			else {
6530882b00eSsunil 				char   *buf = NULL;
6540882b00eSsunil 				size_t	sz = 0;
6550882b00eSsunil 				ssize_t	len;
656bf397b35Seric 
657bf397b35Seric 				if (strcasecmp(env->sc_queue_key, "stdin") == 0) {
6580882b00eSsunil 					if ((len = getline(&buf, &sz, stdin)) == -1)
659ff01b044Seric 						fatal("getline");
6600882b00eSsunil 					if (buf[len - 1] == '\n')
6610882b00eSsunil 						buf[len - 1] = '\0';
6620882b00eSsunil 					env->sc_queue_key = buf;
663bf397b35Seric 				}
664bf397b35Seric 			}
665bf397b35Seric 		}
666bf397b35Seric 
667e6a16d48Sgilles 		log_info("info: %s %s starting", SMTPD_NAME, SMTPD_VERSION);
668e6a16d48Sgilles 
669299c4efeSeric 		if (!foreground)
6701150d065Sgilles 			if (daemon(0, 0) == -1)
671ff01b044Seric 				fatal("failed to daemonize");
6723ef9cbf7Sgilles 
673b88ab68dSeric 		/* setup all processes */
674b88ab68dSeric 
675b88ab68dSeric 		p_ca = start_child(save_argc, save_argv, "ca");
676b88ab68dSeric 		p_ca->proc = PROC_CA;
677b88ab68dSeric 
678b88ab68dSeric 		p_control = start_child(save_argc, save_argv, "control");
679b88ab68dSeric 		p_control->proc = PROC_CONTROL;
680b88ab68dSeric 
681b88ab68dSeric 		p_lka = start_child(save_argc, save_argv, "lka");
682b88ab68dSeric 		p_lka->proc = PROC_LKA;
683b88ab68dSeric 
6841a5b831aSmartijn 		p_dispatcher = start_child(save_argc, save_argv, "dispatcher");
6851a5b831aSmartijn 		p_dispatcher->proc = PROC_DISPATCHER;
686b88ab68dSeric 
687b88ab68dSeric 		p_queue = start_child(save_argc, save_argv, "queue");
688b88ab68dSeric 		p_queue->proc = PROC_QUEUE;
689b88ab68dSeric 
690b88ab68dSeric 		p_scheduler = start_child(save_argc, save_argv, "scheduler");
691b88ab68dSeric 		p_scheduler->proc = PROC_SCHEDULER;
692b88ab68dSeric 
693b88ab68dSeric 		setup_peers(p_control, p_ca);
694b88ab68dSeric 		setup_peers(p_control, p_lka);
6951a5b831aSmartijn 		setup_peers(p_control, p_dispatcher);
696b88ab68dSeric 		setup_peers(p_control, p_queue);
697b88ab68dSeric 		setup_peers(p_control, p_scheduler);
6981a5b831aSmartijn 		setup_peers(p_dispatcher, p_ca);
6991a5b831aSmartijn 		setup_peers(p_dispatcher, p_lka);
7001a5b831aSmartijn 		setup_peers(p_dispatcher, p_queue);
701b88ab68dSeric 		setup_peers(p_queue, p_lka);
702b88ab68dSeric 		setup_peers(p_queue, p_scheduler);
703b88ab68dSeric 
704b88ab68dSeric 		if (env->sc_queue_key) {
705b88ab68dSeric 			if (imsg_compose(&p_queue->imsgbuf, IMSG_SETUP_KEY, 0,
706b88ab68dSeric 			    0, -1, env->sc_queue_key, strlen(env->sc_queue_key)
707b88ab68dSeric 			    + 1) == -1)
708b88ab68dSeric 				fatal("imsg_compose");
709dd7efffeSclaudio 			if (imsgbuf_flush(&p_queue->imsgbuf) == -1)
710dd7efffeSclaudio 				fatal("imsgbuf_flush");
711b88ab68dSeric 		}
712b88ab68dSeric 
713b88ab68dSeric 		setup_done(p_ca);
714b88ab68dSeric 		setup_done(p_control);
715b88ab68dSeric 		setup_done(p_lka);
7161a5b831aSmartijn 		setup_done(p_dispatcher);
717b88ab68dSeric 		setup_done(p_queue);
718b88ab68dSeric 		setup_done(p_scheduler);
719b88ab68dSeric 
7202f742ef6Seric 		log_debug("smtpd: setup done");
721b88ab68dSeric 
722b88ab68dSeric 		return smtpd();
723b88ab68dSeric 	}
724b88ab68dSeric 
725b88ab68dSeric 	if (!strcmp(rexec, "ca")) {
726b88ab68dSeric 		smtpd_process = PROC_CA;
727b88ab68dSeric 		setup_proc();
728b88ab68dSeric 
729b88ab68dSeric 		return ca();
730b88ab68dSeric 	}
731b88ab68dSeric 
732b88ab68dSeric 	else if (!strcmp(rexec, "control")) {
733b88ab68dSeric 		smtpd_process = PROC_CONTROL;
734b88ab68dSeric 		setup_proc();
735b88ab68dSeric 
736b88ab68dSeric 		/* the control socket ensures that only one smtpd instance is running */
737b88ab68dSeric 		control_socket = control_create_socket();
738b88ab68dSeric 
739b88ab68dSeric 		env->sc_stat = stat_backend_lookup(backend_stat);
740b88ab68dSeric 		if (env->sc_stat == NULL)
741ff01b044Seric 			fatalx("could not find stat backend \"%s\"", backend_stat);
742b88ab68dSeric 
743b88ab68dSeric 		return control();
744b88ab68dSeric 	}
745b88ab68dSeric 
746b88ab68dSeric 	else if (!strcmp(rexec, "lka")) {
747b88ab68dSeric 		smtpd_process = PROC_LKA;
748b88ab68dSeric 		setup_proc();
749b88ab68dSeric 
750b88ab68dSeric 		return lka();
751b88ab68dSeric 	}
752b88ab68dSeric 
7531a5b831aSmartijn 	else if (!strcmp(rexec, "dispatcher")) {
7541a5b831aSmartijn 		smtpd_process = PROC_DISPATCHER;
755b88ab68dSeric 		setup_proc();
756b88ab68dSeric 
7571a5b831aSmartijn 		return dispatcher();
758b88ab68dSeric 	}
759b88ab68dSeric 
760b88ab68dSeric 	else if (!strcmp(rexec, "queue")) {
761b88ab68dSeric 		smtpd_process = PROC_QUEUE;
762b88ab68dSeric 		setup_proc();
763b88ab68dSeric 
764b88ab68dSeric 		if (env->sc_queue_flags & QUEUE_COMPRESSION)
765b88ab68dSeric 			env->sc_comp = compress_backend_lookup("gzip");
766b88ab68dSeric 
767b88ab68dSeric 		if (!queue_init(backend_queue, 1))
768ff01b044Seric 			fatalx("could not initialize queue backend");
769b88ab68dSeric 
770b88ab68dSeric 		return queue();
771b88ab68dSeric 	}
772b88ab68dSeric 
773b88ab68dSeric 	else if (!strcmp(rexec, "scheduler")) {
774b88ab68dSeric 		smtpd_process = PROC_SCHEDULER;
775b88ab68dSeric 		setup_proc();
776b88ab68dSeric 
77765c4fdfbSgilles 		for (i = 0; i < MAX_BOUNCE_WARN; i++) {
77865c4fdfbSgilles 			if (env->sc_bounce_warn[i] == 0)
77965c4fdfbSgilles 				break;
78065c4fdfbSgilles 			log_debug("debug: bounce warning after %s",
78165c4fdfbSgilles 			    duration_to_text(env->sc_bounce_warn[i]));
78265c4fdfbSgilles 		}
78365c4fdfbSgilles 
784b88ab68dSeric 		return scheduler();
785b88ab68dSeric 	}
7863ef9cbf7Sgilles 
787b88ab68dSeric 	fatalx("bad rexec: %s", rexec);
788e27f1753Sjacekm 
789b88ab68dSeric 	return (1);
790b88ab68dSeric }
791b88ab68dSeric 
792b88ab68dSeric static struct mproc *
793b88ab68dSeric start_child(int save_argc, char **save_argv, char *rexec)
794b88ab68dSeric {
795b88ab68dSeric 	struct mproc *p;
796b88ab68dSeric 	char *argv[SMTPD_MAXARG];
797b88ab68dSeric 	int sp[2], argc = 0;
798b88ab68dSeric 	pid_t pid;
799b88ab68dSeric 
800b88ab68dSeric 	if (save_argc >= SMTPD_MAXARG - 2)
801b88ab68dSeric 		fatalx("too many arguments");
802b88ab68dSeric 
8039877f962Sgilles 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
804b88ab68dSeric 		fatal("socketpair");
805b88ab68dSeric 
8069877f962Sgilles 	io_set_nonblocking(sp[0]);
8079877f962Sgilles 	io_set_nonblocking(sp[1]);
8089877f962Sgilles 
809b88ab68dSeric 	switch (pid = fork()) {
810b88ab68dSeric 	case -1:
811b88ab68dSeric 		fatal("%s: fork", save_argv[0]);
812b88ab68dSeric 	case 0:
813b88ab68dSeric 		break;
814b88ab68dSeric 	default:
815b88ab68dSeric 		close(sp[0]);
816b88ab68dSeric 		p = calloc(1, sizeof(*p));
817b88ab68dSeric 		if (p == NULL)
818b88ab68dSeric 			fatal("calloc");
819b88ab68dSeric 		if((p->name = strdup(rexec)) == NULL)
820b88ab68dSeric 			fatal("strdup");
821b88ab68dSeric 		mproc_init(p, sp[1]);
822b88ab68dSeric 		p->pid = pid;
823b88ab68dSeric 		p->handler = parent_imsg;
824b88ab68dSeric 		return p;
825b88ab68dSeric 	}
826b88ab68dSeric 
827ef4f5895Syasuoka 	if (sp[0] != 3) {
828b88ab68dSeric 		if (dup2(sp[0], 3) == -1)
829b88ab68dSeric 			fatal("%s: dup2", rexec);
830ef4f5895Syasuoka 	} else if (fcntl(sp[0], F_SETFD, 0) == -1)
831ef4f5895Syasuoka 		fatal("%s: fcntl", rexec);
832b88ab68dSeric 
833366735a1Seric 	if (closefrom(4) == -1)
834366735a1Seric 		fatal("%s: closefrom", rexec);
835366735a1Seric 
836b88ab68dSeric 	for (argc = 0; argc < save_argc; argc++)
837b88ab68dSeric 		argv[argc] = save_argv[argc];
838b88ab68dSeric 	argv[argc++] = "-x";
839b88ab68dSeric 	argv[argc++] = rexec;
840b88ab68dSeric 	argv[argc++] = NULL;
841b88ab68dSeric 
842b88ab68dSeric 	execvp(argv[0], argv);
843b88ab68dSeric 	fatal("%s: execvp", rexec);
844b88ab68dSeric }
845b88ab68dSeric 
846b88ab68dSeric static void
847b88ab68dSeric setup_peers(struct mproc *a, struct mproc *b)
848b88ab68dSeric {
849b88ab68dSeric 	int sp[2];
850b88ab68dSeric 
8519877f962Sgilles 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
852b88ab68dSeric 		fatal("socketpair");
853b88ab68dSeric 
8549877f962Sgilles 	io_set_nonblocking(sp[0]);
8559877f962Sgilles 	io_set_nonblocking(sp[1]);
8569877f962Sgilles 
857b88ab68dSeric 	if (imsg_compose(&a->imsgbuf, IMSG_SETUP_PEER, b->proc, b->pid, sp[0],
858b88ab68dSeric 	    NULL, 0) == -1)
859b88ab68dSeric 		fatal("imsg_compose");
860dd7efffeSclaudio 	if (imsgbuf_flush(&a->imsgbuf) == -1)
861dd7efffeSclaudio 		fatal("imsgbuf_flush");
862b88ab68dSeric 
863b88ab68dSeric 	if (imsg_compose(&b->imsgbuf, IMSG_SETUP_PEER, a->proc, a->pid, sp[1],
864b88ab68dSeric 	    NULL, 0) == -1)
865b88ab68dSeric 		fatal("imsg_compose");
866dd7efffeSclaudio 	if (imsgbuf_flush(&b->imsgbuf) == -1)
867dd7efffeSclaudio 		fatal("imsgbuf_flush");
868b88ab68dSeric }
869b88ab68dSeric 
870b88ab68dSeric static void
871b88ab68dSeric setup_done(struct mproc *p)
872b88ab68dSeric {
873b88ab68dSeric 	struct imsg imsg;
874b88ab68dSeric 
875b88ab68dSeric 	if (imsg_compose(&p->imsgbuf, IMSG_SETUP_DONE, 0, 0, -1, NULL, 0) == -1)
876b88ab68dSeric 		fatal("imsg_compose");
877dd7efffeSclaudio 	if (imsgbuf_flush(&p->imsgbuf) == -1)
878dd7efffeSclaudio 		fatal("imsgbuf_flush");
879b88ab68dSeric 
880b88ab68dSeric 	if (imsg_wait(&p->imsgbuf, &imsg, 10000) == -1)
881b88ab68dSeric 		fatal("imsg_wait");
882b88ab68dSeric 
883b88ab68dSeric 	if (imsg.hdr.type != IMSG_SETUP_DONE)
884b88ab68dSeric 		fatalx("expect IMSG_SETUP_DONE");
885b88ab68dSeric 
8862f742ef6Seric 	log_debug("setup_done: %s[%d] done", p->name, p->pid);
887b88ab68dSeric 
888b88ab68dSeric 	imsg_free(&imsg);
889b88ab68dSeric }
890b88ab68dSeric 
891b88ab68dSeric static void
892b88ab68dSeric setup_proc(void)
893b88ab68dSeric {
894b88ab68dSeric 	struct imsgbuf *ibuf;
895b88ab68dSeric 	struct imsg imsg;
896b88ab68dSeric         int setup = 1;
897b88ab68dSeric 
898f24248b7Sreyk 	log_procinit(proc_title(smtpd_process));
899f24248b7Sreyk 
900b88ab68dSeric 	p_parent = calloc(1, sizeof(*p_parent));
901b88ab68dSeric 	if (p_parent == NULL)
902b88ab68dSeric 		fatal("calloc");
903b88ab68dSeric 	if((p_parent->name = strdup("parent")) == NULL)
904b88ab68dSeric 		fatal("strdup");
905b88ab68dSeric 	p_parent->proc = PROC_PARENT;
906b88ab68dSeric 	p_parent->handler = imsg_dispatch;
907b88ab68dSeric 	mproc_init(p_parent, 3);
908b88ab68dSeric 
909b88ab68dSeric 	ibuf = &p_parent->imsgbuf;
910b88ab68dSeric 
911b88ab68dSeric 	while (setup) {
912b88ab68dSeric 		if (imsg_wait(ibuf, &imsg, 10000) == -1)
913b88ab68dSeric 			fatal("imsg_wait");
914b88ab68dSeric 
915b88ab68dSeric 		switch (imsg.hdr.type) {
916b88ab68dSeric 		case IMSG_SETUP_KEY:
917b88ab68dSeric 			env->sc_queue_key = strdup(imsg.data);
918b88ab68dSeric 			break;
919b88ab68dSeric 		case IMSG_SETUP_PEER:
920510586acSclaudio 			setup_peer(imsg.hdr.peerid, imsg.hdr.pid,
921510586acSclaudio 			    imsg_get_fd(&imsg));
922b88ab68dSeric 			break;
923b88ab68dSeric 		case IMSG_SETUP_DONE:
924b88ab68dSeric 			setup = 0;
925b88ab68dSeric 			break;
926b88ab68dSeric 		default:
927b88ab68dSeric 			fatal("bad imsg %d", imsg.hdr.type);
928b88ab68dSeric 		}
929b88ab68dSeric 		imsg_free(&imsg);
930b88ab68dSeric 	}
931b88ab68dSeric 
932b88ab68dSeric 	if (imsg_compose(ibuf, IMSG_SETUP_DONE, 0, 0, -1, NULL, 0) == -1)
933b88ab68dSeric 		fatal("imsg_compose");
934b88ab68dSeric 
935dd7efffeSclaudio 	if (imsgbuf_flush(ibuf) == -1)
936dd7efffeSclaudio 		fatal("imsgbuf_flush");
937b88ab68dSeric 
9382f742ef6Seric 	log_debug("setup_proc: %s done", proc_title(smtpd_process));
939b88ab68dSeric }
940b88ab68dSeric 
941b88ab68dSeric static struct mproc *
942b88ab68dSeric setup_peer(enum smtp_proc_type proc, pid_t pid, int sock)
943b88ab68dSeric {
944b88ab68dSeric 	struct mproc *p, **pp;
945b88ab68dSeric 
9462f742ef6Seric 	log_debug("setup_peer: %s -> %s[%u] fd=%d", proc_title(smtpd_process),
947b88ab68dSeric 	    proc_title(proc), pid, sock);
948b88ab68dSeric 
949b88ab68dSeric 	if (sock == -1)
950b88ab68dSeric 		fatalx("peer socket not received");
951b88ab68dSeric 
952b88ab68dSeric 	switch (proc) {
953b88ab68dSeric 	case PROC_LKA:
954b88ab68dSeric 		pp = &p_lka;
955b88ab68dSeric 		break;
956b88ab68dSeric 	case PROC_QUEUE:
957b88ab68dSeric 		pp = &p_queue;
958b88ab68dSeric 		break;
959b88ab68dSeric 	case PROC_CONTROL:
960b88ab68dSeric 		pp = &p_control;
961b88ab68dSeric 		break;
962b88ab68dSeric 	case PROC_SCHEDULER:
963b88ab68dSeric 		pp = &p_scheduler;
964b88ab68dSeric 		break;
9651a5b831aSmartijn 	case PROC_DISPATCHER:
9661a5b831aSmartijn 		pp = &p_dispatcher;
967b88ab68dSeric 		break;
968b88ab68dSeric 	case PROC_CA:
969b88ab68dSeric 		pp = &p_ca;
970b88ab68dSeric 		break;
971b88ab68dSeric 	default:
972b88ab68dSeric 		fatalx("unknown peer");
973b88ab68dSeric 	}
974b88ab68dSeric 
975b88ab68dSeric 	if (*pp)
976b88ab68dSeric 		fatalx("peer already set");
977b88ab68dSeric 
978b88ab68dSeric 	p = calloc(1, sizeof(*p));
979b88ab68dSeric 	if (p == NULL)
980b88ab68dSeric 		fatal("calloc");
981b88ab68dSeric 	if((p->name = strdup(proc_title(proc))) == NULL)
982b88ab68dSeric 		fatal("strdup");
983b88ab68dSeric 	mproc_init(p, sock);
984b88ab68dSeric 	p->pid = pid;
985b88ab68dSeric 	p->proc = proc;
986b88ab68dSeric 	p->handler = imsg_dispatch;
987b88ab68dSeric 
988b88ab68dSeric 	*pp = p;
989b88ab68dSeric 
990b88ab68dSeric 	return p;
991b88ab68dSeric }
992b88ab68dSeric 
993b88ab68dSeric static int
994b88ab68dSeric imsg_wait(struct imsgbuf *ibuf, struct imsg *imsg, int timeout)
995b88ab68dSeric {
996b88ab68dSeric 	struct pollfd pfd[1];
997b88ab68dSeric 	ssize_t n;
998b88ab68dSeric 
999b88ab68dSeric 	pfd[0].fd = ibuf->fd;
1000b88ab68dSeric 	pfd[0].events = POLLIN;
1001b88ab68dSeric 
1002b88ab68dSeric 	while (1) {
1003b88ab68dSeric 		if ((n = imsg_get(ibuf, imsg)) == -1)
1004b88ab68dSeric 			return -1;
1005b88ab68dSeric 		if (n)
1006b88ab68dSeric 			return 1;
1007b88ab68dSeric 
1008b88ab68dSeric 		n = poll(pfd, 1, timeout);
1009b88ab68dSeric 		if (n == -1)
1010b88ab68dSeric 			return -1;
1011b88ab68dSeric 		if (n == 0) {
1012b88ab68dSeric 			errno = ETIMEDOUT;
1013b88ab68dSeric 			return -1;
1014b88ab68dSeric 		}
1015b88ab68dSeric 
1016*16b0c81bSclaudio 		if (imsgbuf_read(ibuf) != 1)
1017b88ab68dSeric 			return -1;
1018b88ab68dSeric 	}
1019b88ab68dSeric }
1020b88ab68dSeric 
1021b88ab68dSeric int
1022b88ab68dSeric smtpd(void) {
1023b88ab68dSeric 	struct event	 ev_sigint;
1024b88ab68dSeric 	struct event	 ev_sigterm;
1025b88ab68dSeric 	struct event	 ev_sigchld;
1026b88ab68dSeric 	struct event	 ev_sighup;
1027b88ab68dSeric 	struct timeval	 tv;
10283ef9cbf7Sgilles 
1029ed1929b6Sjacekm 	imsg_callback = parent_imsg;
1030b88ab68dSeric 
1031b88ab68dSeric 	tree_init(&children);
1032b88ab68dSeric 
1033b88ab68dSeric 	child_add(p_queue->pid, CHILD_DAEMON, proc_title(PROC_QUEUE));
1034b88ab68dSeric 	child_add(p_control->pid, CHILD_DAEMON, proc_title(PROC_CONTROL));
1035b88ab68dSeric 	child_add(p_lka->pid, CHILD_DAEMON, proc_title(PROC_LKA));
1036b88ab68dSeric 	child_add(p_scheduler->pid, CHILD_DAEMON, proc_title(PROC_SCHEDULER));
10371a5b831aSmartijn 	child_add(p_dispatcher->pid, CHILD_DAEMON, proc_title(PROC_DISPATCHER));
1038b88ab68dSeric 	child_add(p_ca->pid, CHILD_DAEMON, proc_title(PROC_CA));
1039b88ab68dSeric 
10403ef9cbf7Sgilles 	event_init();
10413ef9cbf7Sgilles 
1042e4d36f12Seric 	signal_set(&ev_sigint, SIGINT, parent_sig_handler, NULL);
1043e4d36f12Seric 	signal_set(&ev_sigterm, SIGTERM, parent_sig_handler, NULL);
1044e4d36f12Seric 	signal_set(&ev_sigchld, SIGCHLD, parent_sig_handler, NULL);
1045e4d36f12Seric 	signal_set(&ev_sighup, SIGHUP, parent_sig_handler, NULL);
10463ef9cbf7Sgilles 	signal_add(&ev_sigint, NULL);
10473ef9cbf7Sgilles 	signal_add(&ev_sigterm, NULL);
10483ef9cbf7Sgilles 	signal_add(&ev_sigchld, NULL);
10493ef9cbf7Sgilles 	signal_add(&ev_sighup, NULL);
10503ef9cbf7Sgilles 	signal(SIGPIPE, SIG_IGN);
10513ef9cbf7Sgilles 
105265c4fdfbSgilles 	config_peer(PROC_CONTROL);
105365c4fdfbSgilles 	config_peer(PROC_LKA);
105465c4fdfbSgilles 	config_peer(PROC_QUEUE);
105576eb97c5Sreyk 	config_peer(PROC_CA);
10561a5b831aSmartijn 	config_peer(PROC_DISPATCHER);
10573ef9cbf7Sgilles 
1058299c4efeSeric 	evtimer_set(&config_ev, parent_send_config, NULL);
1059c1392a69Seric 	memset(&tv, 0, sizeof(tv));
1060299c4efeSeric 	evtimer_add(&config_ev, &tv);
10613ef9cbf7Sgilles 
106214f5466bSeric 	/* defer offline scanning for a second */
106314f5466bSeric 	evtimer_set(&offline_ev, offline_scan, NULL);
106414f5466bSeric 	offline_timeout.tv_sec = 1;
106514f5466bSeric 	offline_timeout.tv_usec = 0;
106614f5466bSeric 	evtimer_add(&offline_ev, &offline_timeout);
106714f5466bSeric 
10685d1bf438Sgilles 	fork_filter_processes();
1069a93e09f5Sgilles 
107013a170a7Seric 	purge_task();
107113a170a7Seric 
1072b384c3c3Sgilles 	if (pledge("stdio rpath wpath cpath fattr tmppath "
107368213f7bSgilles 	    "getpw sendfd proc exec id inet chown unix", NULL) == -1)
1074ff01b044Seric 		fatal("pledge");
1075cc2f8927Sgilles 
1076f94528c3Seric 	event_dispatch();
1077f94528c3Seric 	fatalx("exited event loop");
10783ef9cbf7Sgilles 
10793ef9cbf7Sgilles 	return (0);
10803ef9cbf7Sgilles }
10813ef9cbf7Sgilles 
1082be925435Sgilles static void
10831c3ac238Seric load_pki_tree(void)
1084299c4efeSeric {
10851c3ac238Seric 	struct pki	*pki;
1086d658e598Sgilles 	struct ca	*sca;
1087cc81b7c6Seric 	const char	*k;
10881c3ac238Seric 	void		*iter_dict;
1089299c4efeSeric 
1090cc81b7c6Seric 	log_debug("debug: init ssl-tree");
1091cc81b7c6Seric 	iter_dict = NULL;
10921c3ac238Seric 	while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) {
109376a0ecd1Seric 		log_debug("info: loading pki information for %s", k);
10941c3ac238Seric 		if (pki->pki_cert_file == NULL)
10951c3ac238Seric 			fatalx("load_pki_tree: missing certificate file");
10961c3ac238Seric 		if (pki->pki_key_file == NULL)
10971c3ac238Seric 			fatalx("load_pki_tree: missing key file");
1098299c4efeSeric 
10991c3ac238Seric 		if (!ssl_load_certificate(pki, pki->pki_cert_file))
11001c3ac238Seric 			fatalx("load_pki_tree: failed to load certificate file");
1101299c4efeSeric 	}
1102d658e598Sgilles 
1103d658e598Sgilles 	log_debug("debug: init ca-tree");
1104d658e598Sgilles 	iter_dict = NULL;
1105d658e598Sgilles 	while (dict_iter(env->sc_ca_dict, &iter_dict, &k, (void **)&sca)) {
1106d658e598Sgilles 		log_debug("info: loading CA information for %s", k);
1107f7aa1c30Sgilles 		if (!ssl_load_cafile(sca, sca->ca_cert_file))
1108f7aa1c30Sgilles 			fatalx("load_pki_tree: failed to load CA file");
1109d658e598Sgilles 	}
1110eb351e24Seric }
1111299c4efeSeric 
1112c52bd758Sreyk void
1113c52bd758Sreyk load_pki_keys(void)
1114c52bd758Sreyk {
1115c52bd758Sreyk 	struct pki	*pki;
1116c52bd758Sreyk 	const char	*k;
1117c52bd758Sreyk 	void		*iter_dict;
1118c52bd758Sreyk 
1119c52bd758Sreyk 	log_debug("debug: init ssl-tree");
1120c52bd758Sreyk 	iter_dict = NULL;
1121c52bd758Sreyk 	while (dict_iter(env->sc_pki_dict, &iter_dict, &k, (void **)&pki)) {
1122c52bd758Sreyk 		log_debug("info: loading pki keys for %s", k);
1123c52bd758Sreyk 
1124c52bd758Sreyk 		if (!ssl_load_keyfile(pki, pki->pki_key_file, k))
1125c52bd758Sreyk 			fatalx("load_pki_keys: failed to load key file");
1126c52bd758Sreyk 	}
1127c52bd758Sreyk }
1128c52bd758Sreyk 
1129d6f2ac01Seric int
11308380d000Sop fork_proc_backend(const char *key, const char *conf, const char *procname,
11318380d000Sop     int do_stdout)
1132d6f2ac01Seric {
1133d6f2ac01Seric 	pid_t		pid;
1134d6f2ac01Seric 	int		sp[2];
1135953aae25Sderaadt 	char		path[PATH_MAX];
1136953aae25Sderaadt 	char		name[PATH_MAX];
1137d6f2ac01Seric 	char		*arg;
1138d6f2ac01Seric 
1139d6f2ac01Seric 	if (strlcpy(name, conf, sizeof(name)) >= sizeof(name)) {
1140d6f2ac01Seric 		log_warnx("warn: %s-proc: conf too long", key);
1141a3f92dc1Sop 		return (-1);
1142d6f2ac01Seric 	}
1143d6f2ac01Seric 
1144d6f2ac01Seric 	arg = strchr(name, ':');
1145d6f2ac01Seric 	if (arg)
1146d6f2ac01Seric 		*arg++ = '\0';
1147d6f2ac01Seric 
1148ad8f062fSjung 	if (snprintf(path, sizeof(path), PATH_LIBEXEC "/%s-%s", key, name) >=
1149d6f2ac01Seric 	    (ssize_t)sizeof(path)) {
1150d6f2ac01Seric 		log_warn("warn: %s-proc: exec path too long", key);
1151d6f2ac01Seric 		return (-1);
1152d6f2ac01Seric 	}
1153d6f2ac01Seric 
1154d6f2ac01Seric 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1) {
1155d6f2ac01Seric 		log_warn("warn: %s-proc: socketpair", key);
1156d6f2ac01Seric 		return (-1);
1157d6f2ac01Seric 	}
1158d6f2ac01Seric 
1159d6f2ac01Seric 	if ((pid = fork()) == -1) {
1160d6f2ac01Seric 		log_warn("warn: %s-proc: fork", key);
1161d6f2ac01Seric 		close(sp[0]);
1162d6f2ac01Seric 		close(sp[1]);
1163d6f2ac01Seric 		return (-1);
1164d6f2ac01Seric 	}
1165d6f2ac01Seric 
1166d6f2ac01Seric 	if (pid == 0) {
1167d6f2ac01Seric 		/* child process */
1168d6f2ac01Seric 		dup2(sp[0], STDIN_FILENO);
11698380d000Sop 		if (do_stdout)
11708380d000Sop 			dup2(sp[0], STDOUT_FILENO);
1171df69c215Sderaadt 		if (closefrom(STDERR_FILENO + 1) == -1)
1172d6f2ac01Seric 			exit(1);
1173d6f2ac01Seric 
1174d6f2ac01Seric 		if (procname == NULL)
1175d6f2ac01Seric 			procname = name;
1176d6f2ac01Seric 
117753408464Skrw 		execl(path, procname, arg, (char *)NULL);
1178ff01b044Seric 		fatal("execl: %s", path);
1179d6f2ac01Seric 	}
1180d6f2ac01Seric 
1181d6f2ac01Seric 	/* parent process */
1182d6f2ac01Seric 	close(sp[0]);
1183d6f2ac01Seric 
1184d6f2ac01Seric 	return (sp[1]);
1185d6f2ac01Seric }
1186d6f2ac01Seric 
118711dbc40fSjacekm struct child *
11883a305da9Seric child_add(pid_t pid, int type, const char *title)
118934abc65bSjacekm {
119034abc65bSjacekm 	struct child	*child;
119134abc65bSjacekm 
119234abc65bSjacekm 	if ((child = calloc(1, sizeof(*child))) == NULL)
1193c5ad255fSeric 		fatal("smtpd: child_add: calloc");
119434abc65bSjacekm 
119534abc65bSjacekm 	child->pid = pid;
119634abc65bSjacekm 	child->type = type;
119734abc65bSjacekm 	child->title = title;
119834abc65bSjacekm 
11993a305da9Seric 	tree_xset(&children, pid, child);
120011dbc40fSjacekm 
120111dbc40fSjacekm 	return (child);
120234abc65bSjacekm }
120334abc65bSjacekm 
1204591aa05bSeric static void
1205b35655ddSgilles purge_task(void)
1206591aa05bSeric {
120711d04e02Seric 	struct passwd	*pw;
1208591aa05bSeric 	DIR		*d;
1209591aa05bSeric 	int		 n;
1210591aa05bSeric 	uid_t		 uid;
1211591aa05bSeric 	gid_t		 gid;
1212591aa05bSeric 
1213591aa05bSeric 	n = 0;
1214591aa05bSeric 	if ((d = opendir(PATH_SPOOL PATH_PURGE))) {
12151c6ac251Seric 		while (readdir(d) != NULL)
1216591aa05bSeric 			n++;
1217591aa05bSeric 		closedir(d);
1218591aa05bSeric 	} else
121982614934Seric 		log_warn("warn: purge_task: opendir");
1220591aa05bSeric 
1221591aa05bSeric 	if (n > 2) {
1222591aa05bSeric 		switch (purge_pid = fork()) {
1223591aa05bSeric 		case -1:
122482614934Seric 			log_warn("warn: purge_task: fork");
1225591aa05bSeric 			break;
1226591aa05bSeric 		case 0:
1227e9c4fe73Sgilles 			if ((pw = getpwnam(SMTPD_QUEUE_USER)) == NULL)
1228e9c4fe73Sgilles 				fatalx("unknown user " SMTPD_QUEUE_USER);
1229591aa05bSeric 			if (chroot(PATH_SPOOL PATH_PURGE) == -1)
1230591aa05bSeric 				fatal("smtpd: chroot");
1231591aa05bSeric 			if (chdir("/") == -1)
1232591aa05bSeric 				fatal("smtpd: chdir");
123311d04e02Seric 			uid = pw->pw_uid;
123411d04e02Seric 			gid = pw->pw_gid;
1235591aa05bSeric 			if (setgroups(1, &gid) ||
1236591aa05bSeric 			    setresgid(gid, gid, gid) ||
1237591aa05bSeric 			    setresuid(uid, uid, uid))
1238591aa05bSeric 				fatal("smtpd: cannot drop privileges");
123991039167Seric 			rmtree("/", 1);
1240591aa05bSeric 			_exit(0);
1241591aa05bSeric 			break;
1242591aa05bSeric 		default:
1243591aa05bSeric 			break;
1244591aa05bSeric 		}
1245591aa05bSeric 	}
1246591aa05bSeric }
1247591aa05bSeric 
1248be925435Sgilles static void
12495d1bf438Sgilles fork_filter_processes(void)
1250a93e09f5Sgilles {
1251a93e09f5Sgilles 	const char	*name;
1252a93e09f5Sgilles 	void		*iter;
125326bbc7b9Sgilles 	const char	*fn;
125426bbc7b9Sgilles 	struct filter_config *fc;
125526bbc7b9Sgilles 	struct filter_config *fcs;
125626bbc7b9Sgilles 	struct filter_proc *fp;
125726bbc7b9Sgilles 	size_t		 i;
125826bbc7b9Sgilles 
125926bbc7b9Sgilles 	/* For each filter chain, assign the registered subsystem to subfilters */
126026bbc7b9Sgilles 	iter = NULL;
126126bbc7b9Sgilles 	while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) {
126226bbc7b9Sgilles 		if (fc->chain) {
126326bbc7b9Sgilles 			for (i = 0; i < fc->chain_size; ++i) {
126426bbc7b9Sgilles 				fcs = dict_xget(env->sc_filters_dict, fc->chain[i]);
126526bbc7b9Sgilles 				fcs->filter_subsystem |= fc->filter_subsystem;
126626bbc7b9Sgilles 			}
126726bbc7b9Sgilles 		}
126826bbc7b9Sgilles 	}
126926bbc7b9Sgilles 
127026bbc7b9Sgilles 	/* For each filter, assign the registered subsystem to underlying proc */
127126bbc7b9Sgilles 	iter = NULL;
127226bbc7b9Sgilles 	while (dict_iter(env->sc_filters_dict, &iter, (const char **)&fn, (void **)&fc)) {
127326bbc7b9Sgilles 		if (fc->proc) {
12745d1bf438Sgilles 			fp = dict_xget(env->sc_filter_processes_dict, fc->proc);
127526bbc7b9Sgilles 			fp->filter_subsystem |= fc->filter_subsystem;
127626bbc7b9Sgilles 		}
127726bbc7b9Sgilles 	}
1278a93e09f5Sgilles 
1279a93e09f5Sgilles 	iter = NULL;
12805d1bf438Sgilles 	while (dict_iter(env->sc_filter_processes_dict, &iter, &name, (void **)&fp))
12815d1bf438Sgilles 		fork_filter_process(name, fp->command, fp->user, fp->group, fp->chroot, fp->filter_subsystem);
1282a93e09f5Sgilles }
1283a93e09f5Sgilles 
1284a93e09f5Sgilles static void
12855d1bf438Sgilles fork_filter_process(const char *name, const char *command, const char *user, const char *group, const char *chroot_path, uint32_t subsystems)
1286a93e09f5Sgilles {
1287a93e09f5Sgilles 	pid_t		 pid;
128826bbc7b9Sgilles 	struct filter_proc	*processor;
1289dbc3cea0Smartijn 	char		 buf;
1290dbc3cea0Smartijn 	int		 sp[2], errfd[2];
1291a93e09f5Sgilles 	struct passwd	*pw;
1292a93e09f5Sgilles 	struct group	*gr;
12938dfdf75bSmartijn 	char		 exec[_POSIX_ARG_MAX];
12948dfdf75bSmartijn 	int		 execr;
1295a93e09f5Sgilles 
1296a93e09f5Sgilles 	if (user == NULL)
1297a93e09f5Sgilles 		user = SMTPD_USER;
1298a93e09f5Sgilles 	if ((pw = getpwnam(user)) == NULL)
1299ff01b044Seric 		fatal("getpwnam");
1300a93e09f5Sgilles 
1301a93e09f5Sgilles 	if (group) {
1302a93e09f5Sgilles 		if ((gr = getgrnam(group)) == NULL)
1303ff01b044Seric 			fatal("getgrnam");
1304a93e09f5Sgilles 	}
1305a93e09f5Sgilles 	else {
1306a93e09f5Sgilles 		if ((gr = getgrgid(pw->pw_gid)) == NULL)
1307ff01b044Seric 			fatal("getgrgid");
1308a93e09f5Sgilles 	}
1309a93e09f5Sgilles 
1310a93e09f5Sgilles 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, sp) == -1)
1311ff01b044Seric 		fatal("socketpair");
1312dbc3cea0Smartijn 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, errfd) == -1)
1313ff01b044Seric 		fatal("socketpair");
1314a93e09f5Sgilles 
1315df69c215Sderaadt 	if ((pid = fork()) == -1)
1316ff01b044Seric 		fatal("fork");
1317a93e09f5Sgilles 
1318a93e09f5Sgilles 	/* parent passes the child fd over to lka */
1319a93e09f5Sgilles 	if (pid > 0) {
13205d1bf438Sgilles 		processor = dict_xget(env->sc_filter_processes_dict, name);
1321dbc3cea0Smartijn 		processor->errfd = errfd[1];
1322a93e09f5Sgilles 		child_add(pid, CHILD_PROCESSOR, name);
1323a93e09f5Sgilles 		close(sp[0]);
1324dbc3cea0Smartijn 		close(errfd[0]);
1325a93e09f5Sgilles 		m_create(p_lka, IMSG_LKA_PROCESSOR_FORK, 0, 0, sp[1]);
1326a93e09f5Sgilles 		m_add_string(p_lka, name);
132726bbc7b9Sgilles 		m_add_u32(p_lka, (uint32_t)subsystems);
1328a93e09f5Sgilles 		m_close(p_lka);
1329a93e09f5Sgilles 		return;
1330a93e09f5Sgilles 	}
1331a93e09f5Sgilles 
1332a93e09f5Sgilles 	close(sp[1]);
1333dbc3cea0Smartijn 	close(errfd[1]);
1334a93e09f5Sgilles 	dup2(sp[0], STDIN_FILENO);
1335a93e09f5Sgilles 	dup2(sp[0], STDOUT_FILENO);
1336dbc3cea0Smartijn 	dup2(errfd[0], STDERR_FILENO);
1337a93e09f5Sgilles 
1338a93e09f5Sgilles 	if (chroot_path) {
1339a93e09f5Sgilles 		if (chroot(chroot_path) != 0 || chdir("/") != 0)
1340ff01b044Seric 			fatal("chroot: %s", chroot_path);
1341a93e09f5Sgilles 	}
1342a93e09f5Sgilles 
1343a93e09f5Sgilles 	if (setgroups(1, &gr->gr_gid) ||
1344a93e09f5Sgilles 	    setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid) ||
1345a93e09f5Sgilles 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
1346ff01b044Seric 		fatal("fork_filter_process: cannot drop privileges");
1347a93e09f5Sgilles 
1348df69c215Sderaadt 	if (closefrom(STDERR_FILENO + 1) == -1)
1349ff01b044Seric 		fatal("closefrom");
1350df69c215Sderaadt 	if (setsid() == -1)
1351ff01b044Seric 		fatal("setsid");
1352a93e09f5Sgilles 	if (signal(SIGPIPE, SIG_DFL) == SIG_ERR ||
1353a93e09f5Sgilles 	    signal(SIGINT, SIG_DFL) == SIG_ERR ||
1354a93e09f5Sgilles 	    signal(SIGTERM, SIG_DFL) == SIG_ERR ||
1355a93e09f5Sgilles 	    signal(SIGCHLD, SIG_DFL) == SIG_ERR ||
1356a93e09f5Sgilles 	    signal(SIGHUP, SIG_DFL) == SIG_ERR)
1357ff01b044Seric 		fatal("signal");
1358a93e09f5Sgilles 
13598dfdf75bSmartijn 	if (command[0] == '/')
13608dfdf75bSmartijn 		execr = snprintf(exec, sizeof(exec), "exec %s", command);
13618dfdf75bSmartijn 	else
13628dfdf75bSmartijn 		execr = snprintf(exec, sizeof(exec), "exec %s/%s",
13638dfdf75bSmartijn 		    PATH_LIBEXEC, command);
13648dfdf75bSmartijn 	if (execr >= (int) sizeof(exec))
1365ff01b044Seric 		fatalx("%s: exec path too long", name);
13668dfdf75bSmartijn 
1367dbc3cea0Smartijn 	/*
1368dbc3cea0Smartijn 	 * Wait for lka to acknowledge that it received the fd.
1369dbc3cea0Smartijn 	 * This prevents a race condition between the filter sending an error
1370dbc3cea0Smartijn 	 * message, and exiting and lka not being able to log it because of
1371dbc3cea0Smartijn 	 * SIGCHLD.
1372dbc3cea0Smartijn 	 * (Ab)use read to determine if the fd is installed; since stderr is
1373dbc3cea0Smartijn 	 * never going to be read from we can shutdown(2) the write-end in lka.
1374dbc3cea0Smartijn 	 */
1375dbc3cea0Smartijn 	if (read(STDERR_FILENO, &buf, 1) != 0)
1376ff01b044Seric 		fatalx("lka didn't properly close write end of error socket");
13778dfdf75bSmartijn 	if (system(exec) == -1)
1378ff01b044Seric 		fatal("system");
137940d55801Sgilles 
138040d55801Sgilles 	/* there's no successful exit from a processor */
1381fed76179Sgilles 	_exit(1);
1382a93e09f5Sgilles }
1383a93e09f5Sgilles 
1384a93e09f5Sgilles static void
138565c4fdfbSgilles forkmda(struct mproc *p, uint64_t id, struct deliver *deliver)
13863ef9cbf7Sgilles {
13871743d5a3Sjacekm 	char		 ebuf[128], sfn[32];
1388a8e22235Sgilles 	struct dispatcher	*dsp;
138911dbc40fSjacekm 	struct child	*child;
13903ef9cbf7Sgilles 	pid_t		 pid;
139101472467Sgilles 	int		 allout, pipefd[2];
1392a8e22235Sgilles 	struct passwd	*pw;
13932df8b630Sgilles 	const char	*pw_name;
1394a8e22235Sgilles 	uid_t	pw_uid;
1395a8e22235Sgilles 	gid_t	pw_gid;
1396a8e22235Sgilles 	const char	*pw_dir;
1397a8e22235Sgilles 
1398a8e22235Sgilles 	dsp = dict_xget(env->sc_dispatchers, deliver->dispatcher);
1399b69a9274Smillert 	if (dsp->type != DISPATCHER_LOCAL)
1400b69a9274Smillert 		fatalx("non-local dispatcher called from forkmda()");
14013ef9cbf7Sgilles 
14021c6ac251Seric 	log_debug("debug: smtpd: forking mda for session %016"PRIx64
1403a8e22235Sgilles 	    ": %s as %s", id, deliver->userinfo.username,
1404a8e22235Sgilles 	    dsp->u.local.user ? dsp->u.local.user : deliver->userinfo.username);
14053ef9cbf7Sgilles 
1406a8e22235Sgilles 	if (dsp->u.local.user) {
1407a8e22235Sgilles 		if ((pw = getpwnam(dsp->u.local.user)) == NULL) {
1408a8e22235Sgilles 			(void)snprintf(ebuf, sizeof ebuf,
1409a8e22235Sgilles 			    "delivery user '%s' does not exist",
1410a8e22235Sgilles 			    dsp->u.local.user);
14111a5b831aSmartijn 			m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0, -1);
14121a5b831aSmartijn 			m_add_id(p_dispatcher, id);
14131a5b831aSmartijn 			m_add_int(p_dispatcher, MDA_PERMFAIL);
14141a5b831aSmartijn 			m_add_int(p_dispatcher, EX_NOUSER);
14151a5b831aSmartijn 			m_add_string(p_dispatcher, ebuf);
14161a5b831aSmartijn 			m_close(p_dispatcher);
1417a224a58cSgilles 			return;
14187a999a6cSgilles 		}
14192df8b630Sgilles 		pw_name = pw->pw_name;
1420a8e22235Sgilles 		pw_uid = pw->pw_uid;
1421a8e22235Sgilles 		pw_gid = pw->pw_gid;
1422a8e22235Sgilles 		pw_dir = pw->pw_dir;
1423a8e22235Sgilles 	}
1424a8e22235Sgilles 	else {
14252df8b630Sgilles 		pw_name = deliver->userinfo.username;
1426a8e22235Sgilles 		pw_uid = deliver->userinfo.uid;
1427a8e22235Sgilles 		pw_gid = deliver->userinfo.gid;
1428a8e22235Sgilles 		pw_dir = deliver->userinfo.directory;
1429a8e22235Sgilles 	}
1430a224a58cSgilles 
143103e780bbSgilles 	if (pw_uid == 0 && (!dsp->u.local.is_mbox || deliver->mda_exec[0])) {
143203e780bbSgilles 		(void)snprintf(ebuf, sizeof ebuf, "MDA not allowed to deliver to: %s",
1433a8e22235Sgilles 			       deliver->userinfo.username);
14341a5b831aSmartijn 		m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0, -1);
14351a5b831aSmartijn 		m_add_id(p_dispatcher, id);
14361a5b831aSmartijn 		m_add_int(p_dispatcher, MDA_PERMFAIL);
14371a5b831aSmartijn 		m_add_int(p_dispatcher, EX_NOPERM);
14381a5b831aSmartijn 		m_add_string(p_dispatcher, ebuf);
14391a5b831aSmartijn 		m_close(p_dispatcher);
144069de2731Sgilles 		return;
144169de2731Sgilles 	}
144269de2731Sgilles 
14431dd9d999Sop 	if (dsp->u.local.is_mbox && dsp->u.local.command != NULL)
14441dd9d999Sop 		fatalx("serious memory corruption in privileged process");
14451dd9d999Sop 
1446df69c215Sderaadt 	if (pipe(pipefd) == -1) {
1447095d1e54Sgilles 		(void)snprintf(ebuf, sizeof ebuf, "pipe: %s", strerror(errno));
14481a5b831aSmartijn 		m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0, -1);
14491a5b831aSmartijn 		m_add_id(p_dispatcher, id);
14501a5b831aSmartijn 		m_add_int(p_dispatcher, MDA_TEMPFAIL);
14511a5b831aSmartijn 		m_add_int(p_dispatcher, EX_OSERR);
14521a5b831aSmartijn 		m_add_string(p_dispatcher, ebuf);
14531a5b831aSmartijn 		m_close(p_dispatcher);
14541743d5a3Sjacekm 		return;
14551743d5a3Sjacekm 	}
14561743d5a3Sjacekm 
14571743d5a3Sjacekm 	/* prepare file which captures stdout and stderr */
1458095d1e54Sgilles 	(void)strlcpy(sfn, "/tmp/smtpd.out.XXXXXXXXXXX", sizeof(sfn));
14591743d5a3Sjacekm 	allout = mkstemp(sfn);
1460c2d43ecaSderaadt 	if (allout == -1) {
1461095d1e54Sgilles 		(void)snprintf(ebuf, sizeof ebuf, "mkstemp: %s", strerror(errno));
14621a5b831aSmartijn 		m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0, -1);
14631a5b831aSmartijn 		m_add_id(p_dispatcher, id);
14641a5b831aSmartijn 		m_add_int(p_dispatcher, MDA_TEMPFAIL);
14651a5b831aSmartijn 		m_add_int(p_dispatcher, EX_OSERR);
14661a5b831aSmartijn 		m_add_string(p_dispatcher, ebuf);
14671a5b831aSmartijn 		m_close(p_dispatcher);
14683ef9cbf7Sgilles 		close(pipefd[0]);
14693ef9cbf7Sgilles 		close(pipefd[1]);
14701743d5a3Sjacekm 		return;
14711743d5a3Sjacekm 	}
14721743d5a3Sjacekm 	unlink(sfn);
14731743d5a3Sjacekm 
14741743d5a3Sjacekm 	pid = fork();
1475df69c215Sderaadt 	if (pid == -1) {
1476095d1e54Sgilles 		(void)snprintf(ebuf, sizeof ebuf, "fork: %s", strerror(errno));
14771a5b831aSmartijn 		m_create(p_dispatcher, IMSG_MDA_DONE, 0, 0, -1);
14781a5b831aSmartijn 		m_add_id(p_dispatcher, id);
14791a5b831aSmartijn 		m_add_int(p_dispatcher, MDA_TEMPFAIL);
14801a5b831aSmartijn 		m_add_int(p_dispatcher, EX_OSERR);
14811a5b831aSmartijn 		m_add_string(p_dispatcher, ebuf);
14821a5b831aSmartijn 		m_close(p_dispatcher);
14831743d5a3Sjacekm 		close(pipefd[0]);
14841743d5a3Sjacekm 		close(pipefd[1]);
14851743d5a3Sjacekm 		close(allout);
14861743d5a3Sjacekm 		return;
14873ef9cbf7Sgilles 	}
14883ef9cbf7Sgilles 
14891743d5a3Sjacekm 	/* parent passes the child fd over to mda */
14901743d5a3Sjacekm 	if (pid > 0) {
14913a305da9Seric 		child = child_add(pid, CHILD_MDA, NULL);
14921743d5a3Sjacekm 		child->mda_out = allout;
14931743d5a3Sjacekm 		child->mda_id = id;
14941743d5a3Sjacekm 		close(pipefd[0]);
1495aa1d5973Seric 		m_create(p, IMSG_MDA_FORK, 0, 0, pipefd[1]);
149665c4fdfbSgilles 		m_add_id(p, id);
149765c4fdfbSgilles 		m_close(p);
14981743d5a3Sjacekm 		return;
14991743d5a3Sjacekm 	}
150068213f7bSgilles 
150168213f7bSgilles 	/* mbox helper, create mailbox before privdrop if it doesn't exist */
150268213f7bSgilles 	if (dsp->u.local.is_mbox)
150368213f7bSgilles 		mda_mbox_init(deliver);
150468213f7bSgilles 
1505df69c215Sderaadt 	if (chdir(pw_dir) == -1 && chdir("/") == -1)
1506ff01b044Seric 		fatal("chdir");
1507a8e22235Sgilles 	if (setgroups(1, &pw_gid) ||
1508a8e22235Sgilles 	    setresgid(pw_gid, pw_gid, pw_gid) ||
1509a8e22235Sgilles 	    setresuid(pw_uid, pw_uid, pw_uid))
1510ff01b044Seric 		fatal("forkmda: cannot drop privileges");
1511df69c215Sderaadt 	if (dup2(pipefd[0], STDIN_FILENO) == -1 ||
1512df69c215Sderaadt 	    dup2(allout, STDOUT_FILENO) == -1 ||
1513df69c215Sderaadt 	    dup2(allout, STDERR_FILENO) == -1)
1514ff01b044Seric 		fatal("forkmda: dup2");
1515df69c215Sderaadt 	if (closefrom(STDERR_FILENO + 1) == -1)
1516ff01b044Seric 		fatal("closefrom");
1517df69c215Sderaadt 	if (setsid() == -1)
1518ff01b044Seric 		fatal("setsid");
15191743d5a3Sjacekm 	if (signal(SIGPIPE, SIG_DFL) == SIG_ERR ||
15201743d5a3Sjacekm 	    signal(SIGINT, SIG_DFL) == SIG_ERR ||
15211743d5a3Sjacekm 	    signal(SIGTERM, SIG_DFL) == SIG_ERR ||
15221743d5a3Sjacekm 	    signal(SIGCHLD, SIG_DFL) == SIG_ERR ||
15231743d5a3Sjacekm 	    signal(SIGHUP, SIG_DFL) == SIG_ERR)
1524ff01b044Seric 		fatal("signal");
15253ef9cbf7Sgilles 
15261743d5a3Sjacekm 	/* avoid hangs by setting 5m timeout */
152711dbc40fSjacekm 	alarm(300);
152811dbc40fSjacekm 
1529fea078e1Sgilles 	if (dsp->u.local.is_mbox &&
1530fea078e1Sgilles 	    dsp->u.local.mda_wrapper == NULL &&
1531fea078e1Sgilles 	    deliver->mda_exec[0] == '\0')
15322715edf2Sgilles 		mda_mbox(deliver);
15332715edf2Sgilles 	else
153401472467Sgilles 		mda_unpriv(dsp, deliver, pw_name, pw_dir);
15351743d5a3Sjacekm }
15363ef9cbf7Sgilles 
153714f5466bSeric static void
153814f5466bSeric offline_scan(int fd, short ev, void *arg)
153914f5466bSeric {
15405f4bd98fSgilles 	char		*path_argv[2];
15415f4bd98fSgilles 	FTS		*fts = arg;
15425f4bd98fSgilles 	FTSENT		*e;
154314f5466bSeric 	int		 n = 0;
154414f5466bSeric 
15455f4bd98fSgilles 	path_argv[0] = PATH_SPOOL PATH_OFFLINE;
15465f4bd98fSgilles 	path_argv[1] = NULL;
15475f4bd98fSgilles 
15485f4bd98fSgilles 	if (fts == NULL) {
154982614934Seric 		log_debug("debug: smtpd: scanning offline queue...");
15505f4bd98fSgilles 		fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
15515f4bd98fSgilles 		if (fts == NULL) {
15525f4bd98fSgilles 			log_warn("fts_open: %s", path_argv[0]);
15535f4bd98fSgilles 			return;
15545f4bd98fSgilles 		}
155514f5466bSeric 	}
155614f5466bSeric 
15575f4bd98fSgilles 	while ((e = fts_read(fts)) != NULL) {
15585f4bd98fSgilles 		if (e->fts_info != FTS_F)
155914f5466bSeric 			continue;
156014f5466bSeric 
15615f4bd98fSgilles 		/* offline files must be at depth 1 */
15625f4bd98fSgilles 		if (e->fts_level != 1)
15635f4bd98fSgilles 			continue;
15645f4bd98fSgilles 
15655f4bd98fSgilles 		/* offline file group must match parent directory group */
15665f4bd98fSgilles 		if (e->fts_statp->st_gid != e->fts_parent->fts_statp->st_gid)
15675f4bd98fSgilles 			continue;
15685f4bd98fSgilles 
15695f4bd98fSgilles 		if (e->fts_statp->st_size == 0) {
15705f4bd98fSgilles 			if (unlink(e->fts_accpath) == -1)
15715f4bd98fSgilles 				log_warnx("warn: smtpd: could not unlink %s", e->fts_accpath);
15725f4bd98fSgilles 			continue;
15735f4bd98fSgilles 		}
15745f4bd98fSgilles 
15750228dab0Smillert 		if (offline_add(e->fts_name, e->fts_statp->st_uid,
15760228dab0Smillert 		    e->fts_statp->st_gid)) {
157765c4fdfbSgilles 			log_warnx("warn: smtpd: "
15785f4bd98fSgilles 			    "could not add offline message %s", e->fts_name);
1579d44f36d1Seric 			continue;
1580d44f36d1Seric 		}
158114f5466bSeric 
158214f5466bSeric 		if ((n++) == OFFLINE_READMAX) {
15835f4bd98fSgilles 			evtimer_set(&offline_ev, offline_scan, fts);
158414f5466bSeric 			offline_timeout.tv_sec = 0;
158514f5466bSeric 			offline_timeout.tv_usec = 100000;
158614f5466bSeric 			evtimer_add(&offline_ev, &offline_timeout);
158714f5466bSeric 			return;
158814f5466bSeric 		}
158914f5466bSeric 	}
159014f5466bSeric 
159182614934Seric 	log_debug("debug: smtpd: offline scanning done");
15925f4bd98fSgilles 	fts_close(fts);
159314f5466bSeric }
159414f5466bSeric 
1595be925435Sgilles static int
15960228dab0Smillert offline_enqueue(char *name, uid_t uid, gid_t gid)
159725a5298cSjacekm {
159850e307aaSgilles 	char		*path;
1599e5b07014Sgilles 	struct stat	 sb;
160025a5298cSjacekm 	pid_t		 pid;
1601d44f36d1Seric 	struct child	*child;
160231805db2Sgilles 	struct passwd	*pw;
160350e307aaSgilles 	int		 pathlen;
1604e5b07014Sgilles 
160550e307aaSgilles 	pathlen = asprintf(&path, "%s/%s", PATH_SPOOL PATH_OFFLINE, name);
160650e307aaSgilles 	if (pathlen == -1) {
160750e307aaSgilles 		log_warnx("warn: smtpd: asprintf");
1608d44f36d1Seric 		return (-1);
1609d44f36d1Seric 	}
1610e5b07014Sgilles 
161150e307aaSgilles 	if (pathlen >= PATH_MAX) {
161250e307aaSgilles 		log_warnx("warn: smtpd: pathname exceeds PATH_MAX");
161350e307aaSgilles 		free(path);
1614d44f36d1Seric 		return (-1);
1615d44f36d1Seric 	}
161614f5466bSeric 
161782614934Seric 	log_debug("debug: smtpd: enqueueing offline message %s", path);
1618e5b07014Sgilles 
1619d44f36d1Seric 	if ((pid = fork()) == -1) {
162082614934Seric 		log_warn("warn: smtpd: fork");
1621d44f36d1Seric 		free(path);
1622d44f36d1Seric 		return (-1);
1623e5b07014Sgilles 	}
1624e5b07014Sgilles 
1625e5b07014Sgilles 	if (pid == 0) {
16260882b00eSsunil 		char	*envp[2], *p = NULL, *tmp;
1627cb6e8661Sgilles 		int	 fd;
1628e5b07014Sgilles 		FILE	*fp;
16290882b00eSsunil 		size_t	 sz = 0;
16300882b00eSsunil 		ssize_t	 len;
163125a5298cSjacekm 		arglist	 args;
163225a5298cSjacekm 
1633cb6e8661Sgilles 		if (closefrom(STDERR_FILENO + 1) == -1)
1634cb6e8661Sgilles 			_exit(1);
1635cb6e8661Sgilles 
1636c1392a69Seric 		memset(&args, 0, sizeof(args));
163725a5298cSjacekm 
1638cb6e8661Sgilles 		if ((fd = open(path, O_RDONLY|O_NOFOLLOW|O_NONBLOCK)) == -1) {
1639cb6e8661Sgilles 			log_warn("warn: smtpd: open: %s", path);
164025a5298cSjacekm 			_exit(1);
16410beae34bSjacekm 		}
1642e5b07014Sgilles 
1643cb6e8661Sgilles 		if (fstat(fd, &sb) == -1) {
1644cb6e8661Sgilles 			log_warn("warn: smtpd: fstat: %s", path);
1645cb6e8661Sgilles 			_exit(1);
1646cb6e8661Sgilles 		}
1647cb6e8661Sgilles 
1648cb6e8661Sgilles 		if (!S_ISREG(sb.st_mode)) {
1649cb6e8661Sgilles 			log_warnx("warn: smtpd: file %s (uid %d) not regular",
1650cb6e8661Sgilles 			    path, sb.st_uid);
1651cb6e8661Sgilles 			_exit(1);
1652cb6e8661Sgilles 		}
1653cb6e8661Sgilles 
1654cb6e8661Sgilles 		if (sb.st_nlink != 1) {
1655cb6e8661Sgilles 			log_warnx("warn: smtpd: file %s is hard-link", path);
1656e5b07014Sgilles 			_exit(1);
1657e5b07014Sgilles 		}
1658d44f36d1Seric 
16590228dab0Smillert 		if (sb.st_uid != uid) {
16600228dab0Smillert 			log_warnx("warn: smtpd: file %s has bad uid %d",
16610228dab0Smillert 			    path, sb.st_uid);
16620228dab0Smillert 			_exit(1);
16630228dab0Smillert 		}
16640228dab0Smillert 
16650228dab0Smillert 		if (sb.st_gid != gid) {
16660228dab0Smillert 			log_warnx("warn: smtpd: file %s has bad gid %d",
16670228dab0Smillert 			    path, sb.st_gid);
16680228dab0Smillert 			_exit(1);
16690228dab0Smillert 		}
16700228dab0Smillert 
167131805db2Sgilles 		pw = getpwuid(sb.st_uid);
167231805db2Sgilles 		if (pw == NULL) {
167382614934Seric 			log_warnx("warn: smtpd: getpwuid for uid %d failed",
1674d44f36d1Seric 			    sb.st_uid);
1675d44f36d1Seric 			_exit(1);
1676d44f36d1Seric 		}
1677d44f36d1Seric 
167831805db2Sgilles 		if (setgroups(1, &pw->pw_gid) ||
167931805db2Sgilles 		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
1680cb6e8661Sgilles 		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
1681d44f36d1Seric 			_exit(1);
1682d44f36d1Seric 
1683cb6e8661Sgilles 		if ((fp = fdopen(fd, "r")) == NULL)
1684d44f36d1Seric 			_exit(1);
1685e5b07014Sgilles 
168631805db2Sgilles 		if (chdir(pw->pw_dir) == -1 && chdir("/") == -1)
1687e5b07014Sgilles 			_exit(1);
1688e5b07014Sgilles 
1689e5b07014Sgilles 		if (setsid() == -1 ||
1690e5b07014Sgilles 		    signal(SIGPIPE, SIG_DFL) == SIG_ERR ||
1691e5b07014Sgilles 		    dup2(fileno(fp), STDIN_FILENO) == -1)
1692e5b07014Sgilles 			_exit(1);
1693e5b07014Sgilles 
16940882b00eSsunil 		if ((len = getline(&p, &sz, fp)) == -1)
1695e5b07014Sgilles 			_exit(1);
1696e5b07014Sgilles 
1697e5b07014Sgilles 		if (p[len - 1] != '\n')
1698e5b07014Sgilles 			_exit(1);
1699e5b07014Sgilles 		p[len - 1] = '\0';
1700e5b07014Sgilles 
1701e5b07014Sgilles 		addargs(&args, "%s", "sendmail");
17022a157de5Sgilles 		addargs(&args, "%s", "-S");
1703e5b07014Sgilles 
1704e5b07014Sgilles 		while ((tmp = strsep(&p, "|")) != NULL)
1705e5b07014Sgilles 			addargs(&args, "%s", tmp);
1706e5b07014Sgilles 
17070882b00eSsunil 		free(p);
1708e5b07014Sgilles 		if (lseek(fileno(fp), len, SEEK_SET) == -1)
1709e5b07014Sgilles 			_exit(1);
1710e5b07014Sgilles 
171125a5298cSjacekm 		envp[0] = "PATH=" _PATH_DEFPATH;
171225a5298cSjacekm 		envp[1] = (char *)NULL;
171325a5298cSjacekm 		environ = envp;
1714e5b07014Sgilles 
1715fb2f4678Sjacekm 		execvp(PATH_SMTPCTL, args.list);
171625a5298cSjacekm 		_exit(1);
171725a5298cSjacekm 	}
171825a5298cSjacekm 
171914f5466bSeric 	offline_running++;
17203a305da9Seric 	child = child_add(pid, CHILD_ENQUEUE_OFFLINE, NULL);
1721d44f36d1Seric 	child->path = path;
1722e5b07014Sgilles 
1723d44f36d1Seric 	return (0);
1724e5b07014Sgilles }
1725e5b07014Sgilles 
1726be925435Sgilles static int
17270228dab0Smillert offline_add(char *path, uid_t uid, gid_t gid)
17289a5146a5Seric {
172914f5466bSeric 	struct offline	*q;
17309a5146a5Seric 
173114f5466bSeric 	if (offline_running < OFFLINE_QUEUEMAX)
17329a5146a5Seric 		/* skip queue */
17330228dab0Smillert 		return offline_enqueue(path, uid, gid);
17349a5146a5Seric 
17359a5146a5Seric 	q = malloc(sizeof(*q) + strlen(path) + 1);
17369a5146a5Seric 	if (q == NULL)
17379a5146a5Seric 		return (-1);
17380228dab0Smillert 	q->uid = uid;
17390228dab0Smillert 	q->gid = gid;
17409a5146a5Seric 	q->path = (char *)q + sizeof(*q);
17419a5146a5Seric 	memmove(q->path, path, strlen(path) + 1);
174214f5466bSeric 	TAILQ_INSERT_TAIL(&offline_q, q, entry);
17439a5146a5Seric 
1744d44f36d1Seric 	return (0);
17459a5146a5Seric }
17469a5146a5Seric 
17479a5146a5Seric static void
174814f5466bSeric offline_done(void)
17499a5146a5Seric {
175014f5466bSeric 	struct offline	*q;
17519a5146a5Seric 
175214f5466bSeric 	offline_running--;
17539a5146a5Seric 
175414f5466bSeric 	while (offline_running < OFFLINE_QUEUEMAX) {
175514f5466bSeric 		if ((q = TAILQ_FIRST(&offline_q)) == NULL)
17569a5146a5Seric 			break; /* all done */
175714f5466bSeric 		TAILQ_REMOVE(&offline_q, q, entry);
17580228dab0Smillert 		offline_enqueue(q->path, q->uid, q->gid);
17599a5146a5Seric 		free(q);
17609a5146a5Seric 	}
17619a5146a5Seric }
17629a5146a5Seric 
17639a5146a5Seric static int
176465c4fdfbSgilles parent_forward_open(char *username, char *directory, uid_t uid, gid_t gid)
17657e2a8baeSgilles {
1766953aae25Sderaadt 	char		pathname[PATH_MAX];
17677e2a8baeSgilles 	int		fd;
1768cc138232Seric 	struct stat	sb;
17697e2a8baeSgilles 
17707e8a7d6bSeric 	if (!bsnprintf(pathname, sizeof (pathname), "%s/.forward",
1771f2a89170Ssunil 		directory)) {
1772f2a89170Ssunil 		log_warnx("warn: smtpd: %s: pathname too large", pathname);
1773f2a89170Ssunil 		return -1;
1774f2a89170Ssunil 	}
17757e2a8baeSgilles 
1776df69c215Sderaadt 	if (stat(directory, &sb) == -1) {
1777cc138232Seric 		log_warn("warn: smtpd: parent_forward_open: %s", directory);
1778cc138232Seric 		return -1;
1779cc138232Seric 	}
1780cc138232Seric 	if (sb.st_mode & S_ISVTX) {
1781cc138232Seric 		log_warnx("warn: smtpd: parent_forward_open: %s is sticky",
1782cc138232Seric 		    directory);
1783cc138232Seric 		errno = EAGAIN;
1784cc138232Seric 		return -1;
1785cc138232Seric 	}
1786cc138232Seric 
178765c4fdfbSgilles 	do {
1788cb6e8661Sgilles 		fd = open(pathname, O_RDONLY|O_NOFOLLOW|O_NONBLOCK);
178965c4fdfbSgilles 	} while (fd == -1 && errno == EINTR);
17907e2a8baeSgilles 	if (fd == -1) {
17917e2a8baeSgilles 		if (errno == ENOENT)
179265c4fdfbSgilles 			return -1;
179365c4fdfbSgilles 		if (errno == EMFILE || errno == ENFILE || errno == EIO) {
179465c4fdfbSgilles 			errno = EAGAIN;
179565c4fdfbSgilles 			return -1;
179665c4fdfbSgilles 		}
1797cb6e8661Sgilles 		if (errno == ELOOP)
1798cb6e8661Sgilles 			log_warnx("warn: smtpd: parent_forward_open: %s: "
1799cb6e8661Sgilles 			    "cannot follow symbolic links", pathname);
1800cb6e8661Sgilles 		else
180182614934Seric 			log_warn("warn: smtpd: parent_forward_open: %s", pathname);
18027e2a8baeSgilles 		return -1;
18037e2a8baeSgilles 	}
18047e2a8baeSgilles 
18051c8fd86dSgilles 	if (!secure_file(fd, pathname, directory, uid, 1)) {
1806ad8d242dSop 		log_warnx("warn: smtpd: %s: insecure file", pathname);
1807d0e4aaa3Sjacekm 		close(fd);
1808d0e4aaa3Sjacekm 		return -1;
1809d0e4aaa3Sjacekm 	}
18107e2a8baeSgilles 
18117e2a8baeSgilles 	return fd;
18127e2a8baeSgilles }
18137e2a8baeSgilles 
1814ed1929b6Sjacekm void
181565c4fdfbSgilles imsg_dispatch(struct mproc *p, struct imsg *imsg)
1816ed1929b6Sjacekm {
181721312c12Sgilles 	struct timespec	t0, t1, dt;
1818cbc009c1Seric 	int		msg;
1819ed1929b6Sjacekm 
182065c4fdfbSgilles 	if (imsg == NULL) {
182143962b9cSeric 		imsg_callback(p, imsg);
1822ed1929b6Sjacekm 		return;
1823ed1929b6Sjacekm 	}
1824ed1929b6Sjacekm 
182565c4fdfbSgilles 	log_imsg(smtpd_process, p->proc, imsg);
1826ed1929b6Sjacekm 
182765c4fdfbSgilles 	if (profiling & PROFILE_IMSG)
182821312c12Sgilles 		clock_gettime(CLOCK_MONOTONIC, &t0);
182921312c12Sgilles 
1830cbc009c1Seric 	msg = imsg->hdr.type;
183165c4fdfbSgilles 	imsg_callback(p, imsg);
183221312c12Sgilles 
183365c4fdfbSgilles 	if (profiling & PROFILE_IMSG) {
183421312c12Sgilles 		clock_gettime(CLOCK_MONOTONIC, &t1);
183521312c12Sgilles 		timespecsub(&t1, &t0, &dt);
183621312c12Sgilles 
1837e267740eSeric 		log_debug("profile-imsg: %s %s %s %d %lld.%09ld",
183865c4fdfbSgilles 		    proc_name(smtpd_process),
183965c4fdfbSgilles 		    proc_name(p->proc),
1840cbc009c1Seric 		    imsg_to_str(msg),
184165c4fdfbSgilles 		    (int)imsg->hdr.len,
1842e267740eSeric 		    (long long)dt.tv_sec,
1843e267740eSeric 		    dt.tv_nsec);
184421312c12Sgilles 
184565c4fdfbSgilles 		if (profiling & PROFILE_TOSTAT) {
184621312c12Sgilles 			char	key[STAT_KEY_SIZE];
184721312c12Sgilles 			/* can't profstat control process yet */
184821312c12Sgilles 			if (smtpd_process == PROC_CONTROL)
184921312c12Sgilles 				return;
185021312c12Sgilles 
185121312c12Sgilles 			if (!bsnprintf(key, sizeof key,
185221312c12Sgilles 				"profiling.imsg.%s.%s.%s",
185365c4fdfbSgilles 				proc_name(smtpd_process),
185465c4fdfbSgilles 				proc_name(p->proc),
1855cbc009c1Seric 				imsg_to_str(msg)))
18561e7a2462Sgilles 				return;
185721312c12Sgilles 			stat_set(key, stat_timespec(&dt));
185821312c12Sgilles 		}
185921312c12Sgilles 	}
1860ed1929b6Sjacekm }
1861ed1929b6Sjacekm 
1862576d0b55Sreyk void
1863b041f822Seric log_imsg(int to, int from, struct imsg *imsg)
1864b041f822Seric {
186565c4fdfbSgilles 
1866cc81b7c6Seric 	if (to == PROC_CONTROL && imsg->hdr.type == IMSG_STAT_SET)
186765c4fdfbSgilles 		return;
186865c4fdfbSgilles 
1869bfe8e0bcSeric 	log_trace(TRACE_IMSG, "imsg: %s <- %s: %s (len=%zu)",
187065c4fdfbSgilles 	    proc_name(to),
187165c4fdfbSgilles 	    proc_name(from),
1872b041f822Seric 	    imsg_to_str(imsg->hdr.type),
1873b041f822Seric 	    imsg->hdr.len - IMSG_HEADER_SIZE);
1874b041f822Seric }
1875b041f822Seric 
1876b041f822Seric const char *
187765c4fdfbSgilles proc_title(enum smtp_proc_type proc)
1878b041f822Seric {
1879b041f822Seric 	switch (proc) {
188065c4fdfbSgilles 	case PROC_PARENT:
188165c4fdfbSgilles 		return "[priv]";
1882299c4efeSeric 	case PROC_LKA:
1883299c4efeSeric 		return "lookup";
1884299c4efeSeric 	case PROC_QUEUE:
1885299c4efeSeric 		return "queue";
1886299c4efeSeric 	case PROC_CONTROL:
1887299c4efeSeric 		return "control";
1888299c4efeSeric 	case PROC_SCHEDULER:
1889299c4efeSeric 		return "scheduler";
18901a5b831aSmartijn 	case PROC_DISPATCHER:
18911a5b831aSmartijn 		return "dispatcher";
189276eb97c5Sreyk 	case PROC_CA:
18931a5b831aSmartijn 		return "crypto";
1894fb531d31Santon 	case PROC_CLIENT:
1895fb531d31Santon 		return "client";
1896a93e09f5Sgilles 	case PROC_PROCESSOR:
1897a93e09f5Sgilles 		return "processor";
1898b041f822Seric 	}
1899fb531d31Santon 	return "unknown";
1900b041f822Seric }
1901b041f822Seric 
1902b041f822Seric const char *
190365c4fdfbSgilles proc_name(enum smtp_proc_type proc)
190465c4fdfbSgilles {
190565c4fdfbSgilles 	switch (proc) {
190665c4fdfbSgilles 	case PROC_PARENT:
190765c4fdfbSgilles 		return "parent";
190865c4fdfbSgilles 	case PROC_LKA:
190965c4fdfbSgilles 		return "lka";
191065c4fdfbSgilles 	case PROC_QUEUE:
191165c4fdfbSgilles 		return "queue";
191265c4fdfbSgilles 	case PROC_CONTROL:
191365c4fdfbSgilles 		return "control";
191465c4fdfbSgilles 	case PROC_SCHEDULER:
191565c4fdfbSgilles 		return "scheduler";
19161a5b831aSmartijn 	case PROC_DISPATCHER:
19171a5b831aSmartijn 		return "dispatcher";
191876eb97c5Sreyk 	case PROC_CA:
191976eb97c5Sreyk 		return "ca";
1920299c4efeSeric 	case PROC_CLIENT:
1921299c4efeSeric 		return "client-proc";
192265c4fdfbSgilles 	default:
192365c4fdfbSgilles 		return "unknown";
192465c4fdfbSgilles 	}
192565c4fdfbSgilles }
192665c4fdfbSgilles 
192765c4fdfbSgilles #define CASE(x) case x : return #x
192865c4fdfbSgilles 
192965c4fdfbSgilles const char *
1930b041f822Seric imsg_to_str(int type)
1931b041f822Seric {
19326d095224Schl 	static char	 buf[32];
19336d095224Schl 
1934b041f822Seric 	switch (type) {
1935b041f822Seric 	CASE(IMSG_NONE);
1936aa1d5973Seric 
1937b041f822Seric 	CASE(IMSG_CTL_OK);
1938b041f822Seric 	CASE(IMSG_CTL_FAIL);
1939aa1d5973Seric 
1940aa1d5973Seric 	CASE(IMSG_CTL_GET_DIGEST);
1941aa1d5973Seric 	CASE(IMSG_CTL_GET_STATS);
194265c4fdfbSgilles 	CASE(IMSG_CTL_LIST_MESSAGES);
194365c4fdfbSgilles 	CASE(IMSG_CTL_LIST_ENVELOPES);
1944f9a337f4Seric 	CASE(IMSG_CTL_MTA_SHOW_HOSTS);
1945f9a337f4Seric 	CASE(IMSG_CTL_MTA_SHOW_RELAYS);
1946c5acbec8Seric 	CASE(IMSG_CTL_MTA_SHOW_ROUTES);
1947c5acbec8Seric 	CASE(IMSG_CTL_MTA_SHOW_HOSTSTATS);
19485b6a9ce9Seric 	CASE(IMSG_CTL_MTA_BLOCK);
19495b6a9ce9Seric 	CASE(IMSG_CTL_MTA_UNBLOCK);
19505b6a9ce9Seric 	CASE(IMSG_CTL_MTA_SHOW_BLOCK);
1951aa1d5973Seric 	CASE(IMSG_CTL_PAUSE_EVP);
1952aa1d5973Seric 	CASE(IMSG_CTL_PAUSE_MDA);
1953aa1d5973Seric 	CASE(IMSG_CTL_PAUSE_MTA);
1954aa1d5973Seric 	CASE(IMSG_CTL_PAUSE_SMTP);
1955aa1d5973Seric 	CASE(IMSG_CTL_PROFILE);
1956aa1d5973Seric 	CASE(IMSG_CTL_PROFILE_DISABLE);
1957aa1d5973Seric 	CASE(IMSG_CTL_PROFILE_ENABLE);
1958aa1d5973Seric 	CASE(IMSG_CTL_RESUME_EVP);
1959aa1d5973Seric 	CASE(IMSG_CTL_RESUME_MDA);
1960aa1d5973Seric 	CASE(IMSG_CTL_RESUME_MTA);
1961aa1d5973Seric 	CASE(IMSG_CTL_RESUME_SMTP);
1962aa1d5973Seric 	CASE(IMSG_CTL_RESUME_ROUTE);
1963aa1d5973Seric 	CASE(IMSG_CTL_REMOVE);
1964aa1d5973Seric 	CASE(IMSG_CTL_SCHEDULE);
1965aa1d5973Seric 	CASE(IMSG_CTL_SHOW_STATUS);
1966aa1d5973Seric 	CASE(IMSG_CTL_TRACE_DISABLE);
1967aa1d5973Seric 	CASE(IMSG_CTL_TRACE_ENABLE);
1968aa1d5973Seric 	CASE(IMSG_CTL_UPDATE_TABLE);
1969aa1d5973Seric 	CASE(IMSG_CTL_VERBOSE);
1970a9835440Ssunil 	CASE(IMSG_CTL_DISCOVER_EVPID);
1971a9835440Ssunil 	CASE(IMSG_CTL_DISCOVER_MSGID);
1972aa1d5973Seric 
1973aa1d5973Seric 	CASE(IMSG_CTL_SMTP_SESSION);
1974c5acbec8Seric 
197501eba458Seric 	CASE(IMSG_GETADDRINFO);
197601eba458Seric 	CASE(IMSG_GETADDRINFO_END);
197701eba458Seric 	CASE(IMSG_GETNAMEINFO);
197801361951Seric 	CASE(IMSG_RES_QUERY);
197901eba458Seric 
1980b88ab68dSeric 	CASE(IMSG_SETUP_KEY);
1981b88ab68dSeric 	CASE(IMSG_SETUP_PEER);
1982b88ab68dSeric 	CASE(IMSG_SETUP_DONE);
1983b88ab68dSeric 
1984b041f822Seric 	CASE(IMSG_CONF_START);
1985b041f822Seric 	CASE(IMSG_CONF_END);
1986caba6926Seric 
1987e16a1b99Schl 	CASE(IMSG_STAT_INCREMENT);
1988e16a1b99Schl 	CASE(IMSG_STAT_DECREMENT);
1989e16a1b99Schl 	CASE(IMSG_STAT_SET);
1990e16a1b99Schl 
1991aa1d5973Seric 	CASE(IMSG_LKA_AUTHENTICATE);
1992aa1d5973Seric 	CASE(IMSG_LKA_OPEN_FORWARD);
1993aa1d5973Seric 	CASE(IMSG_LKA_ENVELOPE_SUBMIT);
1994aa1d5973Seric 	CASE(IMSG_LKA_ENVELOPE_COMMIT);
199565c4fdfbSgilles 
1996aa1d5973Seric 	CASE(IMSG_QUEUE_DELIVER);
1997aa1d5973Seric 	CASE(IMSG_QUEUE_DELIVERY_OK);
1998aa1d5973Seric 	CASE(IMSG_QUEUE_DELIVERY_TEMPFAIL);
1999aa1d5973Seric 	CASE(IMSG_QUEUE_DELIVERY_PERMFAIL);
2000aa1d5973Seric 	CASE(IMSG_QUEUE_DELIVERY_LOOP);
2001a9835440Ssunil 	CASE(IMSG_QUEUE_DISCOVER_EVPID);
2002a9835440Ssunil 	CASE(IMSG_QUEUE_DISCOVER_MSGID);
2003acfdf0daSeric 	CASE(IMSG_QUEUE_ENVELOPE_ACK);
2004aa1d5973Seric 	CASE(IMSG_QUEUE_ENVELOPE_COMMIT);
2005aa1d5973Seric 	CASE(IMSG_QUEUE_ENVELOPE_REMOVE);
2006aa1d5973Seric 	CASE(IMSG_QUEUE_ENVELOPE_SCHEDULE);
2007aa1d5973Seric 	CASE(IMSG_QUEUE_ENVELOPE_SUBMIT);
2008aa1d5973Seric 	CASE(IMSG_QUEUE_HOLDQ_HOLD);
2009aa1d5973Seric 	CASE(IMSG_QUEUE_HOLDQ_RELEASE);
2010aa1d5973Seric 	CASE(IMSG_QUEUE_MESSAGE_COMMIT);
2011aa1d5973Seric 	CASE(IMSG_QUEUE_MESSAGE_ROLLBACK);
2012aa1d5973Seric 	CASE(IMSG_QUEUE_SMTP_SESSION);
2013aa1d5973Seric 	CASE(IMSG_QUEUE_TRANSFER);
2014aa1d5973Seric 
2015aa1d5973Seric 	CASE(IMSG_MDA_DELIVERY_OK);
2016aa1d5973Seric 	CASE(IMSG_MDA_DELIVERY_TEMPFAIL);
2017aa1d5973Seric 	CASE(IMSG_MDA_DELIVERY_PERMFAIL);
2018aa1d5973Seric 	CASE(IMSG_MDA_DELIVERY_LOOP);
2019aa1d5973Seric 	CASE(IMSG_MDA_DELIVERY_HOLD);
2020aa1d5973Seric 	CASE(IMSG_MDA_DONE);
2021aa1d5973Seric 	CASE(IMSG_MDA_FORK);
2022aa1d5973Seric 	CASE(IMSG_MDA_HOLDQ_RELEASE);
2023aa1d5973Seric 	CASE(IMSG_MDA_LOOKUP_USERINFO);
2024aa1d5973Seric 	CASE(IMSG_MDA_KILL);
2025aa1d5973Seric 	CASE(IMSG_MDA_OPEN_MESSAGE);
2026aa1d5973Seric 
2027aa1d5973Seric 	CASE(IMSG_MTA_DELIVERY_OK);
2028aa1d5973Seric 	CASE(IMSG_MTA_DELIVERY_TEMPFAIL);
2029aa1d5973Seric 	CASE(IMSG_MTA_DELIVERY_PERMFAIL);
2030aa1d5973Seric 	CASE(IMSG_MTA_DELIVERY_LOOP);
2031aa1d5973Seric 	CASE(IMSG_MTA_DELIVERY_HOLD);
2032aa1d5973Seric 	CASE(IMSG_MTA_DNS_HOST);
2033aa1d5973Seric 	CASE(IMSG_MTA_DNS_HOST_END);
2034aa1d5973Seric 	CASE(IMSG_MTA_DNS_MX);
2035aa1d5973Seric 	CASE(IMSG_MTA_DNS_MX_PREFERENCE);
2036aa1d5973Seric 	CASE(IMSG_MTA_HOLDQ_RELEASE);
2037aa1d5973Seric 	CASE(IMSG_MTA_LOOKUP_CREDENTIALS);
2038aa1d5973Seric 	CASE(IMSG_MTA_LOOKUP_SOURCE);
2039aa1d5973Seric 	CASE(IMSG_MTA_LOOKUP_HELO);
2040a8e22235Sgilles 	CASE(IMSG_MTA_LOOKUP_SMARTHOST);
2041aa1d5973Seric 	CASE(IMSG_MTA_OPEN_MESSAGE);
2042aa1d5973Seric 	CASE(IMSG_MTA_SCHEDULE);
2043aa1d5973Seric 
2044aa1d5973Seric 	CASE(IMSG_SCHED_ENVELOPE_BOUNCE);
2045aa1d5973Seric 	CASE(IMSG_SCHED_ENVELOPE_DELIVER);
2046aa1d5973Seric 	CASE(IMSG_SCHED_ENVELOPE_EXPIRE);
2047aa1d5973Seric 	CASE(IMSG_SCHED_ENVELOPE_INJECT);
2048aa1d5973Seric 	CASE(IMSG_SCHED_ENVELOPE_REMOVE);
2049aa1d5973Seric 	CASE(IMSG_SCHED_ENVELOPE_TRANSFER);
2050aa1d5973Seric 
2051aa1d5973Seric 	CASE(IMSG_SMTP_AUTHENTICATE);
2052aa1d5973Seric 	CASE(IMSG_SMTP_MESSAGE_COMMIT);
2053aa1d5973Seric 	CASE(IMSG_SMTP_MESSAGE_CREATE);
2054aa1d5973Seric 	CASE(IMSG_SMTP_MESSAGE_ROLLBACK);
2055aa1d5973Seric 	CASE(IMSG_SMTP_MESSAGE_OPEN);
205606405042Ssunil 	CASE(IMSG_SMTP_CHECK_SENDER);
2057aa1d5973Seric 	CASE(IMSG_SMTP_EXPAND_RCPT);
2058aa1d5973Seric 	CASE(IMSG_SMTP_LOOKUP_HELO);
2059aa1d5973Seric 
2060aa1d5973Seric 	CASE(IMSG_SMTP_REQ_CONNECT);
2061aa1d5973Seric 	CASE(IMSG_SMTP_REQ_HELO);
2062aa1d5973Seric 	CASE(IMSG_SMTP_REQ_MAIL);
2063aa1d5973Seric 	CASE(IMSG_SMTP_REQ_RCPT);
2064aa1d5973Seric 	CASE(IMSG_SMTP_REQ_DATA);
2065aa1d5973Seric 	CASE(IMSG_SMTP_REQ_EOM);
2066aa1d5973Seric 	CASE(IMSG_SMTP_EVENT_RSET);
2067aa1d5973Seric 	CASE(IMSG_SMTP_EVENT_COMMIT);
2068aa1d5973Seric 	CASE(IMSG_SMTP_EVENT_ROLLBACK);
2069aa1d5973Seric 	CASE(IMSG_SMTP_EVENT_DISCONNECT);
2070c4df3bf2Sreyk 
2071a93e09f5Sgilles 	CASE(IMSG_LKA_PROCESSOR_FORK);
2072dbc3cea0Smartijn 	CASE(IMSG_LKA_PROCESSOR_ERRFD);
2073a93e09f5Sgilles 
2074491f85b0Sgilles 	CASE(IMSG_REPORT_SMTP_LINK_CONNECT);
2075491f85b0Sgilles 	CASE(IMSG_REPORT_SMTP_LINK_DISCONNECT);
20762045dc71Sgilles 	CASE(IMSG_REPORT_SMTP_LINK_GREETING);
20772045dc71Sgilles 	CASE(IMSG_REPORT_SMTP_LINK_IDENTIFY);
207805eca6a7Sop 	CASE(IMSG_REPORT_SMTP_LINK_TLS);
20792045dc71Sgilles 	CASE(IMSG_REPORT_SMTP_LINK_AUTH);
20803ec0e812Sgilles 	CASE(IMSG_REPORT_SMTP_TX_RESET);
2081491f85b0Sgilles 	CASE(IMSG_REPORT_SMTP_TX_BEGIN);
208205eca6a7Sop 	CASE(IMSG_REPORT_SMTP_TX_MAIL);
208305eca6a7Sop 	CASE(IMSG_REPORT_SMTP_TX_RCPT);
2084491f85b0Sgilles 	CASE(IMSG_REPORT_SMTP_TX_ENVELOPE);
208505eca6a7Sop 	CASE(IMSG_REPORT_SMTP_TX_DATA);
2086491f85b0Sgilles 	CASE(IMSG_REPORT_SMTP_TX_COMMIT);
2087491f85b0Sgilles 	CASE(IMSG_REPORT_SMTP_TX_ROLLBACK);
2088491f85b0Sgilles 	CASE(IMSG_REPORT_SMTP_PROTOCOL_CLIENT);
2089491f85b0Sgilles 	CASE(IMSG_REPORT_SMTP_PROTOCOL_SERVER);
209005eca6a7Sop 	CASE(IMSG_REPORT_SMTP_FILTER_RESPONSE);
209105eca6a7Sop 	CASE(IMSG_REPORT_SMTP_TIMEOUT);
2092a055df58Seric 
20932b1a70ccSgilles 	CASE(IMSG_FILTER_SMTP_BEGIN);
20942b1a70ccSgilles 	CASE(IMSG_FILTER_SMTP_END);
20952b1a70ccSgilles 	CASE(IMSG_FILTER_SMTP_PROTOCOL);
20962b1a70ccSgilles 	CASE(IMSG_FILTER_SMTP_DATA_BEGIN);
20972b1a70ccSgilles 	CASE(IMSG_FILTER_SMTP_DATA_END);
2098a055df58Seric 
209941b8cf0bSmillert 	CASE(IMSG_CA_RSA_PRIVENC);
210041b8cf0bSmillert 	CASE(IMSG_CA_RSA_PRIVDEC);
210141b8cf0bSmillert 	CASE(IMSG_CA_ECDSA_SIGN);
210205eca6a7Sop 
2103b041f822Seric 	default:
2104095d1e54Sgilles 		(void)snprintf(buf, sizeof(buf), "IMSG_??? (%d)", type);
21056d095224Schl 
21066d095224Schl 		return buf;
2107b041f822Seric 	}
2108b041f822Seric }
210965c4fdfbSgilles 
211065c4fdfbSgilles int
211165c4fdfbSgilles parent_auth_user(const char *username, const char *password)
211265c4fdfbSgilles {
2113953aae25Sderaadt 	char	user[LOGIN_NAME_MAX];
2114953aae25Sderaadt 	char	pass[LINE_MAX];
211565c4fdfbSgilles 	int	ret;
211665c4fdfbSgilles 
2117095d1e54Sgilles 	(void)strlcpy(user, username, sizeof(user));
2118095d1e54Sgilles 	(void)strlcpy(pass, password, sizeof(pass));
211965c4fdfbSgilles 
212065c4fdfbSgilles 	ret = auth_userokay(user, NULL, "auth-smtp", pass);
212165c4fdfbSgilles 	if (ret)
212265c4fdfbSgilles 		return LKA_OK;
212365c4fdfbSgilles 	return LKA_PERMFAIL;
212465c4fdfbSgilles }
2125