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