xref: /openbsd/usr.sbin/smtpd/control.c (revision 898184e3)
1 /*	$OpenBSD: control.c,v 1.83 2013/03/11 17:40:11 deraadt 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/param.h>
25 #include <sys/stat.h>
26 #include <sys/socket.h>
27 #include <sys/un.h>
28 
29 #include <err.h>
30 #include <errno.h>
31 #include <event.h>
32 #include <fcntl.h>
33 #include <imsg.h>
34 #include <pwd.h>
35 #include <signal.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 #include <unistd.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 
70 static struct stat_backend *stat_backend = NULL;
71 extern const char *backend_stat;
72 
73 static uint32_t			connid = 0;
74 static struct tree		ctl_conns;
75 static struct stat_digest	digest;
76 
77 #define	CONTROL_FD_RESERVE	5
78 
79 static void
80 control_imsg(struct mproc *p, struct imsg *imsg)
81 {
82 	struct ctl_conn		*c;
83 	struct stat_value	 val;
84 	struct msg		 m;
85 	const char		*key;
86 	const void		*data;
87 	size_t			 sz;
88 
89 	if (p->proc == PROC_SMTP) {
90 		switch (imsg->hdr.type) {
91 		case IMSG_SMTP_ENQUEUE_FD:
92 			c = tree_get(&ctl_conns, imsg->hdr.peerid);
93 			if (c == NULL)
94 				return;
95 			m_compose(&c->mproc, IMSG_CTL_OK, 0, 0, imsg->fd,
96 			    NULL, 0);
97 			return;
98 		}
99 	}
100 	if (p->proc == PROC_SCHEDULER) {
101 		switch (imsg->hdr.type) {
102 		case IMSG_CTL_LIST_MESSAGES:
103 			c = tree_get(&ctl_conns, imsg->hdr.peerid);
104 			if (c == NULL)
105 				return;
106 			m_forward(&c->mproc, imsg);
107 			return;
108 		}
109 	}
110 	if (p->proc == PROC_QUEUE) {
111 		switch (imsg->hdr.type) {
112 		case IMSG_CTL_LIST_ENVELOPES:
113 			c = tree_get(&ctl_conns, imsg->hdr.peerid);
114 			if (c == NULL)
115 				return;
116 			m_forward(&c->mproc, imsg);
117 			return;
118 		}
119 	}
120 
121 	switch (imsg->hdr.type) {
122 	case IMSG_STAT_INCREMENT:
123 		m_msg(&m, imsg);
124 		m_get_string(&m, &key);
125 		m_get_data(&m, &data, &sz);
126 		m_end(&m);
127 		memmove(&val, data, sz);
128 		if (stat_backend)
129 			stat_backend->increment(key, val.u.counter);
130 		control_digest_update(key, val.u.counter, 1);
131 		return;
132 	case IMSG_STAT_DECREMENT:
133 		m_msg(&m, imsg);
134 		m_get_string(&m, &key);
135 		m_get_data(&m, &data, &sz);
136 		m_end(&m);
137 		memmove(&val, data, sz);
138 		if (stat_backend)
139 			stat_backend->decrement(key, val.u.counter);
140 		control_digest_update(key, val.u.counter, 0);
141 		return;
142 	case IMSG_STAT_SET:
143 		m_msg(&m, imsg);
144 		m_get_string(&m, &key);
145 		m_get_data(&m, &data, &sz);
146 		m_end(&m);
147 		memmove(&val, data, sz);
148 		if (stat_backend)
149 			stat_backend->set(key, &val);
150 		return;
151 	}
152 
153 	errx(1, "control_imsg: unexpected %s imsg",
154 	    imsg_to_str(imsg->hdr.type));
155 }
156 
157 static void
158 control_sig_handler(int sig, short event, void *p)
159 {
160 	switch (sig) {
161 	case SIGINT:
162 	case SIGTERM:
163 		control_shutdown();
164 		break;
165 	default:
166 		fatalx("control_sig_handler: unexpected signal");
167 	}
168 }
169 
170 pid_t
171 control(void)
172 {
173 	struct sockaddr_un	 sun;
174 	int			 fd;
175 	mode_t			 old_umask;
176 	pid_t			 pid;
177 	struct passwd		*pw;
178 	struct event		 ev_sigint;
179 	struct event		 ev_sigterm;
180 
181 	switch (pid = fork()) {
182 	case -1:
183 		fatal("control: cannot fork");
184 	case 0:
185 		env->sc_pid = getpid();
186 		break;
187 	default:
188 		return (pid);
189 	}
190 
191 	purge_config(PURGE_EVERYTHING);
192 
193 	pw = env->sc_pw;
194 
195 	if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
196 		fatal("control: socket");
197 
198 	bzero(&sun, sizeof(sun));
199 	sun.sun_family = AF_UNIX;
200 	if (strlcpy(sun.sun_path, SMTPD_SOCKET,
201 	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path))
202 		fatal("control: socket name too long");
203 
204 	if (connect(fd, (struct sockaddr *)&sun, sizeof(sun)) == 0)
205 		fatalx("control socket already listening");
206 
207 	if (unlink(SMTPD_SOCKET) == -1)
208 		if (errno != ENOENT)
209 			fatal("control: cannot unlink socket");
210 
211 	old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
212 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
213 		(void)umask(old_umask);
214 		fatal("control: bind");
215 	}
216 	(void)umask(old_umask);
217 
218 	if (chmod(SMTPD_SOCKET,
219 		S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) {
220 		(void)unlink(SMTPD_SOCKET);
221 		fatal("control: chmod");
222 	}
223 
224 	session_socket_blockmode(fd, BM_NONBLOCK);
225 	control_state.fd = fd;
226 
227 	stat_backend = env->sc_stat;
228 	stat_backend->init();
229 
230 	if (chroot(pw->pw_dir) == -1)
231 		fatal("control: chroot");
232 	if (chdir("/") == -1)
233 		fatal("control: chdir(\"/\")");
234 
235 	config_process(PROC_CONTROL);
236 
237 	if (setgroups(1, &pw->pw_gid) ||
238 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
239 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
240 		fatal("control: cannot drop privileges");
241 
242 	imsg_callback = control_imsg;
243 	event_init();
244 
245 	signal_set(&ev_sigint, SIGINT, control_sig_handler, NULL);
246 	signal_set(&ev_sigterm, SIGTERM, control_sig_handler, NULL);
247 	signal_add(&ev_sigint, NULL);
248 	signal_add(&ev_sigterm, NULL);
249 	signal(SIGPIPE, SIG_IGN);
250 	signal(SIGHUP, SIG_IGN);
251 
252 	tree_init(&ctl_conns);
253 
254 	bzero(&digest, sizeof digest);
255 	digest.startup = time(NULL);
256 
257 	config_peer(PROC_SCHEDULER);
258 	config_peer(PROC_QUEUE);
259 	config_peer(PROC_SMTP);
260 	config_peer(PROC_MFA);
261 	config_peer(PROC_PARENT);
262 	config_peer(PROC_LKA);
263 	config_peer(PROC_MDA);
264 	config_peer(PROC_MTA);
265 	config_done();
266 
267 	control_listen();
268 
269 	if (event_dispatch() < 0)
270 		fatal("event_dispatch");
271 	control_shutdown();
272 
273 	return (0);
274 }
275 
276 static void
277 control_shutdown(void)
278 {
279 	log_info("info: control process exiting");
280 	unlink(SMTPD_SOCKET);
281 	_exit(0);
282 }
283 
284 static void
285 control_listen(void)
286 {
287 	if (listen(control_state.fd, CONTROL_BACKLOG) == -1)
288 		fatal("control_listen");
289 
290 	event_set(&control_state.ev, control_state.fd, EV_READ|EV_PERSIST,
291 	    control_accept, NULL);
292 	event_add(&control_state.ev, NULL);
293 }
294 
295 /* ARGSUSED */
296 static void
297 control_accept(int listenfd, short event, void *arg)
298 {
299 	int			 connfd;
300 	socklen_t		 len;
301 	struct sockaddr_un	 sun;
302 	struct ctl_conn		*c;
303 
304 	if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE)
305 		goto pause;
306 
307 	len = sizeof(sun);
308 	if ((connfd = accept(listenfd, (struct sockaddr *)&sun, &len)) == -1) {
309 		if (errno == ENFILE || errno == EMFILE)
310 			goto pause;
311 		if (errno == EINTR || errno == EWOULDBLOCK ||
312 		    errno == ECONNABORTED)
313 			return;
314 		fatal("control_accept: accept");
315 	}
316 
317 	session_socket_blockmode(connfd, BM_NONBLOCK);
318 
319 	c = xcalloc(1, sizeof(*c), "control_accept");
320 	if (getpeereid(connfd, &c->euid, &c->egid) == -1)
321 		fatal("getpeereid");
322 	c->id = ++connid;
323 	c->mproc.handler = control_dispatch_ext;
324 	c->mproc.data = c;
325 	mproc_init(&c->mproc, connfd);
326 	mproc_enable(&c->mproc);
327 	tree_xset(&ctl_conns, c->id, c);
328 
329 	stat_backend->increment("control.session", 1);
330 	return;
331 
332 pause:
333 	log_warnx("warn: ctl client limit hit, disabling new connections");
334 	event_del(&control_state.ev);
335 }
336 
337 static void
338 control_close(struct ctl_conn *c)
339 {
340 	tree_xpop(&ctl_conns, c->id);
341 	mproc_clear(&c->mproc);
342 	free(c);
343 
344 	stat_backend->decrement("control.session", 1);
345 
346 	if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE)
347 		return;
348 
349 	if (!event_pending(&control_state.ev, EV_READ, NULL)) {
350 		log_warnx("warn: re-enabling ctl connections");
351 		event_add(&control_state.ev, NULL);
352 	}
353 }
354 
355 static void
356 control_digest_update(const char *key, size_t value, int incr)
357 {
358 	size_t	*p;
359 
360 	p = NULL;
361 
362 	if (!strcmp(key, "smtp.session")) {
363 		if (incr)
364 			p = &digest.clt_connect;
365 		else
366 			digest.clt_disconnect += value;
367 	}
368 	else if (!strcmp(key, "scheduler.envelope")) {
369 		if (incr)
370 			p = &digest.evp_enqueued;
371 		else
372 			digest.evp_dequeued += value;
373 	}
374 	else if  (!strcmp(key, "scheduler.envelope.expired"))
375 		p = &digest.evp_expired;
376 	else if  (!strcmp(key, "scheduler.envelope.removed"))
377 		p = &digest.evp_removed;
378 	else if  (!strcmp(key, "scheduler.delivery.ok"))
379 		p = &digest.dlv_ok;
380 	else if  (!strcmp(key, "scheduler.delivery.permfail"))
381 		p = &digest.dlv_permfail;
382 	else if  (!strcmp(key, "scheduler.delivery.tempfail"))
383 		p = &digest.dlv_tempfail;
384 	else if  (!strcmp(key, "scheduler.delivery.loop"))
385 		p = &digest.dlv_loop;
386 
387 	else if  (!strcmp(key, "queue.bounce"))
388 		p = &digest.evp_bounce;
389 
390 	if (p) {
391 		if (incr)
392 			*p = *p + value;
393 		else
394 			*p = *p - value;
395 	}
396 }
397 
398 /* ARGSUSED */
399 static void
400 control_dispatch_ext(struct mproc *p, struct imsg *imsg)
401 {
402 	struct ctl_conn		*c;
403 	int			 v;
404 	struct stat_kv		*kvp;
405 	char			*key;
406 	struct stat_value	 val;
407 	size_t			 len;
408 
409 	c = p->data;
410 
411 	if (imsg == NULL) {
412 		control_close(c);
413 		return;
414 	}
415 
416 	switch (imsg->hdr.type) {
417 	case IMSG_SMTP_ENQUEUE_FD:
418 		if (env->sc_flags & (SMTPD_SMTP_PAUSED |
419 		    SMTPD_CONFIGURING | SMTPD_EXITING)) {
420 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
421 			return;
422 		}
423 		m_compose(p_smtp, IMSG_SMTP_ENQUEUE_FD, c->id, 0, -1,
424 		    &c->euid, sizeof(c->euid));
425 		return;
426 
427 	case IMSG_STATS:
428 		if (c->euid)
429 			goto badcred;
430 		m_compose(p, IMSG_STATS, 0, 0, -1, NULL, 0);
431 		return;
432 
433 	case IMSG_DIGEST:
434 		if (c->euid)
435 			goto badcred;
436 		digest.timestamp = time(NULL);
437 		m_compose(p, IMSG_DIGEST, 0, 0, -1, &digest, sizeof digest);
438 		return;
439 
440 	case IMSG_STATS_GET:
441 		if (c->euid)
442 			goto badcred;
443 		kvp = imsg->data;
444 		if (! stat_backend->iter(&kvp->iter, &key, &val))
445 			kvp->iter = NULL;
446 		else {
447 			strlcpy(kvp->key, key, sizeof kvp->key);
448 			kvp->val = val;
449 		}
450 		m_compose(p, IMSG_STATS_GET, 0, 0, -1, kvp, sizeof *kvp);
451 		return;
452 
453 	case IMSG_CTL_SHUTDOWN:
454 		/* NEEDS_FIX */
455 		log_debug("debug: received shutdown request");
456 
457 		if (c->euid)
458 			goto badcred;
459 
460 		if (env->sc_flags & SMTPD_EXITING) {
461 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
462 			return;
463 		}
464 		env->sc_flags |= SMTPD_EXITING;
465 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
466 		m_compose(p_parent, IMSG_CTL_SHUTDOWN, 0, 0, -1, NULL, 0);
467 		return;
468 
469 	case IMSG_CTL_VERBOSE:
470 		if (c->euid)
471 			goto badcred;
472 
473 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
474 			goto badcred;
475 
476 		memcpy(&v, imsg->data, sizeof(v));
477 		verbose = v;
478 		log_verbose(verbose);
479 
480 		m_create(p_parent, IMSG_CTL_VERBOSE, 0, 0, -1, 9);
481 		m_add_int(p_parent, verbose);
482 		m_close(p_parent);
483 
484 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
485 		return;
486 
487 	case IMSG_CTL_TRACE:
488 		if (c->euid)
489 			goto badcred;
490 
491 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
492 			goto badcred;
493 
494 		memcpy(&v, imsg->data, sizeof(v));
495 		verbose |= v;
496 		log_verbose(verbose);
497 
498 		m_create(p_parent, IMSG_CTL_TRACE, 0, 0, -1, 9);
499 		m_add_int(p_parent, v);
500 		m_close(p_parent);
501 
502 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
503 		return;
504 
505 	case IMSG_CTL_UNTRACE:
506 		if (c->euid)
507 			goto badcred;
508 
509 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
510 			goto badcred;
511 
512 		memcpy(&v, imsg->data, sizeof(v));
513 		verbose &= ~v;
514 		log_verbose(verbose);
515 
516 		m_create(p_parent, IMSG_CTL_UNTRACE, 0, 0, -1, 9);
517 		m_add_int(p_parent, v);
518 		m_close(p_parent);
519 
520 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
521 		return;
522 
523 	case IMSG_CTL_PROFILE:
524 		if (c->euid)
525 			goto badcred;
526 
527 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
528 			goto badcred;
529 
530 		memcpy(&v, imsg->data, sizeof(v));
531 		profiling |= v;
532 
533 		m_create(p_parent, IMSG_CTL_PROFILE, 0, 0, -1, 9);
534 		m_add_int(p_parent, v);
535 		m_close(p_parent);
536 
537 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
538 		return;
539 
540 	case IMSG_CTL_UNPROFILE:
541 		if (c->euid)
542 			goto badcred;
543 
544 		if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(verbose))
545 			goto badcred;
546 
547 		memcpy(&v, imsg->data, sizeof(v));
548 		profiling &= ~v;
549 
550 		m_create(p_parent, IMSG_CTL_UNPROFILE, 0, 0, -1, 9);
551 		m_add_int(p_parent, v);
552 		m_close(p_parent);
553 
554 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
555 		return;
556 
557 	case IMSG_CTL_PAUSE_MDA:
558 		if (c->euid)
559 			goto badcred;
560 
561 		if (env->sc_flags & SMTPD_MDA_PAUSED) {
562 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
563 			return;
564 		}
565 		log_info("info: mda paused");
566 		env->sc_flags |= SMTPD_MDA_PAUSED;
567 		m_compose(p_queue, IMSG_CTL_PAUSE_MDA, 0, 0, -1, NULL, 0);
568 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
569 		return;
570 
571 	case IMSG_CTL_PAUSE_MTA:
572 		if (c->euid)
573 			goto badcred;
574 
575 		if (env->sc_flags & SMTPD_MTA_PAUSED) {
576 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
577 			return;
578 		}
579 		log_info("info: mta paused");
580 		env->sc_flags |= SMTPD_MTA_PAUSED;
581 		m_compose(p_queue, IMSG_CTL_PAUSE_MTA, 0, 0, -1, NULL, 0);
582 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
583 		return;
584 
585 	case IMSG_CTL_PAUSE_SMTP:
586 		if (c->euid)
587 			goto badcred;
588 
589 		if (env->sc_flags & SMTPD_SMTP_PAUSED) {
590 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
591 			return;
592 		}
593 		log_info("info: smtp paused");
594 		env->sc_flags |= SMTPD_SMTP_PAUSED;
595 		m_compose(p_smtp, IMSG_CTL_PAUSE_SMTP, 0, 0, -1, NULL, 0);
596 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
597 		return;
598 
599 	case IMSG_CTL_RESUME_MDA:
600 		if (c->euid)
601 			goto badcred;
602 
603 		if (! (env->sc_flags & SMTPD_MDA_PAUSED)) {
604 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
605 			return;
606 		}
607 		log_info("info: mda resumed");
608 		env->sc_flags &= ~SMTPD_MDA_PAUSED;
609 		m_compose(p_queue, IMSG_CTL_RESUME_MDA, 0, 0, -1, NULL, 0);
610 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
611 		return;
612 
613 	case IMSG_CTL_RESUME_MTA:
614 		if (c->euid)
615 			goto badcred;
616 
617 		if (!(env->sc_flags & SMTPD_MTA_PAUSED)) {
618 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
619 			return;
620 		}
621 		log_info("info: mta resumed");
622 		env->sc_flags &= ~SMTPD_MTA_PAUSED;
623 		m_compose(p_queue, IMSG_CTL_RESUME_MTA, 0, 0, -1, NULL, 0);
624 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
625 		return;
626 
627 	case IMSG_CTL_RESUME_SMTP:
628 		if (c->euid)
629 			goto badcred;
630 
631 		if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) {
632 			m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
633 			return;
634 		}
635 		log_info("info: smtp resumed");
636 		env->sc_flags &= ~SMTPD_SMTP_PAUSED;
637 		m_forward(p_smtp, imsg);
638 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
639 		return;
640 
641 	case IMSG_CTL_LIST_MESSAGES:
642 		if (c->euid)
643 			goto badcred;
644 		m_compose(p_scheduler, IMSG_CTL_LIST_MESSAGES, c->id, 0, -1,
645 		    imsg->data, imsg->hdr.len - sizeof(imsg->hdr));
646 		return;
647 
648 	case IMSG_CTL_LIST_ENVELOPES:
649 		if (c->euid)
650 			goto badcred;
651 		m_compose(p_scheduler, IMSG_CTL_LIST_ENVELOPES, c->id, 0, -1,
652 		    imsg->data, imsg->hdr.len - sizeof(imsg->hdr));
653 		return;
654 
655 	case IMSG_CTL_SCHEDULE:
656 		if (c->euid)
657 			goto badcred;
658 
659 		m_forward(p_scheduler, imsg);
660 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
661 		return;
662 
663 	case IMSG_CTL_REMOVE:
664 		if (c->euid)
665 			goto badcred;
666 
667 		m_forward(p_scheduler, imsg);
668 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
669 		return;
670 
671 	case IMSG_LKA_UPDATE_TABLE:
672 		if (c->euid)
673 			goto badcred;
674 
675 		/* table name too long */
676 		len = strlen(imsg->data);
677 		if (len >= MAX_LINE_SIZE)
678 			goto invalid;
679 
680 		m_forward(p_lka, imsg);
681 		m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0);
682 		return;
683 
684 	default:
685 		log_debug("debug: control_dispatch_ext: "
686 		    "error handling %s imsg",
687 		    imsg_to_str(imsg->hdr.type));
688 		return;
689 	}
690 badcred:
691 invalid:
692 	m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0);
693 }
694