1*882428cdSclaudio /* $OpenBSD: control.c,v 1.7 2024/11/21 13:43:10 claudio Exp $ */ 2842565f2Syasuoka 3842565f2Syasuoka /* 4842565f2Syasuoka * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 5842565f2Syasuoka * 6842565f2Syasuoka * Permission to use, copy, modify, and distribute this software for any 7842565f2Syasuoka * purpose with or without fee is hereby granted, provided that the above 8842565f2Syasuoka * copyright notice and this permission notice appear in all copies. 9842565f2Syasuoka * 10842565f2Syasuoka * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11842565f2Syasuoka * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12842565f2Syasuoka * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13842565f2Syasuoka * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14842565f2Syasuoka * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15842565f2Syasuoka * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16842565f2Syasuoka * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17842565f2Syasuoka */ 18842565f2Syasuoka 19842565f2Syasuoka #include <sys/types.h> 20842565f2Syasuoka #include <sys/queue.h> 21842565f2Syasuoka #include <sys/socket.h> 22842565f2Syasuoka #include <sys/stat.h> 23842565f2Syasuoka #include <sys/time.h> 24842565f2Syasuoka #include <sys/un.h> 25842565f2Syasuoka 26842565f2Syasuoka #include <errno.h> 27842565f2Syasuoka #include <event.h> 28842565f2Syasuoka #include <imsg.h> 29842565f2Syasuoka #include <stdlib.h> 30842565f2Syasuoka #include <stdio.h> 31842565f2Syasuoka #include <string.h> 32842565f2Syasuoka #include <unistd.h> 33842565f2Syasuoka 34842565f2Syasuoka #include "radiusd.h" 35842565f2Syasuoka #include "radiusd_local.h" 36842565f2Syasuoka #include "log.h" 37842565f2Syasuoka #include "control.h" 38842565f2Syasuoka 39842565f2Syasuoka static TAILQ_HEAD(, ctl_conn) ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns); 40842565f2Syasuoka 41842565f2Syasuoka #define CONTROL_BACKLOG 5 42842565f2Syasuoka static int idseq = 0; 43842565f2Syasuoka 44842565f2Syasuoka struct ctl_conn *control_connbyfd(int); 45842565f2Syasuoka struct ctl_conn *control_connbyid(uint32_t); 46842565f2Syasuoka void control_close(int); 47842565f2Syasuoka void control_connfree(struct ctl_conn *); 48842565f2Syasuoka void control_event_add(struct ctl_conn *); 49842565f2Syasuoka 50842565f2Syasuoka struct { 51842565f2Syasuoka struct event ev; 52842565f2Syasuoka struct event evt; 53842565f2Syasuoka int fd; 54842565f2Syasuoka } control_state; 55842565f2Syasuoka 56842565f2Syasuoka int 57842565f2Syasuoka control_init(const char *path) 58842565f2Syasuoka { 59842565f2Syasuoka struct sockaddr_un sun; 60842565f2Syasuoka int fd; 61842565f2Syasuoka mode_t old_umask; 62842565f2Syasuoka 63842565f2Syasuoka if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 64842565f2Syasuoka 0)) == -1) { 65842565f2Syasuoka log_warn("control_init: socket"); 66842565f2Syasuoka return (-1); 67842565f2Syasuoka } 68842565f2Syasuoka 69842565f2Syasuoka memset(&sun, 0, sizeof(sun)); 70842565f2Syasuoka sun.sun_family = AF_UNIX; 71842565f2Syasuoka strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); 72842565f2Syasuoka 73842565f2Syasuoka if (unlink(path) == -1) 74842565f2Syasuoka if (errno != ENOENT) { 75842565f2Syasuoka log_warn("control_init: unlink %s", path); 76842565f2Syasuoka close(fd); 77842565f2Syasuoka return (-1); 78842565f2Syasuoka } 79842565f2Syasuoka 80842565f2Syasuoka old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH); 81842565f2Syasuoka if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) { 82842565f2Syasuoka log_warn("control_init: bind: %s", path); 83842565f2Syasuoka close(fd); 84842565f2Syasuoka umask(old_umask); 85842565f2Syasuoka return (-1); 86842565f2Syasuoka } 87842565f2Syasuoka umask(old_umask); 88842565f2Syasuoka 89842565f2Syasuoka if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) { 90842565f2Syasuoka log_warn("control_init: chmod"); 91842565f2Syasuoka close(fd); 92842565f2Syasuoka (void)unlink(path); 93842565f2Syasuoka return (-1); 94842565f2Syasuoka } 95842565f2Syasuoka 96842565f2Syasuoka control_state.fd = fd; 97842565f2Syasuoka 98842565f2Syasuoka return (0); 99842565f2Syasuoka } 100842565f2Syasuoka 101842565f2Syasuoka int 102842565f2Syasuoka control_listen(void) 103842565f2Syasuoka { 104842565f2Syasuoka 105842565f2Syasuoka if (listen(control_state.fd, CONTROL_BACKLOG) == -1) { 106842565f2Syasuoka log_warn("control_listen: listen"); 107842565f2Syasuoka return (-1); 108842565f2Syasuoka } 109842565f2Syasuoka 110842565f2Syasuoka event_set(&control_state.ev, control_state.fd, EV_READ, 111842565f2Syasuoka control_accept, NULL); 112842565f2Syasuoka event_add(&control_state.ev, NULL); 113842565f2Syasuoka evtimer_set(&control_state.evt, control_accept, NULL); 114842565f2Syasuoka 115842565f2Syasuoka return (0); 116842565f2Syasuoka } 117842565f2Syasuoka 118842565f2Syasuoka void 119842565f2Syasuoka control_cleanup(void) 120842565f2Syasuoka { 121842565f2Syasuoka struct ctl_conn *c, *t; 122842565f2Syasuoka 123842565f2Syasuoka TAILQ_FOREACH_SAFE(c, &ctl_conns, entry, t) { 124842565f2Syasuoka TAILQ_REMOVE(&ctl_conns, c, entry); 125842565f2Syasuoka control_connfree(c); 126842565f2Syasuoka } 127842565f2Syasuoka event_del(&control_state.ev); 128842565f2Syasuoka event_del(&control_state.evt); 129842565f2Syasuoka } 130842565f2Syasuoka 131842565f2Syasuoka /* ARGSUSED */ 132842565f2Syasuoka void 133842565f2Syasuoka control_accept(int listenfd, short event, void *bula) 134842565f2Syasuoka { 135842565f2Syasuoka int connfd; 136842565f2Syasuoka socklen_t len; 137842565f2Syasuoka struct sockaddr_un sun; 138842565f2Syasuoka struct ctl_conn *c; 139842565f2Syasuoka 140842565f2Syasuoka event_add(&control_state.ev, NULL); 141842565f2Syasuoka if ((event & EV_TIMEOUT)) 142842565f2Syasuoka return; 143842565f2Syasuoka 144842565f2Syasuoka len = sizeof(sun); 145842565f2Syasuoka if ((connfd = accept4(listenfd, (struct sockaddr *)&sun, &len, 146842565f2Syasuoka SOCK_CLOEXEC | SOCK_NONBLOCK)) == -1) { 147842565f2Syasuoka /* 148842565f2Syasuoka * Pause accept if we are out of file descriptors, or 149842565f2Syasuoka * libevent will haunt us here too. 150842565f2Syasuoka */ 151842565f2Syasuoka if (errno == ENFILE || errno == EMFILE) { 152842565f2Syasuoka struct timeval evtpause = { 1, 0 }; 153842565f2Syasuoka 154842565f2Syasuoka event_del(&control_state.ev); 155842565f2Syasuoka evtimer_add(&control_state.evt, &evtpause); 156842565f2Syasuoka } else if (errno != EWOULDBLOCK && errno != EINTR && 157842565f2Syasuoka errno != ECONNABORTED) 158842565f2Syasuoka log_warn("control_accept: accept"); 159842565f2Syasuoka return; 160842565f2Syasuoka } 161842565f2Syasuoka 162842565f2Syasuoka if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) { 163842565f2Syasuoka log_warn("control_accept"); 164842565f2Syasuoka close(connfd); 165842565f2Syasuoka return; 166842565f2Syasuoka } 167842565f2Syasuoka 168*882428cdSclaudio if (imsgbuf_init(&c->iev.ibuf, connfd) == -1) { 169*882428cdSclaudio log_warn("control_accept"); 170*882428cdSclaudio close(connfd); 171*882428cdSclaudio free(c); 172*882428cdSclaudio return; 173*882428cdSclaudio } 174842565f2Syasuoka if (idseq == 0) /* don't use zero. See radiusd_module_imsg */ 175842565f2Syasuoka ++idseq; 176842565f2Syasuoka c->id = idseq++; 177842565f2Syasuoka c->iev.handler = control_dispatch_imsg; 178842565f2Syasuoka c->iev.events = EV_READ; 179842565f2Syasuoka event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events, c->iev.handler, c); 180842565f2Syasuoka event_add(&c->iev.ev, NULL); 181842565f2Syasuoka 182842565f2Syasuoka TAILQ_INSERT_TAIL(&ctl_conns, c, entry); 183842565f2Syasuoka } 184842565f2Syasuoka 185842565f2Syasuoka struct ctl_conn * 186842565f2Syasuoka control_connbyfd(int fd) 187842565f2Syasuoka { 188842565f2Syasuoka struct ctl_conn *c; 189842565f2Syasuoka 190842565f2Syasuoka TAILQ_FOREACH(c, &ctl_conns, entry) { 191842565f2Syasuoka if (c->iev.ibuf.fd == fd) 192842565f2Syasuoka break; 193842565f2Syasuoka } 194842565f2Syasuoka 195842565f2Syasuoka return (c); 196842565f2Syasuoka } 197842565f2Syasuoka 198842565f2Syasuoka struct ctl_conn * 199842565f2Syasuoka control_connbyid(uint32_t id) 200842565f2Syasuoka { 201842565f2Syasuoka struct ctl_conn *c; 202842565f2Syasuoka 203842565f2Syasuoka TAILQ_FOREACH(c, &ctl_conns, entry) { 204842565f2Syasuoka if (c->id == id) 205842565f2Syasuoka break; 206842565f2Syasuoka } 207842565f2Syasuoka 208842565f2Syasuoka return (c); 209842565f2Syasuoka } 210842565f2Syasuoka 211842565f2Syasuoka void 212842565f2Syasuoka control_close(int fd) 213842565f2Syasuoka { 214842565f2Syasuoka struct ctl_conn *c; 215842565f2Syasuoka 216842565f2Syasuoka if ((c = control_connbyfd(fd)) == NULL) { 217842565f2Syasuoka log_warn("control_close: fd %d: not found", fd); 218842565f2Syasuoka return; 219842565f2Syasuoka } 220842565f2Syasuoka if (c->modulename[0] != '\0') 221842565f2Syasuoka radiusd_imsg_compose_module(radiusd_s, c->modulename, 222842565f2Syasuoka IMSG_RADIUSD_MODULE_CTRL_UNBIND, c->id, -1, -1, NULL, 0); 223842565f2Syasuoka 224842565f2Syasuoka control_connfree(c); 225842565f2Syasuoka } 226842565f2Syasuoka 227842565f2Syasuoka void 228842565f2Syasuoka control_connfree(struct ctl_conn *c) 229842565f2Syasuoka { 2309cbf9e90Sclaudio imsgbuf_clear(&c->iev.ibuf); 231842565f2Syasuoka TAILQ_REMOVE(&ctl_conns, c, entry); 232842565f2Syasuoka 233842565f2Syasuoka event_del(&c->iev.ev); 234842565f2Syasuoka close(c->iev.ibuf.fd); 235842565f2Syasuoka 236842565f2Syasuoka /* Some file descriptors are available again. */ 237842565f2Syasuoka if (evtimer_pending(&control_state.evt, NULL)) { 238842565f2Syasuoka evtimer_del(&control_state.evt); 239842565f2Syasuoka event_add(&control_state.ev, NULL); 240842565f2Syasuoka } 241842565f2Syasuoka 242842565f2Syasuoka free(c); 243842565f2Syasuoka } 244842565f2Syasuoka 245842565f2Syasuoka /* ARGSUSED */ 246842565f2Syasuoka void 247842565f2Syasuoka control_dispatch_imsg(int fd, short event, void *bula) 248842565f2Syasuoka { 249842565f2Syasuoka struct ctl_conn *c; 250842565f2Syasuoka struct imsg imsg; 251842565f2Syasuoka ssize_t n, datalen; 252842565f2Syasuoka char modulename[RADIUSD_MODULE_NAME_LEN + 1], msg[128]; 253842565f2Syasuoka 254842565f2Syasuoka if ((c = control_connbyfd(fd)) == NULL) { 255842565f2Syasuoka log_warn("control_dispatch_imsg: fd %d: not found", fd); 256842565f2Syasuoka return; 257842565f2Syasuoka } 258842565f2Syasuoka 259842565f2Syasuoka if (event & EV_READ) { 2604f3fb1ffSclaudio if (imsgbuf_read(&c->iev.ibuf) != 1) { 261842565f2Syasuoka control_close(fd); 262842565f2Syasuoka return; 263842565f2Syasuoka } 264842565f2Syasuoka } 265842565f2Syasuoka if (event & EV_WRITE) { 266dd7efffeSclaudio if (imsgbuf_write(&c->iev.ibuf) == -1) { 267842565f2Syasuoka control_close(fd); 268842565f2Syasuoka return; 269842565f2Syasuoka } 270842565f2Syasuoka } 271842565f2Syasuoka 272842565f2Syasuoka for (;;) { 273842565f2Syasuoka if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) { 274842565f2Syasuoka control_close(fd); 275842565f2Syasuoka return; 276842565f2Syasuoka } 277842565f2Syasuoka 278842565f2Syasuoka if (n == 0) 279842565f2Syasuoka break; 280842565f2Syasuoka 281842565f2Syasuoka datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 282842565f2Syasuoka switch (imsg.hdr.type) { 283842565f2Syasuoka default: 284842565f2Syasuoka if (imsg.hdr.type >= IMSG_RADIUSD_MODULE_MIN) { 285842565f2Syasuoka if (datalen < RADIUSD_MODULE_NAME_LEN) { 286842565f2Syasuoka log_warnx( "%s: received an invalid " 287842565f2Syasuoka "imsg %d: too small", __func__, 288842565f2Syasuoka imsg.hdr.type); 289842565f2Syasuoka break; 290842565f2Syasuoka } 291842565f2Syasuoka memset(modulename, 0, sizeof(modulename)); 292842565f2Syasuoka memcpy(modulename, imsg.data, 293842565f2Syasuoka RADIUSD_MODULE_NAME_LEN); 294842565f2Syasuoka if (radiusd_imsg_compose_module(radiusd_s, 295842565f2Syasuoka modulename, imsg.hdr.type, c->id, -1, -1, 296842565f2Syasuoka (caddr_t)imsg.data + 297842565f2Syasuoka RADIUSD_MODULE_NAME_LEN, datalen - 298842565f2Syasuoka RADIUSD_MODULE_NAME_LEN) != 0) { 299842565f2Syasuoka snprintf(msg, sizeof(msg), 300842565f2Syasuoka "module `%s' is not loaded or not " 301842565f2Syasuoka "capable for control command", 302842565f2Syasuoka modulename); 303842565f2Syasuoka imsg_compose_event(&c->iev, 304842565f2Syasuoka IMSG_NG, c->id, -1, -1, msg, 305842565f2Syasuoka strlen(msg) + 1); 306842565f2Syasuoka } 307842565f2Syasuoka } else 308842565f2Syasuoka log_debug("control_dispatch_imsg: " 309842565f2Syasuoka "error handling imsg %d", imsg.hdr.type); 310842565f2Syasuoka break; 311842565f2Syasuoka } 312842565f2Syasuoka imsg_free(&imsg); 313842565f2Syasuoka } 314842565f2Syasuoka imsg_event_add(&c->iev); 315842565f2Syasuoka } 316842565f2Syasuoka 317842565f2Syasuoka int 318842565f2Syasuoka control_imsg_relay(struct imsg *imsg) 319842565f2Syasuoka { 320842565f2Syasuoka struct ctl_conn *c; 321842565f2Syasuoka 322842565f2Syasuoka if ((c = control_connbyid(imsg->hdr.peerid)) == NULL) 323842565f2Syasuoka return (0); 324842565f2Syasuoka 325842565f2Syasuoka return (imsg_compose_event(&c->iev, imsg->hdr.type, 0, imsg->hdr.pid, 326842565f2Syasuoka -1, imsg->data, imsg->hdr.len - IMSG_HEADER_SIZE)); 327842565f2Syasuoka } 328842565f2Syasuoka 329842565f2Syasuoka void 330842565f2Syasuoka control_conn_bind(uint32_t peerid, const char *modulename) 331842565f2Syasuoka { 332842565f2Syasuoka struct ctl_conn *c; 333842565f2Syasuoka 334842565f2Syasuoka if ((c = control_connbyid(peerid)) == NULL) 335842565f2Syasuoka return; 336842565f2Syasuoka 337842565f2Syasuoka if (c->modulename[0] != '\0') 338842565f2Syasuoka radiusd_imsg_compose_module(radiusd_s, c->modulename, 339842565f2Syasuoka IMSG_RADIUSD_MODULE_CTRL_UNBIND, c->id, -1, -1, NULL, 0); 340842565f2Syasuoka strlcpy(c->modulename, modulename, sizeof(c->modulename)); 341842565f2Syasuoka } 342