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
control_imsg(struct mproc * p,struct imsg * imsg)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
control_create_socket(void)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
control(void)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
control_shutdown(void)257 control_shutdown(void)
258 {
259 log_debug("debug: control agent exiting");
260 _exit(0);
261 }
262
263 static void
control_listen(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
control_accept(int listenfd,short event,void * arg)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
control_close(struct ctl_conn * c)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
control_digest_update(const char * key,size_t value,int incr)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
control_dispatch_ext(struct mproc * p,struct imsg * imsg)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
control_broadcast_verbose(int msg,int v)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