xref: /openbsd-src/usr.sbin/smtpd/control.c (revision 0b7734b3d77bb9b21afec6f4621cae6c805dbd45)
1 /*	$OpenBSD: control.c,v 1.113 2016/05/28 21:21:20 eric Exp $	*/
2 
3 /*
4  * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org>
5  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
6  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
7  *
8  * Permission to use, copy, modify, and distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  */
20 
21 #include <sys/types.h>
22 #include <sys/queue.h>
23 #include <sys/tree.h>
24 #include <sys/stat.h>
25 #include <sys/socket.h>
26 #include <sys/un.h>
27 
28 #include <err.h>
29 #include <errno.h>
30 #include <event.h>
31 #include <fcntl.h>
32 #include <imsg.h>
33 #include <pwd.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <time.h>
39 #include <unistd.h>
40 #include <limits.h>
41 
42 #include "smtpd.h"
43 #include "log.h"
44 
45 #define CONTROL_BACKLOG 5
46 
47 struct ctl_conn {
48 	uint32_t		 id;
49 	uint8_t			 flags;
50 #define CTL_CONN_NOTIFY		 0x01
51 	struct mproc		 mproc;
52 	uid_t			 euid;
53 	gid_t			 egid;
54 };
55 
56 struct {
57 	struct event		 ev;
58 	int			 fd;
59 } control_state;
60 
61 static void control_imsg(struct mproc *, struct imsg *);
62 static void control_shutdown(void);
63 static void control_listen(void);
64 static void control_accept(int, short, void *);
65 static void control_close(struct ctl_conn *);
66 static void control_sig_handler(int, short, void *);
67 static void control_dispatch_ext(struct mproc *, struct imsg *);
68 static void control_digest_update(const char *, size_t, int);
69 static void control_broadcast_verbose(int, int);
70 
71 static struct stat_backend *stat_backend = NULL;
72 extern const char *backend_stat;
73 
74 static uint64_t			connid = 0;
75 static struct tree		ctl_conns;
76 static struct tree		ctl_count;
77 static struct stat_digest	digest;
78 
79 #define	CONTROL_FD_RESERVE		5
80 #define	CONTROL_MAXCONN_PER_CLIENT	32
81 
82 static void
83 control_imsg(struct mproc *p, struct imsg *imsg)
84 {
85 	struct ctl_conn		*c;
86 	struct stat_value	 val;
87 	struct msg		 m;
88 	const char		*key;
89 	const void		*data;
90 	size_t			 sz;
91 
92 	if (p->proc == PROC_PONY) {
93 		switch (imsg->hdr.type) {
94 		case IMSG_CTL_SMTP_SESSION:
95 			c = tree_get(&ctl_conns, imsg->hdr.peerid);
96 			if (c == NULL)
97 				return;
98 			m_compose(&c->mproc, IMSG_CTL_OK, 0, 0, imsg->fd,
99 			    NULL, 0);
100 			return;
101 		}
102 	}
103 	if (p->proc == PROC_SCHEDULER) {
104 		switch (imsg->hdr.type) {
105 		case IMSG_CTL_OK:
106 		case IMSG_CTL_FAIL:
107 		case IMSG_CTL_LIST_MESSAGES:
108 			c = tree_get(&ctl_conns, imsg->hdr.peerid);
109 			if (c == NULL)
110 				return;
111 			imsg->hdr.peerid = 0;
112 			m_forward(&c->mproc, imsg);
113 			return;
114 		}
115 	}
116 	if (p->proc == PROC_QUEUE) {
117 		switch (imsg->hdr.type) {
118 		case IMSG_CTL_LIST_ENVELOPES:
119 		case IMSG_CTL_DISCOVER_EVPID:
120 		case IMSG_CTL_DISCOVER_MSGID:
121 		case IMSG_CTL_UNCORRUPT_MSGID:
122 			c = tree_get(&ctl_conns, imsg->hdr.peerid);
123 			if (c == NULL)
124 				return;
125 			m_forward(&c->mproc, imsg);
126 			return;
127 		}
128 	}
129 	if (p->proc == PROC_PONY) {
130 		switch (imsg->hdr.type) {
131 		case IMSG_CTL_OK:
132 		case IMSG_CTL_FAIL:
133 		case IMSG_CTL_MTA_SHOW_HOSTS:
134 		case IMSG_CTL_MTA_SHOW_RELAYS:
135 		case IMSG_CTL_MTA_SHOW_ROUTES:
136 		case IMSG_CTL_MTA_SHOW_HOSTSTATS:
137 		case IMSG_CTL_MTA_SHOW_BLOCK:
138 			c = tree_get(&ctl_conns, imsg->hdr.peerid);
139 			if (c == NULL)
140 				return;
141 			imsg->hdr.peerid = 0;
142 			m_forward(&c->mproc, imsg);
143 			return;
144 		}
145 	}
146 
147 	switch (imsg->hdr.type) {
148 	case IMSG_STAT_INCREMENT:
149 		m_msg(&m, imsg);
150 		m_get_string(&m, &key);
151 		m_get_data(&m, &data, &sz);
152 		m_end(&m);
153 		if (sz != sizeof(val))
154 			fatalx("control: IMSG_STAT_INCREMENT size mismatch");
155 		memmove(&val, data, sz);
156 		if (stat_backend)
157 			stat_backend->increment(key, val.u.counter);
158 		control_digest_update(key, val.u.counter, 1);
159 		return;
160 	case IMSG_STAT_DECREMENT:
161 		m_msg(&m, imsg);
162 		m_get_string(&m, &key);
163 		m_get_data(&m, &data, &sz);
164 		m_end(&m);
165 		if (sz != sizeof(val))
166 			fatalx("control: IMSG_STAT_DECREMENT size mismatch");
167 		memmove(&val, data, sz);
168 		if (stat_backend)
169 			stat_backend->decrement(key, val.u.counter);
170 		control_digest_update(key, val.u.counter, 0);
171 		return;
172 	case IMSG_STAT_SET:
173 		m_msg(&m, imsg);
174 		m_get_string(&m, &key);
175 		m_get_data(&m, &data, &sz);
176 		m_end(&m);
177 		if (sz != sizeof(val))
178 			fatalx("control: IMSG_STAT_SET size mismatch");
179 		memmove(&val, data, sz);
180 		if (stat_backend)
181 			stat_backend->set(key, &val);
182 		return;
183 	}
184 
185 	errx(1, "control_imsg: unexpected %s imsg",
186 	    imsg_to_str(imsg->hdr.type));
187 }
188 
189 static void
190 control_sig_handler(int sig, short event, void *p)
191 {
192 	switch (sig) {
193 	case SIGINT:
194 	case SIGTERM:
195 		control_shutdown();
196 		break;
197 	default:
198 		fatalx("control_sig_handler: unexpected signal");
199 	}
200 }
201 
202 int
203 control_create_socket(void)
204 {
205 	struct sockaddr_un	s_un;
206 	int			fd;
207 	mode_t			old_umask;
208 
209 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
210 		fatal("control: socket");
211 
212 	memset(&s_un, 0, sizeof(s_un));
213 	s_un.sun_family = AF_UNIX;
214 	if (strlcpy(s_un.sun_path, SMTPD_SOCKET,
215 	    sizeof(s_un.sun_path)) >= sizeof(s_un.sun_path))
216 		fatal("control: socket name too long");
217 
218 	if (connect(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == 0)
219 		fatalx("control socket already listening");
220 
221 	if (unlink(SMTPD_SOCKET) == -1)
222 		if (errno != ENOENT)
223 			fatal("control: cannot unlink socket");
224 
225 	old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
226 	if (bind(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) {
227 		(void)umask(old_umask);
228 		fatal("control: bind");
229 	}
230 	(void)umask(old_umask);
231 
232 	if (chmod(SMTPD_SOCKET,
233 		S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) {
234 		(void)unlink(SMTPD_SOCKET);
235 		fatal("control: chmod");
236 	}
237 
238 	io_set_nonblocking(fd);
239 	control_state.fd = fd;
240 
241 	return fd;
242 }
243 
244 int
245 control(void)
246 {
247 	struct passwd		*pw;
248 	struct event		 ev_sigint;
249 	struct event		 ev_sigterm;
250 
251 	purge_config(PURGE_EVERYTHING);
252 
253 	if ((pw = getpwnam(SMTPD_USER)) == NULL)
254 		fatalx("unknown user " SMTPD_USER);
255 
256 	stat_backend = env->sc_stat;
257 	stat_backend->init();
258 
259 	if (chroot(PATH_CHROOT) == -1)
260 		fatal("control: chroot");
261 	if (chdir("/") == -1)
262 		fatal("control: chdir(\"/\")");
263 
264 	config_process(PROC_CONTROL);
265 
266 	if (setgroups(1, &pw->pw_gid) ||
267 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
268 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
269 		fatal("control: cannot drop privileges");
270 
271 	imsg_callback = control_imsg;
272 	event_init();
273 
274 	signal_set(&ev_sigint, SIGINT, control_sig_handler, NULL);
275 	signal_set(&ev_sigterm, SIGTERM, control_sig_handler, NULL);
276 	signal_add(&ev_sigint, NULL);
277 	signal_add(&ev_sigterm, NULL);
278 	signal(SIGPIPE, SIG_IGN);
279 	signal(SIGHUP, SIG_IGN);
280 
281 	tree_init(&ctl_conns);
282 	tree_init(&ctl_count);
283 
284 	memset(&digest, 0, sizeof digest);
285 	digest.startup = time(NULL);
286 
287 	config_peer(PROC_SCHEDULER);
288 	config_peer(PROC_QUEUE);
289 	config_peer(PROC_PARENT);
290 	config_peer(PROC_LKA);
291 	config_peer(PROC_PONY);
292 	config_peer(PROC_CA);
293 	config_done();
294 
295 	control_listen();
296 
297 	if (pledge("stdio unix recvfd sendfd", NULL) == -1)
298 		err(1, "pledge");
299 
300 	if (event_dispatch() < 0)
301 		fatal("event_dispatch");
302 	control_shutdown();
303 
304 	return (0);
305 }
306 
307 static void
308 control_shutdown(void)
309 {
310 	log_info("info: control process exiting");
311 	_exit(0);
312 }
313 
314 static void
315 control_listen(void)
316 {
317 	if (listen(control_state.fd, CONTROL_BACKLOG) == -1)
318 		fatal("control_listen");
319 
320 	event_set(&control_state.ev, control_state.fd, EV_READ|EV_PERSIST,
321 	    control_accept, NULL);
322 	event_add(&control_state.ev, NULL);
323 }
324 
325 /* ARGSUSED */
326 static void
327 control_accept(int listenfd, short event, void *arg)
328 {
329 	int			 connfd;
330 	socklen_t		 len;
331 	struct sockaddr_un	 s_un;
332 	struct ctl_conn		*c;
333 	size_t			*count;
334 	uid_t			 euid;
335 	gid_t			 egid;
336 
337 	if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE)
338 		goto pause;
339 
340 	len = sizeof(s_un);
341 	if ((connfd = accept(listenfd, (struct sockaddr *)&s_un, &len)) == -1) {
342 		if (errno == ENFILE || errno == EMFILE)
343 			goto pause;
344 		if (errno == EINTR || errno == EWOULDBLOCK ||
345 		    errno == ECONNABORTED)
346 			return;
347 		fatal("control_accept: accept");
348 	}
349 
350 	io_set_nonblocking(connfd);
351 
352 	if (getpeereid(connfd, &euid, &egid) == -1)
353 		fatal("getpeereid");
354 
355 	count = tree_get(&ctl_count, euid);
356 	if (count == NULL) {
357 		count = xcalloc(1, sizeof *count, "control_accept");
358 		tree_xset(&ctl_count, euid, count);
359 	}
360 
361 	if (*count == CONTROL_MAXCONN_PER_CLIENT) {
362 		close(connfd);
363 		log_warnx("warn: too many connections to control socket "
364 		    "from user with uid %lu", (unsigned long int)euid);
365 		return;
366 	}
367 	(*count)++;
368 
369 	do {
370 		++connid;
371 	} while (tree_get(&ctl_conns, connid));
372 
373 	c = xcalloc(1, sizeof(*c), "control_accept");
374 	c->euid = euid;
375 	c->egid = egid;
376 	c->id = connid;
377 	c->mproc.proc = PROC_CLIENT;
378 	c->mproc.handler = control_dispatch_ext;
379 	c->mproc.data = c;
380 	mproc_init(&c->mproc, connfd);
381 	mproc_enable(&c->mproc);
382 	tree_xset(&ctl_conns, c->id, c);
383 
384 	stat_backend->increment("control.session", 1);
385 	return;
386 
387 pause:
388 	log_warnx("warn: ctl client limit hit, disabling new connections");
389 	event_del(&control_state.ev);
390 }
391 
392 static void
393 control_close(struct ctl_conn *c)
394 {
395 	size_t	*count;
396 
397 	count = tree_xget(&ctl_count, c->euid);
398 	(*count)--;
399 	if (*count == 0) {
400 		tree_xpop(&ctl_count, c->euid);
401 		free(count);
402 	}
403 	tree_xpop(&ctl_conns, c->id);
404 	mproc_clear(&c->mproc);
405 	free(c);
406 
407 	stat_backend->decrement("control.session", 1);
408 
409 	if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE)
410 		return;
411 
412 	if (!event_pending(&control_state.ev, EV_READ, NULL)) {
413 		log_warnx("warn: re-enabling ctl connections");
414 		event_add(&control_state.ev, NULL);
415 	}
416 }
417 
418 static void
419 control_digest_update(const char *key, size_t value, int incr)
420 {
421 	size_t	*p;
422 
423 	p = NULL;
424 
425 	if (!strcmp(key, "smtp.session")) {
426 		if (incr)
427 			p = &digest.clt_connect;
428 		else
429 			digest.clt_disconnect += value;
430 	}
431 	else if (!strcmp(key, "scheduler.envelope")) {
432 		if (incr)
433 			p = &digest.evp_enqueued;
434 		else
435 			digest.evp_dequeued += value;
436 	}
437 	else if  (!strcmp(key, "scheduler.envelope.expired"))
438 		p = &digest.evp_expired;
439 	else if  (!strcmp(key, "scheduler.envelope.removed"))
440 		p = &digest.evp_removed;
441 	else if  (!strcmp(key, "scheduler.delivery.ok"))
442 		p = &digest.dlv_ok;
443 	else if  (!strcmp(key, "scheduler.delivery.permfail"))
444 		p = &digest.dlv_permfail;
445 	else if  (!strcmp(key, "scheduler.delivery.tempfail"))
446 		p = &digest.dlv_tempfail;
447 	else if  (!strcmp(key, "scheduler.delivery.loop"))
448 		p = &digest.dlv_loop;
449 
450 	else if  (!strcmp(key, "queue.bounce"))
451 		p = &digest.evp_bounce;
452 
453 	if (p) {
454 		if (incr)
455 			*p = *p + value;
456 		else
457 			*p = *p - value;
458 	}
459 }
460 
461 /* ARGSUSED */
462 static void
463 control_dispatch_ext(struct mproc *p, struct imsg *imsg)
464 {
465 	struct sockaddr_storage	 ss;
466 	struct ctl_conn		*c;
467 	int			 v;
468 	struct stat_kv		*kvp;
469 	char			*key;
470 	struct stat_value	 val;
471 	size_t			 len;
472 	uint64_t		 evpid;
473 	uint32_t		 msgid;
474 
475 	c = p->data;
476 
477 	if (imsg == NULL) {
478 		control_close(c);
479 		return;
480 	}
481 
482 	if (imsg->hdr.peerid != IMSG_VERSION) {
483 		m_compose(p, IMSG_CTL_FAIL, IMSG_VERSION, 0, -1, NULL, 0);
484 		return;
485 	}
486 
487 	switch (imsg->hdr.type) {
488 	case IMSG_CTL_SMTP_SESSION:
489 		if (env->sc_flags & (SMTPD_SMTP_PAUSED | SMTPD_EXITING)) {
490 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
491 			return;
492 		}
493 		m_compose(p_pony, IMSG_CTL_SMTP_SESSION, c->id, 0, -1,
494 		    &c->euid, sizeof(c->euid));
495 		return;
496 
497 	case IMSG_CTL_GET_DIGEST:
498 		if (c->euid)
499 			goto badcred;
500 		digest.timestamp = time(NULL);
501 		m_compose(p, IMSG_CTL_GET_DIGEST, 0, 0, -1, &digest, sizeof digest);
502 		return;
503 
504 	case IMSG_CTL_GET_STATS:
505 		if (c->euid)
506 			goto badcred;
507 		kvp = imsg->data;
508 		if (!stat_backend->iter(&kvp->iter, &key, &val))
509 			kvp->iter = NULL;
510 		else {
511 			(void)strlcpy(kvp->key, key, sizeof kvp->key);
512 			kvp->val = val;
513 		}
514 		m_compose(p, IMSG_CTL_GET_STATS, 0, 0, -1, kvp, sizeof *kvp);
515 		return;
516 
517 	case IMSG_CTL_SHUTDOWN:
518 		/* NEEDS_FIX */
519 		log_debug("debug: received shutdown request");
520 
521 		if (c->euid)
522 			goto badcred;
523 
524 		if (env->sc_flags & SMTPD_EXITING) {
525 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
526 			return;
527 		}
528 		env->sc_flags |= SMTPD_EXITING;
529 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
530 		m_compose(p_parent, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0);
531 		return;
532 
533 	case IMSG_CTL_VERBOSE:
534 		if (c->euid)
535 			goto badcred;
536 
537 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
538 			goto badcred;
539 
540 		memcpy(&v, imsg->data, sizeof(v));
541 		verbose = v;
542 		log_verbose(verbose);
543 
544 		control_broadcast_verbose(IMSG_CTL_VERBOSE, verbose);
545 
546 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
547 		return;
548 
549 	case IMSG_CTL_TRACE_ENABLE:
550 		if (c->euid)
551 			goto badcred;
552 
553 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
554 			goto badcred;
555 
556 		memcpy(&v, imsg->data, sizeof(v));
557 		verbose |= v;
558 		log_verbose(verbose);
559 
560 		control_broadcast_verbose(IMSG_CTL_VERBOSE, verbose);
561 
562 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
563 		return;
564 
565 	case IMSG_CTL_TRACE_DISABLE:
566 		if (c->euid)
567 			goto badcred;
568 
569 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
570 			goto badcred;
571 
572 		memcpy(&v, imsg->data, sizeof(v));
573 		verbose &= ~v;
574 		log_verbose(verbose);
575 
576 		control_broadcast_verbose(IMSG_CTL_VERBOSE, verbose);
577 
578 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
579 		return;
580 
581 	case IMSG_CTL_PROFILE_ENABLE:
582 		if (c->euid)
583 			goto badcred;
584 
585 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
586 			goto badcred;
587 
588 		memcpy(&v, imsg->data, sizeof(v));
589 		profiling |= v;
590 
591 		control_broadcast_verbose(IMSG_CTL_PROFILE, profiling);
592 
593 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
594 		return;
595 
596 	case IMSG_CTL_PROFILE_DISABLE:
597 		if (c->euid)
598 			goto badcred;
599 
600 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
601 			goto badcred;
602 
603 		memcpy(&v, imsg->data, sizeof(v));
604 		profiling &= ~v;
605 
606 		control_broadcast_verbose(IMSG_CTL_PROFILE, profiling);
607 
608 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
609 		return;
610 
611 	case IMSG_CTL_PAUSE_EVP:
612 		if (c->euid)
613 			goto badcred;
614 
615 		imsg->hdr.peerid = c->id;
616 		m_forward(p_scheduler, imsg);
617 		return;
618 
619 	case IMSG_CTL_PAUSE_MDA:
620 		if (c->euid)
621 			goto badcred;
622 
623 		if (env->sc_flags & SMTPD_MDA_PAUSED) {
624 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
625 			return;
626 		}
627 		log_info("info: mda paused");
628 		env->sc_flags |= SMTPD_MDA_PAUSED;
629 		m_compose(p_queue, IMSG_CTL_PAUSE_MDA, 0, 0, -1, NULL, 0);
630 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
631 		return;
632 
633 	case IMSG_CTL_PAUSE_MTA:
634 		if (c->euid)
635 			goto badcred;
636 
637 		if (env->sc_flags & SMTPD_MTA_PAUSED) {
638 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
639 			return;
640 		}
641 		log_info("info: mta paused");
642 		env->sc_flags |= SMTPD_MTA_PAUSED;
643 		m_compose(p_queue, IMSG_CTL_PAUSE_MTA, 0, 0, -1, NULL, 0);
644 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
645 		return;
646 
647 	case IMSG_CTL_PAUSE_SMTP:
648 		if (c->euid)
649 			goto badcred;
650 
651 		if (env->sc_flags & SMTPD_SMTP_PAUSED) {
652 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
653 			return;
654 		}
655 		log_info("info: smtp paused");
656 		env->sc_flags |= SMTPD_SMTP_PAUSED;
657 		m_compose(p_pony, IMSG_CTL_PAUSE_SMTP, 0, 0, -1, NULL, 0);
658 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
659 		return;
660 
661 	case IMSG_CTL_RESUME_EVP:
662 		if (c->euid)
663 			goto badcred;
664 
665 		imsg->hdr.peerid = c->id;
666 		m_forward(p_scheduler, imsg);
667 		return;
668 
669 	case IMSG_CTL_RESUME_MDA:
670 		if (c->euid)
671 			goto badcred;
672 
673 		if (!(env->sc_flags & SMTPD_MDA_PAUSED)) {
674 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
675 			return;
676 		}
677 		log_info("info: mda resumed");
678 		env->sc_flags &= ~SMTPD_MDA_PAUSED;
679 		m_compose(p_queue, IMSG_CTL_RESUME_MDA, 0, 0, -1, NULL, 0);
680 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
681 		return;
682 
683 	case IMSG_CTL_RESUME_MTA:
684 		if (c->euid)
685 			goto badcred;
686 
687 		if (!(env->sc_flags & SMTPD_MTA_PAUSED)) {
688 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
689 			return;
690 		}
691 		log_info("info: mta resumed");
692 		env->sc_flags &= ~SMTPD_MTA_PAUSED;
693 		m_compose(p_queue, IMSG_CTL_RESUME_MTA, 0, 0, -1, NULL, 0);
694 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
695 		return;
696 
697 	case IMSG_CTL_RESUME_SMTP:
698 		if (c->euid)
699 			goto badcred;
700 
701 		if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) {
702 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
703 			return;
704 		}
705 		log_info("info: smtp resumed");
706 		env->sc_flags &= ~SMTPD_SMTP_PAUSED;
707 		m_forward(p_pony, imsg);
708 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
709 		return;
710 
711 	case IMSG_CTL_RESUME_ROUTE:
712 		if (c->euid)
713 			goto badcred;
714 
715 		m_forward(p_pony, imsg);
716 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
717 		return;
718 
719 	case IMSG_CTL_LIST_MESSAGES:
720 		if (c->euid)
721 			goto badcred;
722 		m_compose(p_scheduler, IMSG_CTL_LIST_MESSAGES, c->id, 0, -1,
723 		    imsg->data, imsg->hdr.len - sizeof(imsg->hdr));
724 		return;
725 
726 	case IMSG_CTL_LIST_ENVELOPES:
727 		if (c->euid)
728 			goto badcred;
729 		m_compose(p_scheduler, IMSG_CTL_LIST_ENVELOPES, c->id, 0, -1,
730 		    imsg->data, imsg->hdr.len - sizeof(imsg->hdr));
731 		return;
732 
733 	case IMSG_CTL_MTA_SHOW_HOSTS:
734 	case IMSG_CTL_MTA_SHOW_RELAYS:
735 	case IMSG_CTL_MTA_SHOW_ROUTES:
736 	case IMSG_CTL_MTA_SHOW_HOSTSTATS:
737 	case IMSG_CTL_MTA_SHOW_BLOCK:
738 		if (c->euid)
739 			goto badcred;
740 
741 		imsg->hdr.peerid = c->id;
742 		m_forward(p_pony, imsg);
743 		return;
744 
745 	case IMSG_CTL_SHOW_STATUS:
746 		if (c->euid)
747 			goto badcred;
748 
749 		m_compose(p, IMSG_CTL_SHOW_STATUS, 0, 0, -1, &env->sc_flags,
750 		    sizeof(env->sc_flags));
751 		return;
752 
753 	case IMSG_CTL_MTA_BLOCK:
754 	case IMSG_CTL_MTA_UNBLOCK:
755 		if (c->euid)
756 			goto badcred;
757 
758 		if (imsg->hdr.len - IMSG_HEADER_SIZE <= sizeof(ss))
759 			goto invalid;
760 		memmove(&ss, imsg->data, sizeof(ss));
761 		m_create(p_pony, imsg->hdr.type, c->id, 0, -1);
762 		m_add_sockaddr(p_pony, (struct sockaddr *)&ss);
763 		m_add_string(p_pony, (char *)imsg->data + sizeof(ss));
764 		m_close(p_pony);
765 		return;
766 
767 	case IMSG_CTL_SCHEDULE:
768 		if (c->euid)
769 			goto badcred;
770 
771 		imsg->hdr.peerid = c->id;
772 		m_forward(p_scheduler, imsg);
773 		return;
774 
775 	case IMSG_CTL_REMOVE:
776 		if (c->euid)
777 			goto badcred;
778 
779 		imsg->hdr.peerid = c->id;
780 		m_forward(p_scheduler, imsg);
781 		return;
782 
783 	case IMSG_CTL_UPDATE_TABLE:
784 		if (c->euid)
785 			goto badcred;
786 
787 		/* table name too long */
788 		len = strlen(imsg->data);
789 		if (len >= LINE_MAX)
790 			goto invalid;
791 
792 		m_forward(p_lka, imsg);
793 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
794 		return;
795 
796 	case IMSG_CTL_DISCOVER_EVPID:
797 		if (c->euid)
798 			goto badcred;
799 
800 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof evpid)
801 			goto invalid;
802 
803 		memmove(&evpid, imsg->data, sizeof evpid);
804 		m_create(p_queue, imsg->hdr.type, c->id, 0, -1);
805 		m_add_evpid(p_queue, evpid);
806 		m_close(p_queue);
807 		return;
808 
809 	case IMSG_CTL_DISCOVER_MSGID:
810 	case IMSG_CTL_UNCORRUPT_MSGID:
811 		if (c->euid)
812 			goto badcred;
813 
814 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof msgid)
815 			goto invalid;
816 
817 		memmove(&msgid, imsg->data, sizeof msgid);
818 		m_create(p_queue, imsg->hdr.type, c->id, 0, -1);
819 		m_add_msgid(p_queue, msgid);
820 		m_close(p_queue);
821 		return;
822 
823 	default:
824 		log_debug("debug: control_dispatch_ext: "
825 		    "error handling %s imsg",
826 		    imsg_to_str(imsg->hdr.type));
827 		return;
828 	}
829 badcred:
830 invalid:
831 	m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
832 }
833 
834 static void
835 control_broadcast_verbose(int msg, int v)
836 {
837 	m_create(p_lka, msg, 0, 0, -1);
838 	m_add_int(p_lka, v);
839 	m_close(p_lka);
840 
841 	m_create(p_pony, msg, 0, 0, -1);
842 	m_add_int(p_pony, v);
843 	m_close(p_pony);
844 
845 	m_create(p_queue, msg, 0, 0, -1);
846 	m_add_int(p_queue, v);
847 	m_close(p_queue);
848 
849 	m_create(p_ca, msg, 0, 0, -1);
850 	m_add_int(p_ca, v);
851 	m_close(p_ca);
852 
853 	m_create(p_scheduler, msg, 0, 0, -1);
854 	m_add_int(p_scheduler, v);
855 	m_close(p_scheduler);
856 
857 	m_create(p_parent, msg, 0, 0, -1);
858 	m_add_int(p_parent, v);
859 	m_close(p_parent);
860 }
861