xref: /openbsd/usr.sbin/smtpd/control.c (revision e459d0ce)
1 /*	$OpenBSD: control.c,v 1.109 2015/12/28 22:08:30 jung 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 		memmove(&val, data, sz);
154 		if (stat_backend)
155 			stat_backend->increment(key, val.u.counter);
156 		control_digest_update(key, val.u.counter, 1);
157 		return;
158 	case IMSG_STAT_DECREMENT:
159 		m_msg(&m, imsg);
160 		m_get_string(&m, &key);
161 		m_get_data(&m, &data, &sz);
162 		m_end(&m);
163 		memmove(&val, data, sz);
164 		if (stat_backend)
165 			stat_backend->decrement(key, val.u.counter);
166 		control_digest_update(key, val.u.counter, 0);
167 		return;
168 	case IMSG_STAT_SET:
169 		m_msg(&m, imsg);
170 		m_get_string(&m, &key);
171 		m_get_data(&m, &data, &sz);
172 		m_end(&m);
173 		memmove(&val, data, sz);
174 		if (stat_backend)
175 			stat_backend->set(key, &val);
176 		return;
177 	}
178 
179 	errx(1, "control_imsg: unexpected %s imsg",
180 	    imsg_to_str(imsg->hdr.type));
181 }
182 
183 static void
184 control_sig_handler(int sig, short event, void *p)
185 {
186 	switch (sig) {
187 	case SIGINT:
188 	case SIGTERM:
189 		control_shutdown();
190 		break;
191 	default:
192 		fatalx("control_sig_handler: unexpected signal");
193 	}
194 }
195 
196 int
197 control_create_socket(void)
198 {
199 	struct sockaddr_un	sun;
200 	int			fd;
201 	mode_t			old_umask;
202 
203 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
204 		fatal("control: socket");
205 
206 	memset(&sun, 0, sizeof(sun));
207 	sun.sun_family = AF_UNIX;
208 	if (strlcpy(sun.sun_path, SMTPD_SOCKET,
209 	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path))
210 		fatal("control: socket name too long");
211 
212 	if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0)
213 		fatalx("control socket already listening");
214 
215 	if (unlink(SMTPD_SOCKET) == -1)
216 		if (errno != ENOENT)
217 			fatal("control: cannot unlink socket");
218 
219 	old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
220 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
221 		(void)umask(old_umask);
222 		fatal("control: bind");
223 	}
224 	(void)umask(old_umask);
225 
226 	if (chmod(SMTPD_SOCKET,
227 		S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) {
228 		(void)unlink(SMTPD_SOCKET);
229 		fatal("control: chmod");
230 	}
231 
232 	session_socket_blockmode(fd, BM_NONBLOCK);
233 	control_state.fd = fd;
234 
235 	return fd;
236 }
237 
238 pid_t
239 control(void)
240 {
241 	pid_t			 pid;
242 	struct passwd		*pw;
243 	struct event		 ev_sigint;
244 	struct event		 ev_sigterm;
245 
246 	switch (pid = fork()) {
247 	case -1:
248 		fatal("control: cannot fork");
249 	case 0:
250 		post_fork(PROC_CONTROL);
251 		break;
252 	default:
253 		return (pid);
254 	}
255 
256 	purge_config(PURGE_EVERYTHING);
257 
258 	if ((pw = getpwnam(SMTPD_USER)) == NULL)
259 		fatalx("unknown user " SMTPD_USER);
260 
261 	stat_backend = env->sc_stat;
262 	stat_backend->init();
263 
264 	if (chroot(PATH_CHROOT) == -1)
265 		fatal("control: chroot");
266 	if (chdir("/") == -1)
267 		fatal("control: chdir(\"/\")");
268 
269 	config_process(PROC_CONTROL);
270 
271 	if (setgroups(1, &pw->pw_gid) ||
272 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
273 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
274 		fatal("control: cannot drop privileges");
275 
276 	imsg_callback = control_imsg;
277 	event_init();
278 
279 	signal_set(&ev_sigint, SIGINT, control_sig_handler, NULL);
280 	signal_set(&ev_sigterm, SIGTERM, control_sig_handler, NULL);
281 	signal_add(&ev_sigint, NULL);
282 	signal_add(&ev_sigterm, NULL);
283 	signal(SIGPIPE, SIG_IGN);
284 	signal(SIGHUP, SIG_IGN);
285 
286 	tree_init(&ctl_conns);
287 	tree_init(&ctl_count);
288 
289 	memset(&digest, 0, sizeof digest);
290 	digest.startup = time(NULL);
291 
292 	config_peer(PROC_SCHEDULER);
293 	config_peer(PROC_QUEUE);
294 	config_peer(PROC_PARENT);
295 	config_peer(PROC_LKA);
296 	config_peer(PROC_PONY);
297 	config_peer(PROC_CA);
298 	config_done();
299 
300 	control_listen();
301 
302 	if (pledge("stdio unix recvfd sendfd", NULL) == -1)
303 		err(1, "pledge");
304 
305 	if (event_dispatch() < 0)
306 		fatal("event_dispatch");
307 	control_shutdown();
308 
309 	return (0);
310 }
311 
312 static void
313 control_shutdown(void)
314 {
315 	log_info("info: control process exiting");
316 	_exit(0);
317 }
318 
319 static void
320 control_listen(void)
321 {
322 	if (listen(control_state.fd, CONTROL_BACKLOG) == -1)
323 		fatal("control_listen");
324 
325 	event_set(&control_state.ev, control_state.fd, EV_READ|EV_PERSIST,
326 	    control_accept, NULL);
327 	event_add(&control_state.ev, NULL);
328 }
329 
330 /* ARGSUSED */
331 static void
332 control_accept(int listenfd, short event, void *arg)
333 {
334 	int			 connfd;
335 	socklen_t		 len;
336 	struct sockaddr_un	 sun;
337 	struct ctl_conn		*c;
338 	size_t			*count;
339 	uid_t			 euid;
340 	gid_t			 egid;
341 
342 	if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE)
343 		goto pause;
344 
345 	len = sizeof(sun);
346 	if ((connfd = accept(listenfd, (struct sockaddr *)&sun, &len)) == -1) {
347 		if (errno == ENFILE || errno == EMFILE)
348 			goto pause;
349 		if (errno == EINTR || errno == EWOULDBLOCK ||
350 		    errno == ECONNABORTED)
351 			return;
352 		fatal("control_accept: accept");
353 	}
354 
355 	session_socket_blockmode(connfd, BM_NONBLOCK);
356 
357 	if (getpeereid(connfd, &euid, &egid) == -1)
358 		fatal("getpeereid");
359 
360 	count = tree_get(&ctl_count, euid);
361 	if (count == NULL) {
362 		count = xcalloc(1, sizeof *count, "control_accept");
363 		tree_xset(&ctl_count, euid, count);
364 	}
365 
366 	if (*count == CONTROL_MAXCONN_PER_CLIENT) {
367 		close(connfd);
368 		log_warnx("warn: too many connections to control socket "
369 		    "from user with uid %lu", (unsigned long int)euid);
370 		return;
371 	}
372 	(*count)++;
373 
374 	do {
375 		++connid;
376 	} while (tree_get(&ctl_conns, connid));
377 
378 	c = xcalloc(1, sizeof(*c), "control_accept");
379 	c->euid = euid;
380 	c->egid = egid;
381 	c->id = connid;
382 	c->mproc.proc = PROC_CLIENT;
383 	c->mproc.handler = control_dispatch_ext;
384 	c->mproc.data = c;
385 	mproc_init(&c->mproc, connfd);
386 	mproc_enable(&c->mproc);
387 	tree_xset(&ctl_conns, c->id, c);
388 
389 	stat_backend->increment("control.session", 1);
390 	return;
391 
392 pause:
393 	log_warnx("warn: ctl client limit hit, disabling new connections");
394 	event_del(&control_state.ev);
395 }
396 
397 static void
398 control_close(struct ctl_conn *c)
399 {
400 	size_t	*count;
401 
402 	count = tree_xget(&ctl_count, c->euid);
403 	(*count)--;
404 	if (*count == 0) {
405 		tree_xpop(&ctl_count, c->euid);
406 		free(count);
407 	}
408 	tree_xpop(&ctl_conns, c->id);
409 	mproc_clear(&c->mproc);
410 	free(c);
411 
412 	stat_backend->decrement("control.session", 1);
413 
414 	if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE)
415 		return;
416 
417 	if (!event_pending(&control_state.ev, EV_READ, NULL)) {
418 		log_warnx("warn: re-enabling ctl connections");
419 		event_add(&control_state.ev, NULL);
420 	}
421 }
422 
423 static void
424 control_digest_update(const char *key, size_t value, int incr)
425 {
426 	size_t	*p;
427 
428 	p = NULL;
429 
430 	if (!strcmp(key, "smtp.session")) {
431 		if (incr)
432 			p = &digest.clt_connect;
433 		else
434 			digest.clt_disconnect += value;
435 	}
436 	else if (!strcmp(key, "scheduler.envelope")) {
437 		if (incr)
438 			p = &digest.evp_enqueued;
439 		else
440 			digest.evp_dequeued += value;
441 	}
442 	else if  (!strcmp(key, "scheduler.envelope.expired"))
443 		p = &digest.evp_expired;
444 	else if  (!strcmp(key, "scheduler.envelope.removed"))
445 		p = &digest.evp_removed;
446 	else if  (!strcmp(key, "scheduler.delivery.ok"))
447 		p = &digest.dlv_ok;
448 	else if  (!strcmp(key, "scheduler.delivery.permfail"))
449 		p = &digest.dlv_permfail;
450 	else if  (!strcmp(key, "scheduler.delivery.tempfail"))
451 		p = &digest.dlv_tempfail;
452 	else if  (!strcmp(key, "scheduler.delivery.loop"))
453 		p = &digest.dlv_loop;
454 
455 	else if  (!strcmp(key, "queue.bounce"))
456 		p = &digest.evp_bounce;
457 
458 	if (p) {
459 		if (incr)
460 			*p = *p + value;
461 		else
462 			*p = *p - value;
463 	}
464 }
465 
466 /* ARGSUSED */
467 static void
468 control_dispatch_ext(struct mproc *p, struct imsg *imsg)
469 {
470 	struct sockaddr_storage	 ss;
471 	struct ctl_conn		*c;
472 	int			 v;
473 	struct stat_kv		*kvp;
474 	char			*key;
475 	struct stat_value	 val;
476 	size_t			 len;
477 	uint64_t		 evpid;
478 	uint32_t		 msgid;
479 
480 	c = p->data;
481 
482 	if (imsg == NULL) {
483 		control_close(c);
484 		return;
485 	}
486 
487 	if (imsg->hdr.peerid != IMSG_VERSION) {
488 		m_compose(p, IMSG_CTL_FAIL, IMSG_VERSION, 0, -1, NULL, 0);
489 		return;
490 	}
491 
492 	switch (imsg->hdr.type) {
493 	case IMSG_CTL_SMTP_SESSION:
494 		if (env->sc_flags & (SMTPD_SMTP_PAUSED | SMTPD_EXITING)) {
495 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
496 			return;
497 		}
498 		m_compose(p_pony, IMSG_CTL_SMTP_SESSION, c->id, 0, -1,
499 		    &c->euid, sizeof(c->euid));
500 		return;
501 
502 	case IMSG_CTL_GET_DIGEST:
503 		if (c->euid)
504 			goto badcred;
505 		digest.timestamp = time(NULL);
506 		m_compose(p, IMSG_CTL_GET_DIGEST, 0, 0, -1, &digest, sizeof digest);
507 		return;
508 
509 	case IMSG_CTL_GET_STATS:
510 		if (c->euid)
511 			goto badcred;
512 		kvp = imsg->data;
513 		if (!stat_backend->iter(&kvp->iter, &key, &val))
514 			kvp->iter = NULL;
515 		else {
516 			(void)strlcpy(kvp->key, key, sizeof kvp->key);
517 			kvp->val = val;
518 		}
519 		m_compose(p, IMSG_CTL_GET_STATS, 0, 0, -1, kvp, sizeof *kvp);
520 		return;
521 
522 	case IMSG_CTL_SHUTDOWN:
523 		/* NEEDS_FIX */
524 		log_debug("debug: received shutdown request");
525 
526 		if (c->euid)
527 			goto badcred;
528 
529 		if (env->sc_flags & SMTPD_EXITING) {
530 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
531 			return;
532 		}
533 		env->sc_flags |= SMTPD_EXITING;
534 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
535 		m_compose(p_parent, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0);
536 		return;
537 
538 	case IMSG_CTL_VERBOSE:
539 		if (c->euid)
540 			goto badcred;
541 
542 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
543 			goto badcred;
544 
545 		memcpy(&v, imsg->data, sizeof(v));
546 		verbose = v;
547 		log_verbose(verbose);
548 
549 		control_broadcast_verbose(IMSG_CTL_VERBOSE, verbose);
550 
551 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
552 		return;
553 
554 	case IMSG_CTL_TRACE_ENABLE:
555 		if (c->euid)
556 			goto badcred;
557 
558 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
559 			goto badcred;
560 
561 		memcpy(&v, imsg->data, sizeof(v));
562 		verbose |= v;
563 		log_verbose(verbose);
564 
565 		control_broadcast_verbose(IMSG_CTL_VERBOSE, verbose);
566 
567 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
568 		return;
569 
570 	case IMSG_CTL_TRACE_DISABLE:
571 		if (c->euid)
572 			goto badcred;
573 
574 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
575 			goto badcred;
576 
577 		memcpy(&v, imsg->data, sizeof(v));
578 		verbose &= ~v;
579 		log_verbose(verbose);
580 
581 		control_broadcast_verbose(IMSG_CTL_VERBOSE, verbose);
582 
583 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
584 		return;
585 
586 	case IMSG_CTL_PROFILE_ENABLE:
587 		if (c->euid)
588 			goto badcred;
589 
590 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
591 			goto badcred;
592 
593 		memcpy(&v, imsg->data, sizeof(v));
594 		profiling |= v;
595 
596 		control_broadcast_verbose(IMSG_CTL_PROFILE, profiling);
597 
598 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
599 		return;
600 
601 	case IMSG_CTL_PROFILE_DISABLE:
602 		if (c->euid)
603 			goto badcred;
604 
605 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
606 			goto badcred;
607 
608 		memcpy(&v, imsg->data, sizeof(v));
609 		profiling &= ~v;
610 
611 		control_broadcast_verbose(IMSG_CTL_PROFILE, profiling);
612 
613 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
614 		return;
615 
616 	case IMSG_CTL_PAUSE_EVP:
617 		if (c->euid)
618 			goto badcred;
619 
620 		imsg->hdr.peerid = c->id;
621 		m_forward(p_scheduler, imsg);
622 		return;
623 
624 	case IMSG_CTL_PAUSE_MDA:
625 		if (c->euid)
626 			goto badcred;
627 
628 		if (env->sc_flags & SMTPD_MDA_PAUSED) {
629 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
630 			return;
631 		}
632 		log_info("info: mda paused");
633 		env->sc_flags |= SMTPD_MDA_PAUSED;
634 		m_compose(p_queue, IMSG_CTL_PAUSE_MDA, 0, 0, -1, NULL, 0);
635 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
636 		return;
637 
638 	case IMSG_CTL_PAUSE_MTA:
639 		if (c->euid)
640 			goto badcred;
641 
642 		if (env->sc_flags & SMTPD_MTA_PAUSED) {
643 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
644 			return;
645 		}
646 		log_info("info: mta paused");
647 		env->sc_flags |= SMTPD_MTA_PAUSED;
648 		m_compose(p_queue, IMSG_CTL_PAUSE_MTA, 0, 0, -1, NULL, 0);
649 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
650 		return;
651 
652 	case IMSG_CTL_PAUSE_SMTP:
653 		if (c->euid)
654 			goto badcred;
655 
656 		if (env->sc_flags & SMTPD_SMTP_PAUSED) {
657 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
658 			return;
659 		}
660 		log_info("info: smtp paused");
661 		env->sc_flags |= SMTPD_SMTP_PAUSED;
662 		m_compose(p_pony, IMSG_CTL_PAUSE_SMTP, 0, 0, -1, NULL, 0);
663 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
664 		return;
665 
666 	case IMSG_CTL_RESUME_EVP:
667 		if (c->euid)
668 			goto badcred;
669 
670 		imsg->hdr.peerid = c->id;
671 		m_forward(p_scheduler, imsg);
672 		return;
673 
674 	case IMSG_CTL_RESUME_MDA:
675 		if (c->euid)
676 			goto badcred;
677 
678 		if (!(env->sc_flags & SMTPD_MDA_PAUSED)) {
679 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
680 			return;
681 		}
682 		log_info("info: mda resumed");
683 		env->sc_flags &= ~SMTPD_MDA_PAUSED;
684 		m_compose(p_queue, IMSG_CTL_RESUME_MDA, 0, 0, -1, NULL, 0);
685 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
686 		return;
687 
688 	case IMSG_CTL_RESUME_MTA:
689 		if (c->euid)
690 			goto badcred;
691 
692 		if (!(env->sc_flags & SMTPD_MTA_PAUSED)) {
693 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
694 			return;
695 		}
696 		log_info("info: mta resumed");
697 		env->sc_flags &= ~SMTPD_MTA_PAUSED;
698 		m_compose(p_queue, IMSG_CTL_RESUME_MTA, 0, 0, -1, NULL, 0);
699 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
700 		return;
701 
702 	case IMSG_CTL_RESUME_SMTP:
703 		if (c->euid)
704 			goto badcred;
705 
706 		if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) {
707 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
708 			return;
709 		}
710 		log_info("info: smtp resumed");
711 		env->sc_flags &= ~SMTPD_SMTP_PAUSED;
712 		m_forward(p_pony, imsg);
713 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
714 		return;
715 
716 	case IMSG_CTL_RESUME_ROUTE:
717 		if (c->euid)
718 			goto badcred;
719 
720 		m_forward(p_pony, imsg);
721 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
722 		return;
723 
724 	case IMSG_CTL_LIST_MESSAGES:
725 		if (c->euid)
726 			goto badcred;
727 		m_compose(p_scheduler, IMSG_CTL_LIST_MESSAGES, c->id, 0, -1,
728 		    imsg->data, imsg->hdr.len - sizeof(imsg->hdr));
729 		return;
730 
731 	case IMSG_CTL_LIST_ENVELOPES:
732 		if (c->euid)
733 			goto badcred;
734 		m_compose(p_scheduler, IMSG_CTL_LIST_ENVELOPES, c->id, 0, -1,
735 		    imsg->data, imsg->hdr.len - sizeof(imsg->hdr));
736 		return;
737 
738 	case IMSG_CTL_MTA_SHOW_HOSTS:
739 	case IMSG_CTL_MTA_SHOW_RELAYS:
740 	case IMSG_CTL_MTA_SHOW_ROUTES:
741 	case IMSG_CTL_MTA_SHOW_HOSTSTATS:
742 	case IMSG_CTL_MTA_SHOW_BLOCK:
743 		if (c->euid)
744 			goto badcred;
745 
746 		imsg->hdr.peerid = c->id;
747 		m_forward(p_pony, imsg);
748 		return;
749 
750 	case IMSG_CTL_SHOW_STATUS:
751 		if (c->euid)
752 			goto badcred;
753 
754 		m_compose(p, IMSG_CTL_SHOW_STATUS, 0, 0, -1, &env->sc_flags,
755 		    sizeof(env->sc_flags));
756 		return;
757 
758 	case IMSG_CTL_MTA_BLOCK:
759 	case IMSG_CTL_MTA_UNBLOCK:
760 		if (c->euid)
761 			goto badcred;
762 
763 		if (imsg->hdr.len - IMSG_HEADER_SIZE <= sizeof(ss))
764 			goto invalid;
765 		memmove(&ss, imsg->data, sizeof(ss));
766 		m_create(p_pony, imsg->hdr.type, c->id, 0, -1);
767 		m_add_sockaddr(p_pony, (struct sockaddr *)&ss);
768 		m_add_string(p_pony, (char *)imsg->data + sizeof(ss));
769 		m_close(p_pony);
770 		return;
771 
772 	case IMSG_CTL_SCHEDULE:
773 		if (c->euid)
774 			goto badcred;
775 
776 		imsg->hdr.peerid = c->id;
777 		m_forward(p_scheduler, imsg);
778 		return;
779 
780 	case IMSG_CTL_REMOVE:
781 		if (c->euid)
782 			goto badcred;
783 
784 		imsg->hdr.peerid = c->id;
785 		m_forward(p_scheduler, imsg);
786 		return;
787 
788 	case IMSG_CTL_UPDATE_TABLE:
789 		if (c->euid)
790 			goto badcred;
791 
792 		/* table name too long */
793 		len = strlen(imsg->data);
794 		if (len >= LINE_MAX)
795 			goto invalid;
796 
797 		m_forward(p_lka, imsg);
798 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
799 		return;
800 
801 	case IMSG_CTL_DISCOVER_EVPID:
802 		if (c->euid)
803 			goto badcred;
804 
805 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof evpid)
806 			goto invalid;
807 
808 		memmove(&evpid, imsg->data, sizeof evpid);
809 		m_create(p_queue, imsg->hdr.type, c->id, 0, -1);
810 		m_add_evpid(p_queue, evpid);
811 		m_close(p_queue);
812 		return;
813 
814 	case IMSG_CTL_DISCOVER_MSGID:
815 	case IMSG_CTL_UNCORRUPT_MSGID:
816 		if (c->euid)
817 			goto badcred;
818 
819 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof msgid)
820 			goto invalid;
821 
822 		memmove(&msgid, imsg->data, sizeof msgid);
823 		m_create(p_queue, imsg->hdr.type, c->id, 0, -1);
824 		m_add_msgid(p_queue, msgid);
825 		m_close(p_queue);
826 		return;
827 
828 	default:
829 		log_debug("debug: control_dispatch_ext: "
830 		    "error handling %s imsg",
831 		    imsg_to_str(imsg->hdr.type));
832 		return;
833 	}
834 badcred:
835 invalid:
836 	m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
837 }
838 
839 static void
840 control_broadcast_verbose(int msg, int v)
841 {
842 	m_create(p_lka, msg, 0, 0, -1);
843 	m_add_int(p_lka, v);
844 	m_close(p_lka);
845 
846 	m_create(p_pony, msg, 0, 0, -1);
847 	m_add_int(p_pony, v);
848 	m_close(p_pony);
849 
850 	m_create(p_queue, msg, 0, 0, -1);
851 	m_add_int(p_queue, v);
852 	m_close(p_queue);
853 
854 	m_create(p_ca, msg, 0, 0, -1);
855 	m_add_int(p_ca, v);
856 	m_close(p_ca);
857 
858 	m_create(p_scheduler, msg, 0, 0, -1);
859 	m_add_int(p_scheduler, v);
860 	m_close(p_scheduler);
861 
862 	m_create(p_parent, msg, 0, 0, -1);
863 	m_add_int(p_parent, v);
864 	m_close(p_parent);
865 }
866