1*bf921b2aSclaudio /* $OpenBSD: control.c,v 1.132 2024/11/21 13:42:22 claudio Exp $ */ 21f3c2bcfSsobrado 33ef9cbf7Sgilles /* 465c4fdfbSgilles * Copyright (c) 2012 Gilles Chehade <gilles@poolp.org> 53ef9cbf7Sgilles * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 63ef9cbf7Sgilles * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 73ef9cbf7Sgilles * 83ef9cbf7Sgilles * Permission to use, copy, modify, and distribute this software for any 93ef9cbf7Sgilles * purpose with or without fee is hereby granted, provided that the above 103ef9cbf7Sgilles * copyright notice and this permission notice appear in all copies. 113ef9cbf7Sgilles * 123ef9cbf7Sgilles * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 133ef9cbf7Sgilles * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 143ef9cbf7Sgilles * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 153ef9cbf7Sgilles * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 163ef9cbf7Sgilles * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 173ef9cbf7Sgilles * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 183ef9cbf7Sgilles * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 193ef9cbf7Sgilles */ 203ef9cbf7Sgilles 213ef9cbf7Sgilles #include <sys/stat.h> 223ef9cbf7Sgilles #include <sys/un.h> 233ef9cbf7Sgilles 243ef9cbf7Sgilles #include <errno.h> 253ef9cbf7Sgilles #include <pwd.h> 26439d0b42Schl #include <signal.h> 273ef9cbf7Sgilles #include <stdlib.h> 283ef9cbf7Sgilles #include <string.h> 290dcffd0dSop #include <time.h> 303ef9cbf7Sgilles #include <unistd.h> 313ef9cbf7Sgilles 323ef9cbf7Sgilles #include "smtpd.h" 335eb8dddaSgilles #include "log.h" 343ef9cbf7Sgilles 353ef9cbf7Sgilles #define CONTROL_BACKLOG 5 363ef9cbf7Sgilles 37104d47d3Seric struct ctl_conn { 3865c4fdfbSgilles uint32_t id; 39104d47d3Seric uint8_t flags; 40104d47d3Seric #define CTL_CONN_NOTIFY 0x01 4165c4fdfbSgilles struct mproc mproc; 4265c4fdfbSgilles uid_t euid; 4365c4fdfbSgilles gid_t egid; 44104d47d3Seric }; 45104d47d3Seric 463ef9cbf7Sgilles struct { 473ef9cbf7Sgilles struct event ev; 483ef9cbf7Sgilles int fd; 493ef9cbf7Sgilles } control_state; 503ef9cbf7Sgilles 5165c4fdfbSgilles static void control_imsg(struct mproc *, struct imsg *); 52104d47d3Seric static void control_shutdown(void); 53104d47d3Seric static void control_listen(void); 54104d47d3Seric static void control_accept(int, short, void *); 55104d47d3Seric static void control_close(struct ctl_conn *); 5665c4fdfbSgilles static void control_dispatch_ext(struct mproc *, struct imsg *); 5782614934Seric static void control_digest_update(const char *, size_t, int); 5857d312f7Seric static void control_broadcast_verbose(int, int); 5982614934Seric 609ed3223cSgilles static struct stat_backend *stat_backend = NULL; 619ed3223cSgilles extern const char *backend_stat; 629ed3223cSgilles 63bf1fa702Sgilles static uint64_t connid = 0; 6465c4fdfbSgilles static struct tree ctl_conns; 658e9397b5Sgilles static struct tree ctl_count; 6699399201Smartijn static struct stat_digest digest; 6782614934Seric 6851474229Sgilles #define CONTROL_FD_RESERVE 5 698e9397b5Sgilles #define CONTROL_MAXCONN_PER_CLIENT 32 709ed3223cSgilles 71104d47d3Seric static void 7265c4fdfbSgilles control_imsg(struct mproc *p, struct imsg *imsg) 73ed1929b6Sjacekm { 74ed1929b6Sjacekm struct ctl_conn *c; 75e4632cf4Sgilles struct stat_value val; 7665c4fdfbSgilles struct msg m; 7765c4fdfbSgilles const char *key; 7865c4fdfbSgilles const void *data; 7965c4fdfbSgilles size_t sz; 80ed1929b6Sjacekm 8143962b9cSeric if (imsg == NULL) { 8243962b9cSeric if (p->proc != PROC_CLIENT) 8343962b9cSeric control_shutdown(); 8443962b9cSeric return; 8543962b9cSeric } 8643962b9cSeric 874fe02f32Seric switch (imsg->hdr.type) { 880fcb81a3Seric case IMSG_CTL_OK: 890fcb81a3Seric case IMSG_CTL_FAIL: 9065c4fdfbSgilles case IMSG_CTL_LIST_MESSAGES: 9165c4fdfbSgilles case IMSG_CTL_LIST_ENVELOPES: 92a9835440Ssunil case IMSG_CTL_DISCOVER_EVPID: 93a9835440Ssunil case IMSG_CTL_DISCOVER_MSGID: 94f9a337f4Seric case IMSG_CTL_MTA_SHOW_HOSTS: 95f9a337f4Seric case IMSG_CTL_MTA_SHOW_RELAYS: 96c5acbec8Seric case IMSG_CTL_MTA_SHOW_ROUTES: 97c5acbec8Seric case IMSG_CTL_MTA_SHOW_HOSTSTATS: 985b6a9ce9Seric case IMSG_CTL_MTA_SHOW_BLOCK: 99c5acbec8Seric c = tree_get(&ctl_conns, imsg->hdr.peerid); 100c5acbec8Seric if (c == NULL) 101c5acbec8Seric return; 1020fcb81a3Seric imsg->hdr.peerid = 0; 103c5acbec8Seric m_forward(&c->mproc, imsg); 104c5acbec8Seric return; 105ed1929b6Sjacekm 10625d8b68dSeric case IMSG_CTL_SMTP_SESSION: 10725d8b68dSeric c = tree_get(&ctl_conns, imsg->hdr.peerid); 10825d8b68dSeric if (c == NULL) 10925d8b68dSeric return; 110510586acSclaudio m_compose(&c->mproc, IMSG_CTL_OK, 0, 0, imsg_get_fd(imsg), 111510586acSclaudio NULL, 0); 11225d8b68dSeric return; 11325d8b68dSeric 1149ed3223cSgilles case IMSG_STAT_INCREMENT: 11565c4fdfbSgilles m_msg(&m, imsg); 11665c4fdfbSgilles m_get_string(&m, &key); 11765c4fdfbSgilles m_get_data(&m, &data, &sz); 11865c4fdfbSgilles m_end(&m); 119bc1ed85bSsunil if (sz != sizeof(val)) 120bc1ed85bSsunil fatalx("control: IMSG_STAT_INCREMENT size mismatch"); 12165c4fdfbSgilles memmove(&val, data, sz); 1229ed3223cSgilles if (stat_backend) 123e4632cf4Sgilles stat_backend->increment(key, val.u.counter); 12482614934Seric control_digest_update(key, val.u.counter, 1); 1259ed3223cSgilles return; 12625d8b68dSeric 1279ed3223cSgilles case IMSG_STAT_DECREMENT: 12865c4fdfbSgilles m_msg(&m, imsg); 12965c4fdfbSgilles m_get_string(&m, &key); 13065c4fdfbSgilles m_get_data(&m, &data, &sz); 13165c4fdfbSgilles m_end(&m); 132bc1ed85bSsunil if (sz != sizeof(val)) 133bc1ed85bSsunil fatalx("control: IMSG_STAT_DECREMENT size mismatch"); 13465c4fdfbSgilles memmove(&val, data, sz); 1359ed3223cSgilles if (stat_backend) 136e4632cf4Sgilles stat_backend->decrement(key, val.u.counter); 13782614934Seric control_digest_update(key, val.u.counter, 0); 1389ed3223cSgilles return; 13925d8b68dSeric 1409ed3223cSgilles case IMSG_STAT_SET: 14165c4fdfbSgilles m_msg(&m, imsg); 14265c4fdfbSgilles m_get_string(&m, &key); 14365c4fdfbSgilles m_get_data(&m, &data, &sz); 14465c4fdfbSgilles m_end(&m); 145bc1ed85bSsunil if (sz != sizeof(val)) 146bc1ed85bSsunil fatalx("control: IMSG_STAT_SET size mismatch"); 14765c4fdfbSgilles memmove(&val, data, sz); 1489ed3223cSgilles if (stat_backend) 149e4632cf4Sgilles stat_backend->set(key, &val); 1509ed3223cSgilles return; 1519ed3223cSgilles } 1529ed3223cSgilles 153ff01b044Seric fatalx("control_imsg: unexpected %s imsg", 1546d095224Schl imsg_to_str(imsg->hdr.type)); 155ed1929b6Sjacekm } 156ed1929b6Sjacekm 1575894db6eSeric int 1585894db6eSeric control_create_socket(void) 1593ef9cbf7Sgilles { 160ac61da4aSgilles struct sockaddr_un s_un; 1613ef9cbf7Sgilles int fd; 1623ef9cbf7Sgilles mode_t old_umask; 1633ef9cbf7Sgilles 1643ef9cbf7Sgilles if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) 1653ef9cbf7Sgilles fatal("control: socket"); 1663ef9cbf7Sgilles 167ac61da4aSgilles memset(&s_un, 0, sizeof(s_un)); 168ac61da4aSgilles s_un.sun_family = AF_UNIX; 169ac61da4aSgilles if (strlcpy(s_un.sun_path, SMTPD_SOCKET, 170ac61da4aSgilles sizeof(s_un.sun_path)) >= sizeof(s_un.sun_path)) 1713ef9cbf7Sgilles fatal("control: socket name too long"); 1723ef9cbf7Sgilles 173ac61da4aSgilles if (connect(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == 0) 1749ede5fd3Sjacekm fatalx("control socket already listening"); 1759ede5fd3Sjacekm 1763ef9cbf7Sgilles if (unlink(SMTPD_SOCKET) == -1) 1773ef9cbf7Sgilles if (errno != ENOENT) 1783ef9cbf7Sgilles fatal("control: cannot unlink socket"); 1793ef9cbf7Sgilles 1803ef9cbf7Sgilles old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); 181ac61da4aSgilles if (bind(fd, (struct sockaddr *)&s_un, sizeof(s_un)) == -1) { 1823ef9cbf7Sgilles (void)umask(old_umask); 1833ef9cbf7Sgilles fatal("control: bind"); 1843ef9cbf7Sgilles } 1853ef9cbf7Sgilles (void)umask(old_umask); 1863ef9cbf7Sgilles 1877bdbba2fSeric if (chmod(SMTPD_SOCKET, 1887bdbba2fSeric S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) == -1) { 1893ef9cbf7Sgilles (void)unlink(SMTPD_SOCKET); 1903ef9cbf7Sgilles fatal("control: chmod"); 1913ef9cbf7Sgilles } 1923ef9cbf7Sgilles 193907c4b99Skrw io_set_nonblocking(fd); 1943ef9cbf7Sgilles control_state.fd = fd; 1953ef9cbf7Sgilles 1965894db6eSeric return fd; 1975894db6eSeric } 1985894db6eSeric 199b88ab68dSeric int 2005894db6eSeric control(void) 2015894db6eSeric { 2025894db6eSeric struct passwd *pw; 2035894db6eSeric 2045894db6eSeric purge_config(PURGE_EVERYTHING); 2055894db6eSeric 2065894db6eSeric if ((pw = getpwnam(SMTPD_USER)) == NULL) 2075894db6eSeric fatalx("unknown user " SMTPD_USER); 2085894db6eSeric 2099ed3223cSgilles stat_backend = env->sc_stat; 2109ed3223cSgilles stat_backend->init(); 2119ed3223cSgilles 21211d04e02Seric if (chroot(PATH_CHROOT) == -1) 2133ef9cbf7Sgilles fatal("control: chroot"); 2143ef9cbf7Sgilles if (chdir("/") == -1) 2153ef9cbf7Sgilles fatal("control: chdir(\"/\")"); 2163ef9cbf7Sgilles 21765c4fdfbSgilles config_process(PROC_CONTROL); 2183ef9cbf7Sgilles 2193ef9cbf7Sgilles if (setgroups(1, &pw->pw_gid) || 2203ef9cbf7Sgilles setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 2213ef9cbf7Sgilles setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 2223ef9cbf7Sgilles fatal("control: cannot drop privileges"); 2233ef9cbf7Sgilles 224ed1929b6Sjacekm imsg_callback = control_imsg; 2253ef9cbf7Sgilles event_init(); 2263ef9cbf7Sgilles 22743962b9cSeric signal(SIGINT, SIG_IGN); 22843962b9cSeric signal(SIGTERM, SIG_IGN); 2293ef9cbf7Sgilles signal(SIGPIPE, SIG_IGN); 2303ef9cbf7Sgilles signal(SIGHUP, SIG_IGN); 2313ef9cbf7Sgilles 23265c4fdfbSgilles tree_init(&ctl_conns); 2338e9397b5Sgilles tree_init(&ctl_count); 2343ef9cbf7Sgilles 235c1392a69Seric memset(&digest, 0, sizeof digest); 23682614934Seric digest.startup = time(NULL); 23782614934Seric 23865c4fdfbSgilles config_peer(PROC_SCHEDULER); 23965c4fdfbSgilles config_peer(PROC_QUEUE); 24065c4fdfbSgilles config_peer(PROC_PARENT); 24165c4fdfbSgilles config_peer(PROC_LKA); 2421a5b831aSmartijn config_peer(PROC_DISPATCHER); 24357d312f7Seric config_peer(PROC_CA); 24465c4fdfbSgilles 245e4d36f12Seric control_listen(); 246e5b07014Sgilles 247b0d27b30Sgilles if (pledge("stdio unix recvfd sendfd", NULL) == -1) 248ff01b044Seric fatal("pledge"); 249b0d27b30Sgilles 250f94528c3Seric event_dispatch(); 251f94528c3Seric fatalx("exited event loop"); 2523ef9cbf7Sgilles 2533ef9cbf7Sgilles return (0); 2543ef9cbf7Sgilles } 2553ef9cbf7Sgilles 256104d47d3Seric static void 2573ef9cbf7Sgilles control_shutdown(void) 2583ef9cbf7Sgilles { 25943962b9cSeric log_debug("debug: control agent exiting"); 2603ef9cbf7Sgilles _exit(0); 2613ef9cbf7Sgilles } 2623ef9cbf7Sgilles 263104d47d3Seric static void 264e4d36f12Seric control_listen(void) 2653ef9cbf7Sgilles { 266cb5c228eSjacekm if (listen(control_state.fd, CONTROL_BACKLOG) == -1) 267cb5c228eSjacekm fatal("control_listen"); 2683ef9cbf7Sgilles 2693ef9cbf7Sgilles event_set(&control_state.ev, control_state.fd, EV_READ|EV_PERSIST, 270e4d36f12Seric control_accept, NULL); 2713ef9cbf7Sgilles event_add(&control_state.ev, NULL); 2723ef9cbf7Sgilles } 2733ef9cbf7Sgilles 274104d47d3Seric static void 2753ef9cbf7Sgilles control_accept(int listenfd, short event, void *arg) 2763ef9cbf7Sgilles { 2773ef9cbf7Sgilles int connfd; 2783ef9cbf7Sgilles socklen_t len; 279ac61da4aSgilles struct sockaddr_un s_un; 2803ef9cbf7Sgilles struct ctl_conn *c; 2818e9397b5Sgilles size_t *count; 2828e9397b5Sgilles uid_t euid; 2838e9397b5Sgilles gid_t egid; 2843ef9cbf7Sgilles 28551474229Sgilles if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE) 28651474229Sgilles goto pause; 28751474229Sgilles 288ac61da4aSgilles len = sizeof(s_un); 289ac61da4aSgilles if ((connfd = accept(listenfd, (struct sockaddr *)&s_un, &len)) == -1) { 29051474229Sgilles if (errno == ENFILE || errno == EMFILE) 29151474229Sgilles goto pause; 29262e3c252Sderaadt if (errno == EINTR || errno == EWOULDBLOCK || 29362e3c252Sderaadt errno == ECONNABORTED) 2943ef9cbf7Sgilles return; 295cb5c228eSjacekm fatal("control_accept: accept"); 2963ef9cbf7Sgilles } 2973ef9cbf7Sgilles 298907c4b99Skrw io_set_nonblocking(connfd); 2993ef9cbf7Sgilles 3008e9397b5Sgilles if (getpeereid(connfd, &euid, &egid) == -1) 30165c4fdfbSgilles fatal("getpeereid"); 3028e9397b5Sgilles 3038e9397b5Sgilles count = tree_get(&ctl_count, euid); 3048e9397b5Sgilles if (count == NULL) { 305118c16f3Sgilles count = xcalloc(1, sizeof *count); 3068e9397b5Sgilles tree_xset(&ctl_count, euid, count); 3078e9397b5Sgilles } 3088e9397b5Sgilles 3098e9397b5Sgilles if (*count == CONTROL_MAXCONN_PER_CLIENT) { 3108e9397b5Sgilles close(connfd); 3118e9397b5Sgilles log_warnx("warn: too many connections to control socket " 3128e9397b5Sgilles "from user with uid %lu", (unsigned long int)euid); 3138e9397b5Sgilles return; 3148e9397b5Sgilles } 3158e9397b5Sgilles (*count)++; 3168e9397b5Sgilles 317bf1fa702Sgilles do { 318bf1fa702Sgilles ++connid; 319bf1fa702Sgilles } while (tree_get(&ctl_conns, connid)); 320bf1fa702Sgilles 321118c16f3Sgilles c = xcalloc(1, sizeof(*c)); 3228e9397b5Sgilles c->euid = euid; 3238e9397b5Sgilles c->egid = egid; 324bf1fa702Sgilles c->id = connid; 325299c4efeSeric c->mproc.proc = PROC_CLIENT; 32665c4fdfbSgilles c->mproc.handler = control_dispatch_ext; 32765c4fdfbSgilles c->mproc.data = c; 328fb531d31Santon if ((c->mproc.name = strdup(proc_title(c->mproc.proc))) == NULL) 329fb531d31Santon fatal("strdup"); 33065c4fdfbSgilles mproc_init(&c->mproc, connfd); 33165c4fdfbSgilles mproc_enable(&c->mproc); 33265c4fdfbSgilles tree_xset(&ctl_conns, c->id, c); 333cb5c228eSjacekm 334e4632cf4Sgilles stat_backend->increment("control.session", 1); 33551474229Sgilles return; 3369ed3223cSgilles 33751474229Sgilles pause: 33882614934Seric log_warnx("warn: ctl client limit hit, disabling new connections"); 339cb5c228eSjacekm event_del(&control_state.ev); 340cb5c228eSjacekm } 3413ef9cbf7Sgilles 342104d47d3Seric static void 343104d47d3Seric control_close(struct ctl_conn *c) 3443ef9cbf7Sgilles { 3458e9397b5Sgilles size_t *count; 3468e9397b5Sgilles 3478e9397b5Sgilles count = tree_xget(&ctl_count, c->euid); 3488e9397b5Sgilles (*count)--; 3498e9397b5Sgilles if (*count == 0) { 3508e9397b5Sgilles tree_xpop(&ctl_count, c->euid); 3518e9397b5Sgilles free(count); 3528e9397b5Sgilles } 35365c4fdfbSgilles tree_xpop(&ctl_conns, c->id); 35465c4fdfbSgilles mproc_clear(&c->mproc); 3553ef9cbf7Sgilles free(c); 356cb5c228eSjacekm 357e4632cf4Sgilles stat_backend->decrement("control.session", 1); 3589ed3223cSgilles 35951474229Sgilles if (getdtablesize() - getdtablecount() < CONTROL_FD_RESERVE) 36051474229Sgilles return; 36151474229Sgilles 36251474229Sgilles if (!event_pending(&control_state.ev, EV_READ, NULL)) { 36382614934Seric log_warnx("warn: re-enabling ctl connections"); 364cb5c228eSjacekm event_add(&control_state.ev, NULL); 365cb5c228eSjacekm } 3663ef9cbf7Sgilles } 3673ef9cbf7Sgilles 36882614934Seric static void 36982614934Seric control_digest_update(const char *key, size_t value, int incr) 37082614934Seric { 37182614934Seric size_t *p; 37282614934Seric 37382614934Seric p = NULL; 37482614934Seric 37582614934Seric if (!strcmp(key, "smtp.session")) { 37682614934Seric if (incr) 37782614934Seric p = &digest.clt_connect; 37882614934Seric else 37982614934Seric digest.clt_disconnect += value; 38082614934Seric } 38182614934Seric else if (!strcmp(key, "scheduler.envelope")) { 38282614934Seric if (incr) 38382614934Seric p = &digest.evp_enqueued; 38482614934Seric else 38582614934Seric digest.evp_dequeued += value; 38682614934Seric } 38782614934Seric else if (!strcmp(key, "scheduler.envelope.expired")) 38882614934Seric p = &digest.evp_expired; 38982614934Seric else if (!strcmp(key, "scheduler.envelope.removed")) 39082614934Seric p = &digest.evp_removed; 39182614934Seric else if (!strcmp(key, "scheduler.delivery.ok")) 39282614934Seric p = &digest.dlv_ok; 39382614934Seric else if (!strcmp(key, "scheduler.delivery.permfail")) 39482614934Seric p = &digest.dlv_permfail; 39582614934Seric else if (!strcmp(key, "scheduler.delivery.tempfail")) 39682614934Seric p = &digest.dlv_tempfail; 39782614934Seric else if (!strcmp(key, "scheduler.delivery.loop")) 39882614934Seric p = &digest.dlv_loop; 39982614934Seric 40082614934Seric else if (!strcmp(key, "queue.bounce")) 40182614934Seric p = &digest.evp_bounce; 40282614934Seric 40382614934Seric if (p) { 40482614934Seric if (incr) 40582614934Seric *p = *p + value; 40682614934Seric else 40782614934Seric *p = *p - value; 40882614934Seric } 40982614934Seric } 41082614934Seric 411104d47d3Seric static void 41265c4fdfbSgilles control_dispatch_ext(struct mproc *p, struct imsg *imsg) 4133ef9cbf7Sgilles { 4145b6a9ce9Seric struct sockaddr_storage ss; 4153ef9cbf7Sgilles struct ctl_conn *c; 41665c4fdfbSgilles int v; 4179ed3223cSgilles struct stat_kv *kvp; 4189ed3223cSgilles char *key; 419e4632cf4Sgilles struct stat_value val; 420e9283ba6Sgilles size_t len; 421a9835440Ssunil uint64_t evpid; 422a9835440Ssunil uint32_t msgid; 423b7191141Sgilles 42465c4fdfbSgilles c = p->data; 4253ef9cbf7Sgilles 42665c4fdfbSgilles if (imsg == NULL) { 427104d47d3Seric control_close(c); 4283ef9cbf7Sgilles return; 4293ef9cbf7Sgilles } 4303ef9cbf7Sgilles 431299c4efeSeric if (imsg->hdr.peerid != IMSG_VERSION) { 432299c4efeSeric m_compose(p, IMSG_CTL_FAIL, IMSG_VERSION, 0, -1, NULL, 0); 433299c4efeSeric return; 434299c4efeSeric } 435299c4efeSeric 43665c4fdfbSgilles switch (imsg->hdr.type) { 437aa1d5973Seric case IMSG_CTL_SMTP_SESSION: 438*bf921b2aSclaudio imsgbuf_allow_fdpass(&p->imsgbuf); 4394dcd8201Seric if (env->sc_flags & SMTPD_SMTP_PAUSED) { 44065c4fdfbSgilles m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 44165c4fdfbSgilles return; 44221dda8f7Sjacekm } 4431a5b831aSmartijn m_compose(p_dispatcher, IMSG_CTL_SMTP_SESSION, c->id, 0, -1, 44465c4fdfbSgilles &c->euid, sizeof(c->euid)); 44565c4fdfbSgilles return; 4469b96bad3Seric 447aa1d5973Seric case IMSG_CTL_GET_DIGEST: 44865c4fdfbSgilles if (c->euid) 44982614934Seric goto badcred; 45082614934Seric digest.timestamp = time(NULL); 451aa1d5973Seric m_compose(p, IMSG_CTL_GET_DIGEST, 0, 0, -1, &digest, sizeof digest); 45265c4fdfbSgilles return; 45382614934Seric 454aa1d5973Seric case IMSG_CTL_GET_STATS: 45565c4fdfbSgilles if (c->euid) 4569ed3223cSgilles goto badcred; 45765c4fdfbSgilles kvp = imsg->data; 4589ed3223cSgilles if (!stat_backend->iter(&kvp->iter, &key, &val)) 4599ed3223cSgilles kvp->iter = NULL; 4609ed3223cSgilles else { 461e42ff713Sgilles (void)strlcpy(kvp->key, key, sizeof kvp->key); 4629ed3223cSgilles kvp->val = val; 4639ed3223cSgilles } 464aa1d5973Seric m_compose(p, IMSG_CTL_GET_STATS, 0, 0, -1, kvp, sizeof *kvp); 46565c4fdfbSgilles return; 4669b96bad3Seric 4679b96bad3Seric case IMSG_CTL_VERBOSE: 46865c4fdfbSgilles if (c->euid) 469ba0c8f48Schl goto badcred; 470ba0c8f48Schl 471f24248b7Sreyk if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) 472ba0c8f48Schl goto badcred; 473ba0c8f48Schl 47465c4fdfbSgilles memcpy(&v, imsg->data, sizeof(v)); 475f24248b7Sreyk log_trace_verbose(v); 4769b96bad3Seric 477f24248b7Sreyk control_broadcast_verbose(IMSG_CTL_VERBOSE, v); 47865c4fdfbSgilles 47965c4fdfbSgilles m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 48065c4fdfbSgilles return; 48165c4fdfbSgilles 482aa1d5973Seric case IMSG_CTL_TRACE_ENABLE: 48365c4fdfbSgilles if (c->euid) 48465c4fdfbSgilles goto badcred; 48565c4fdfbSgilles 486f24248b7Sreyk if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) 48765c4fdfbSgilles goto badcred; 48865c4fdfbSgilles 48965c4fdfbSgilles memcpy(&v, imsg->data, sizeof(v)); 490f24248b7Sreyk tracing |= v; 491f24248b7Sreyk log_trace_verbose(tracing); 49265c4fdfbSgilles 493f24248b7Sreyk control_broadcast_verbose(IMSG_CTL_VERBOSE, tracing); 49465c4fdfbSgilles 49565c4fdfbSgilles m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 49665c4fdfbSgilles return; 49765c4fdfbSgilles 498aa1d5973Seric case IMSG_CTL_TRACE_DISABLE: 49965c4fdfbSgilles if (c->euid) 50065c4fdfbSgilles goto badcred; 50165c4fdfbSgilles 502f24248b7Sreyk if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) 50365c4fdfbSgilles goto badcred; 50465c4fdfbSgilles 50565c4fdfbSgilles memcpy(&v, imsg->data, sizeof(v)); 506f24248b7Sreyk tracing &= ~v; 507f24248b7Sreyk log_trace_verbose(tracing); 50865c4fdfbSgilles 509f24248b7Sreyk control_broadcast_verbose(IMSG_CTL_VERBOSE, tracing); 51065c4fdfbSgilles 51165c4fdfbSgilles m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 51265c4fdfbSgilles return; 51365c4fdfbSgilles 514aa1d5973Seric case IMSG_CTL_PROFILE_ENABLE: 51565c4fdfbSgilles if (c->euid) 51665c4fdfbSgilles goto badcred; 51765c4fdfbSgilles 518f24248b7Sreyk if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) 51965c4fdfbSgilles goto badcred; 52065c4fdfbSgilles 52165c4fdfbSgilles memcpy(&v, imsg->data, sizeof(v)); 52265c4fdfbSgilles profiling |= v; 52365c4fdfbSgilles 52457d312f7Seric control_broadcast_verbose(IMSG_CTL_PROFILE, profiling); 52565c4fdfbSgilles 52665c4fdfbSgilles m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 52765c4fdfbSgilles return; 52865c4fdfbSgilles 529aa1d5973Seric case IMSG_CTL_PROFILE_DISABLE: 53065c4fdfbSgilles if (c->euid) 53165c4fdfbSgilles goto badcred; 53265c4fdfbSgilles 533f24248b7Sreyk if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof(v)) 53465c4fdfbSgilles goto badcred; 53565c4fdfbSgilles 53665c4fdfbSgilles memcpy(&v, imsg->data, sizeof(v)); 53765c4fdfbSgilles profiling &= ~v; 53865c4fdfbSgilles 53957d312f7Seric control_broadcast_verbose(IMSG_CTL_PROFILE, profiling); 54065c4fdfbSgilles 54165c4fdfbSgilles m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 54265c4fdfbSgilles return; 54365c4fdfbSgilles 54435e161d3Seric case IMSG_CTL_PAUSE_EVP: 54535e161d3Seric if (c->euid) 54635e161d3Seric goto badcred; 54735e161d3Seric 5480fcb81a3Seric imsg->hdr.peerid = c->id; 54935e161d3Seric m_forward(p_scheduler, imsg); 55035e161d3Seric return; 55135e161d3Seric 55265c4fdfbSgilles case IMSG_CTL_PAUSE_MDA: 55365c4fdfbSgilles if (c->euid) 554b7191141Sgilles goto badcred; 555b7191141Sgilles 556df079d11Sgilles if (env->sc_flags & SMTPD_MDA_PAUSED) { 55765c4fdfbSgilles m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 55865c4fdfbSgilles return; 559df079d11Sgilles } 56082614934Seric log_info("info: mda paused"); 561df079d11Sgilles env->sc_flags |= SMTPD_MDA_PAUSED; 56265c4fdfbSgilles m_compose(p_queue, IMSG_CTL_PAUSE_MDA, 0, 0, -1, NULL, 0); 56365c4fdfbSgilles m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 56465c4fdfbSgilles return; 5659b96bad3Seric 56665c4fdfbSgilles case IMSG_CTL_PAUSE_MTA: 56765c4fdfbSgilles if (c->euid) 568b7191141Sgilles goto badcred; 569b7191141Sgilles 570df079d11Sgilles if (env->sc_flags & SMTPD_MTA_PAUSED) { 57165c4fdfbSgilles m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 57265c4fdfbSgilles return; 573df079d11Sgilles } 57482614934Seric log_info("info: mta paused"); 575df079d11Sgilles env->sc_flags |= SMTPD_MTA_PAUSED; 57665c4fdfbSgilles m_compose(p_queue, IMSG_CTL_PAUSE_MTA, 0, 0, -1, NULL, 0); 57765c4fdfbSgilles m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 57865c4fdfbSgilles return; 5799b96bad3Seric 58065c4fdfbSgilles case IMSG_CTL_PAUSE_SMTP: 58165c4fdfbSgilles if (c->euid) 582b7191141Sgilles goto badcred; 583b7191141Sgilles 584c78ed3ecSgilles if (env->sc_flags & SMTPD_SMTP_PAUSED) { 58565c4fdfbSgilles m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 58665c4fdfbSgilles return; 587c78ed3ecSgilles } 58882614934Seric log_info("info: smtp paused"); 589c78ed3ecSgilles env->sc_flags |= SMTPD_SMTP_PAUSED; 5901a5b831aSmartijn m_compose(p_dispatcher, IMSG_CTL_PAUSE_SMTP, 0, 0, -1, NULL, 0); 59165c4fdfbSgilles m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 59265c4fdfbSgilles return; 5939b96bad3Seric 59435e161d3Seric case IMSG_CTL_RESUME_EVP: 59535e161d3Seric if (c->euid) 59635e161d3Seric goto badcred; 59735e161d3Seric 5980fcb81a3Seric imsg->hdr.peerid = c->id; 59935e161d3Seric m_forward(p_scheduler, imsg); 60035e161d3Seric return; 60135e161d3Seric 60265c4fdfbSgilles case IMSG_CTL_RESUME_MDA: 60365c4fdfbSgilles if (c->euid) 604b7191141Sgilles goto badcred; 605b7191141Sgilles 606df079d11Sgilles if (!(env->sc_flags & SMTPD_MDA_PAUSED)) { 60765c4fdfbSgilles m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 60865c4fdfbSgilles return; 609df079d11Sgilles } 61082614934Seric log_info("info: mda resumed"); 611df079d11Sgilles env->sc_flags &= ~SMTPD_MDA_PAUSED; 61265c4fdfbSgilles m_compose(p_queue, IMSG_CTL_RESUME_MDA, 0, 0, -1, NULL, 0); 61365c4fdfbSgilles m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 61465c4fdfbSgilles return; 6159b96bad3Seric 61665c4fdfbSgilles case IMSG_CTL_RESUME_MTA: 61765c4fdfbSgilles if (c->euid) 618b7191141Sgilles goto badcred; 619b7191141Sgilles 620df079d11Sgilles if (!(env->sc_flags & SMTPD_MTA_PAUSED)) { 62165c4fdfbSgilles m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 62265c4fdfbSgilles return; 623df079d11Sgilles } 62482614934Seric log_info("info: mta resumed"); 625df079d11Sgilles env->sc_flags &= ~SMTPD_MTA_PAUSED; 62665c4fdfbSgilles m_compose(p_queue, IMSG_CTL_RESUME_MTA, 0, 0, -1, NULL, 0); 62765c4fdfbSgilles m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 62865c4fdfbSgilles return; 629ed5c65a8Sgilles 63065c4fdfbSgilles case IMSG_CTL_RESUME_SMTP: 63165c4fdfbSgilles if (c->euid) 632b7191141Sgilles goto badcred; 633b7191141Sgilles 634c78ed3ecSgilles if (!(env->sc_flags & SMTPD_SMTP_PAUSED)) { 63565c4fdfbSgilles m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 63665c4fdfbSgilles return; 637c78ed3ecSgilles } 63882614934Seric log_info("info: smtp resumed"); 639c78ed3ecSgilles env->sc_flags &= ~SMTPD_SMTP_PAUSED; 6401a5b831aSmartijn m_forward(p_dispatcher, imsg); 64165c4fdfbSgilles m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 64265c4fdfbSgilles return; 643ed5c65a8Sgilles 644c5acbec8Seric case IMSG_CTL_RESUME_ROUTE: 645c5acbec8Seric if (c->euid) 646c5acbec8Seric goto badcred; 647c5acbec8Seric 6481a5b831aSmartijn m_forward(p_dispatcher, imsg); 649c5acbec8Seric m_compose(p, IMSG_CTL_OK, 0, 0, -1, NULL, 0); 650c5acbec8Seric return; 651c5acbec8Seric 65265c4fdfbSgilles case IMSG_CTL_LIST_MESSAGES: 65365c4fdfbSgilles if (c->euid) 6544fe02f32Seric goto badcred; 65565c4fdfbSgilles m_compose(p_scheduler, IMSG_CTL_LIST_MESSAGES, c->id, 0, -1, 65665c4fdfbSgilles imsg->data, imsg->hdr.len - sizeof(imsg->hdr)); 65765c4fdfbSgilles return; 6584fe02f32Seric 65965c4fdfbSgilles case IMSG_CTL_LIST_ENVELOPES: 66065c4fdfbSgilles if (c->euid) 6614fe02f32Seric goto badcred; 66265c4fdfbSgilles m_compose(p_scheduler, IMSG_CTL_LIST_ENVELOPES, c->id, 0, -1, 66365c4fdfbSgilles imsg->data, imsg->hdr.len - sizeof(imsg->hdr)); 66465c4fdfbSgilles return; 6654fe02f32Seric 666f9a337f4Seric case IMSG_CTL_MTA_SHOW_HOSTS: 667f9a337f4Seric case IMSG_CTL_MTA_SHOW_RELAYS: 668c5acbec8Seric case IMSG_CTL_MTA_SHOW_ROUTES: 669c5acbec8Seric case IMSG_CTL_MTA_SHOW_HOSTSTATS: 6705b6a9ce9Seric case IMSG_CTL_MTA_SHOW_BLOCK: 671c5acbec8Seric if (c->euid) 672c5acbec8Seric goto badcred; 6730fcb81a3Seric 6740fcb81a3Seric imsg->hdr.peerid = c->id; 6751a5b831aSmartijn m_forward(p_dispatcher, imsg); 676c5acbec8Seric return; 677c5acbec8Seric 678c37e9483Seric case IMSG_CTL_SHOW_STATUS: 679c37e9483Seric if (c->euid) 680c37e9483Seric goto badcred; 681c37e9483Seric 682c37e9483Seric m_compose(p, IMSG_CTL_SHOW_STATUS, 0, 0, -1, &env->sc_flags, 683c37e9483Seric sizeof(env->sc_flags)); 684c37e9483Seric return; 685c37e9483Seric 6865b6a9ce9Seric case IMSG_CTL_MTA_BLOCK: 6875b6a9ce9Seric case IMSG_CTL_MTA_UNBLOCK: 6885b6a9ce9Seric if (c->euid) 6895b6a9ce9Seric goto badcred; 6905b6a9ce9Seric 6915b6a9ce9Seric if (imsg->hdr.len - IMSG_HEADER_SIZE <= sizeof(ss)) 6925b6a9ce9Seric goto invalid; 6935b6a9ce9Seric memmove(&ss, imsg->data, sizeof(ss)); 6941a5b831aSmartijn m_create(p_dispatcher, imsg->hdr.type, c->id, 0, -1); 6951a5b831aSmartijn m_add_sockaddr(p_dispatcher, (struct sockaddr *)&ss); 6961a5b831aSmartijn m_add_string(p_dispatcher, (char *)imsg->data + sizeof(ss)); 6971a5b831aSmartijn m_close(p_dispatcher); 6985b6a9ce9Seric return; 6995b6a9ce9Seric 70065c4fdfbSgilles case IMSG_CTL_SCHEDULE: 70165c4fdfbSgilles if (c->euid) 702ed5c65a8Sgilles goto badcred; 703ed5c65a8Sgilles 7040fcb81a3Seric imsg->hdr.peerid = c->id; 70565c4fdfbSgilles m_forward(p_scheduler, imsg); 70665c4fdfbSgilles return; 707ed5c65a8Sgilles 70865c4fdfbSgilles case IMSG_CTL_REMOVE: 70965c4fdfbSgilles if (c->euid) 710ed5c65a8Sgilles goto badcred; 711ed5c65a8Sgilles 7120fcb81a3Seric imsg->hdr.peerid = c->id; 71365c4fdfbSgilles m_forward(p_scheduler, imsg); 71465c4fdfbSgilles return; 7159b96bad3Seric 716aa1d5973Seric case IMSG_CTL_UPDATE_TABLE: 71765c4fdfbSgilles if (c->euid) 718e9283ba6Sgilles goto badcred; 719e9283ba6Sgilles 72065c4fdfbSgilles /* table name too long */ 72165c4fdfbSgilles len = strlen(imsg->data); 722953aae25Sderaadt if (len >= LINE_MAX) 723e9283ba6Sgilles goto invalid; 724e9283ba6Sgilles 725bbe9c651Ssunil imsg->hdr.peerid = c->id; 72665c4fdfbSgilles m_forward(p_lka, imsg); 72765c4fdfbSgilles return; 728e9283ba6Sgilles 729a9835440Ssunil case IMSG_CTL_DISCOVER_EVPID: 730a9835440Ssunil if (c->euid) 731a9835440Ssunil goto badcred; 732a9835440Ssunil 733a9835440Ssunil if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof evpid) 734a9835440Ssunil goto invalid; 735a9835440Ssunil 736a9835440Ssunil memmove(&evpid, imsg->data, sizeof evpid); 737a9835440Ssunil m_create(p_queue, imsg->hdr.type, c->id, 0, -1); 738a9835440Ssunil m_add_evpid(p_queue, evpid); 739a9835440Ssunil m_close(p_queue); 740a9835440Ssunil return; 741a9835440Ssunil 742a9835440Ssunil case IMSG_CTL_DISCOVER_MSGID: 743a9835440Ssunil if (c->euid) 744a9835440Ssunil goto badcred; 745a9835440Ssunil 746a9835440Ssunil if (imsg->hdr.len - IMSG_HEADER_SIZE != sizeof msgid) 747a9835440Ssunil goto invalid; 748a9835440Ssunil 749a9835440Ssunil memmove(&msgid, imsg->data, sizeof msgid); 750a9835440Ssunil m_create(p_queue, imsg->hdr.type, c->id, 0, -1); 751a9835440Ssunil m_add_msgid(p_queue, msgid); 752a9835440Ssunil m_close(p_queue); 753a9835440Ssunil return; 754a9835440Ssunil 7553ef9cbf7Sgilles default: 75682614934Seric log_debug("debug: control_dispatch_ext: " 7576d095224Schl "error handling %s imsg", 75865c4fdfbSgilles imsg_to_str(imsg->hdr.type)); 75965c4fdfbSgilles return; 7603ef9cbf7Sgilles } 761b7191141Sgilles badcred: 762e9283ba6Sgilles invalid: 76365c4fdfbSgilles m_compose(p, IMSG_CTL_FAIL, 0, 0, -1, NULL, 0); 7643ef9cbf7Sgilles } 76557d312f7Seric 76657d312f7Seric static void 76757d312f7Seric control_broadcast_verbose(int msg, int v) 76857d312f7Seric { 76957d312f7Seric m_create(p_lka, msg, 0, 0, -1); 77057d312f7Seric m_add_int(p_lka, v); 77157d312f7Seric m_close(p_lka); 77257d312f7Seric 7731a5b831aSmartijn m_create(p_dispatcher, msg, 0, 0, -1); 7741a5b831aSmartijn m_add_int(p_dispatcher, v); 7751a5b831aSmartijn m_close(p_dispatcher); 77657d312f7Seric 77757d312f7Seric m_create(p_queue, msg, 0, 0, -1); 77857d312f7Seric m_add_int(p_queue, v); 77957d312f7Seric m_close(p_queue); 78057d312f7Seric 78157d312f7Seric m_create(p_ca, msg, 0, 0, -1); 78257d312f7Seric m_add_int(p_ca, v); 78357d312f7Seric m_close(p_ca); 78457d312f7Seric 78557d312f7Seric m_create(p_scheduler, msg, 0, 0, -1); 78657d312f7Seric m_add_int(p_scheduler, v); 78757d312f7Seric m_close(p_scheduler); 78857d312f7Seric 78957d312f7Seric m_create(p_parent, msg, 0, 0, -1); 79057d312f7Seric m_add_int(p_parent, v); 79157d312f7Seric m_close(p_parent); 79257d312f7Seric } 793