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