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