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