1*882428cdSclaudio /* $OpenBSD: radiusd_module.c,v 1.26 2024/11/21 13:43:10 claudio Exp $ */ 2a7ca44b8Syasuoka 3a7ca44b8Syasuoka /* 4a7ca44b8Syasuoka * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> 5a7ca44b8Syasuoka * 6a7ca44b8Syasuoka * Permission to use, copy, modify, and distribute this software for any 7a7ca44b8Syasuoka * purpose with or without fee is hereby granted, provided that the above 8a7ca44b8Syasuoka * copyright notice and this permission notice appear in all copies. 9a7ca44b8Syasuoka * 10a7ca44b8Syasuoka * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11a7ca44b8Syasuoka * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12a7ca44b8Syasuoka * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13a7ca44b8Syasuoka * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14a7ca44b8Syasuoka * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15a7ca44b8Syasuoka * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16a7ca44b8Syasuoka * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17a7ca44b8Syasuoka */ 18a7ca44b8Syasuoka 19a7ca44b8Syasuoka /* radiusd_module.c -- helper functions for radiusd modules */ 20a7ca44b8Syasuoka 21a7ca44b8Syasuoka #include <sys/types.h> 22a7ca44b8Syasuoka #include <sys/queue.h> 23a7ca44b8Syasuoka #include <sys/uio.h> 24a7ca44b8Syasuoka 25a7ca44b8Syasuoka #include <err.h> 26a7ca44b8Syasuoka #include <errno.h> 27a7ca44b8Syasuoka #include <event.h> 28a7ca44b8Syasuoka #include <fcntl.h> 29a7ca44b8Syasuoka #include <imsg.h> 30a7ca44b8Syasuoka #include <stdio.h> 31a7ca44b8Syasuoka #include <stdlib.h> 32a7ca44b8Syasuoka #include <string.h> 33a7ca44b8Syasuoka #include <syslog.h> 34a7ca44b8Syasuoka #include <unistd.h> 353d3cf35cSyasuoka #include <pwd.h> 36a7ca44b8Syasuoka 37a7ca44b8Syasuoka #include "radiusd.h" 38a7ca44b8Syasuoka #include "radiusd_module.h" 39a7ca44b8Syasuoka #include "imsg_subr.h" 40a7ca44b8Syasuoka 41a7ca44b8Syasuoka static void (*module_config_set) (void *, const char *, int, 42a7ca44b8Syasuoka char * const *) = NULL; 43a7ca44b8Syasuoka static void (*module_start_module) (void *) = NULL; 44a7ca44b8Syasuoka static void (*module_stop_module) (void *) = NULL; 45a7ca44b8Syasuoka static void (*module_userpass) (void *, u_int, const char *, const char *) 46a7ca44b8Syasuoka = NULL; 47a7ca44b8Syasuoka static void (*module_access_request) (void *, u_int, const u_char *, 48a7ca44b8Syasuoka size_t) = NULL; 49ed1dc925Syasuoka static void (*module_next_response) (void *, u_int, const u_char *, 50ed1dc925Syasuoka size_t) = NULL; 51237e61d9Syasuoka static void (*module_request_decoration) (void *, u_int, const u_char *, 52237e61d9Syasuoka size_t) = NULL; 53237e61d9Syasuoka static void (*module_response_decoration) (void *, u_int, const u_char *, 5476e157acSyasuoka size_t, const u_char *, size_t) = NULL; 55747da5e9Syasuoka static void (*module_accounting_request) (void *, u_int, const u_char *, 56747da5e9Syasuoka size_t) = NULL; 57842565f2Syasuoka static void (*module_dispatch_control) (void *, struct imsg *) = NULL; 58a7ca44b8Syasuoka 59a7ca44b8Syasuoka struct module_base { 60a7ca44b8Syasuoka void *ctx; 61a7ca44b8Syasuoka struct imsgbuf ibuf; 623d3cf35cSyasuoka bool priv_dropped; 63a7ca44b8Syasuoka 64a7ca44b8Syasuoka /* Buffer for receiving the RADIUS packet */ 65a7ca44b8Syasuoka u_char *radpkt; 66a7ca44b8Syasuoka int radpktsiz; 67a7ca44b8Syasuoka int radpktoff; 6876e157acSyasuoka u_char *radpkt2; 6976e157acSyasuoka int radpkt2siz; /* allocated size */ 7076e157acSyasuoka int radpkt2len; /* actual size */ 71a7ca44b8Syasuoka 72a7ca44b8Syasuoka #ifdef USE_LIBEVENT 73a7ca44b8Syasuoka struct module_imsgbuf *module_imsgbuf; 74a7ca44b8Syasuoka bool writeready; 754655f5f2Syasuoka bool stopped; 76a7ca44b8Syasuoka bool ev_onhandler; 77a7ca44b8Syasuoka struct event ev; 78a7ca44b8Syasuoka #endif 79a7ca44b8Syasuoka }; 80a7ca44b8Syasuoka 81a7ca44b8Syasuoka static int module_common_radpkt(struct module_base *, uint32_t, u_int, 82d7548b59Syasuoka const u_char *, size_t); 83a7ca44b8Syasuoka static int module_recv_imsg(struct module_base *); 84a7ca44b8Syasuoka static int module_imsg_handler(struct module_base *, struct imsg *); 85a7ca44b8Syasuoka #ifdef USE_LIBEVENT 86a7ca44b8Syasuoka static void module_on_event(int, short, void *); 87a7ca44b8Syasuoka #endif 88a7ca44b8Syasuoka static void module_reset_event(struct module_base *); 89a7ca44b8Syasuoka 90a7ca44b8Syasuoka struct module_base * 91a7ca44b8Syasuoka module_create(int sock, void *ctx, struct module_handlers *handler) 92a7ca44b8Syasuoka { 93a7ca44b8Syasuoka struct module_base *base; 94a7ca44b8Syasuoka 95a7ca44b8Syasuoka if ((base = calloc(1, sizeof(struct module_base))) == NULL) 96a7ca44b8Syasuoka return (NULL); 97a7ca44b8Syasuoka 98*882428cdSclaudio if (imsgbuf_init(&base->ibuf, sock) == -1) { 99*882428cdSclaudio free(base); 100*882428cdSclaudio return (NULL); 101*882428cdSclaudio } 102a7ca44b8Syasuoka base->ctx = ctx; 103a7ca44b8Syasuoka 104a7ca44b8Syasuoka module_userpass = handler->userpass; 105a7ca44b8Syasuoka module_access_request = handler->access_request; 106ed1dc925Syasuoka module_next_response = handler->next_response; 107a7ca44b8Syasuoka module_config_set = handler->config_set; 108237e61d9Syasuoka module_request_decoration = handler->request_decoration; 109237e61d9Syasuoka module_response_decoration = handler->response_decoration; 110747da5e9Syasuoka module_accounting_request = handler->accounting_request; 111a7ca44b8Syasuoka module_start_module = handler->start; 112a7ca44b8Syasuoka module_stop_module = handler->stop; 113842565f2Syasuoka module_dispatch_control = handler->dispatch_control; 114a7ca44b8Syasuoka 115a7ca44b8Syasuoka return (base); 116a7ca44b8Syasuoka } 117a7ca44b8Syasuoka 118a7ca44b8Syasuoka void 119a7ca44b8Syasuoka module_start(struct module_base *base) 120a7ca44b8Syasuoka { 121a7ca44b8Syasuoka #ifdef USE_LIBEVENT 1221df00c46Syasuoka int ival; 123a7ca44b8Syasuoka 124df69c215Sderaadt if ((ival = fcntl(base->ibuf.fd, F_GETFL)) == -1) 1251df00c46Syasuoka err(1, "Failed to F_GETFL"); 126df69c215Sderaadt if (fcntl(base->ibuf.fd, F_SETFL, ival | O_NONBLOCK) == -1) 127a7ca44b8Syasuoka err(1, "Failed to setup NONBLOCK"); 1284655f5f2Syasuoka event_set(&base->ev, base->ibuf.fd, EV_READ, module_on_event, base); 1294655f5f2Syasuoka event_add(&base->ev, NULL); 130a7ca44b8Syasuoka #endif 131a7ca44b8Syasuoka } 132a7ca44b8Syasuoka 133a7ca44b8Syasuoka int 134a7ca44b8Syasuoka module_run(struct module_base *base) 135a7ca44b8Syasuoka { 136a7ca44b8Syasuoka int ret; 137a7ca44b8Syasuoka 138a7ca44b8Syasuoka ret = module_recv_imsg(base); 139a7ca44b8Syasuoka if (ret == 0) 140dd7efffeSclaudio imsgbuf_flush(&base->ibuf); 141a7ca44b8Syasuoka 142a7ca44b8Syasuoka return (ret); 143a7ca44b8Syasuoka } 144a7ca44b8Syasuoka 145a7ca44b8Syasuoka void 146a7ca44b8Syasuoka module_destroy(struct module_base *base) 147a7ca44b8Syasuoka { 14876e157acSyasuoka if (base != NULL) { 14976e157acSyasuoka free(base->radpkt); 15076e157acSyasuoka free(base->radpkt2); 151dd7efffeSclaudio imsgbuf_clear(&base->ibuf); 15276e157acSyasuoka } 153a7ca44b8Syasuoka free(base); 154a7ca44b8Syasuoka } 155a7ca44b8Syasuoka 156a7ca44b8Syasuoka void 157a7ca44b8Syasuoka module_load(struct module_base *base) 158a7ca44b8Syasuoka { 159a7ca44b8Syasuoka struct radiusd_module_load_arg load; 160a7ca44b8Syasuoka 161a7ca44b8Syasuoka memset(&load, 0, sizeof(load)); 162a7ca44b8Syasuoka if (module_userpass != NULL) 163a7ca44b8Syasuoka load.cap |= RADIUSD_MODULE_CAP_USERPASS; 164a7ca44b8Syasuoka if (module_access_request != NULL) 165a7ca44b8Syasuoka load.cap |= RADIUSD_MODULE_CAP_ACCSREQ; 166ed1dc925Syasuoka if (module_next_response != NULL) 167ed1dc925Syasuoka load.cap |= RADIUSD_MODULE_CAP_NEXTRES; 168237e61d9Syasuoka if (module_request_decoration != NULL) 169237e61d9Syasuoka load.cap |= RADIUSD_MODULE_CAP_REQDECO; 170237e61d9Syasuoka if (module_response_decoration != NULL) 171237e61d9Syasuoka load.cap |= RADIUSD_MODULE_CAP_RESDECO; 172747da5e9Syasuoka if (module_accounting_request != NULL) 173747da5e9Syasuoka load.cap |= RADIUSD_MODULE_CAP_ACCTREQ; 174842565f2Syasuoka if (module_dispatch_control != NULL) 175842565f2Syasuoka load.cap |= RADIUSD_MODULE_CAP_CONTROL; 176a7ca44b8Syasuoka imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_LOAD, 0, 0, -1, &load, 177a7ca44b8Syasuoka sizeof(load)); 178dd7efffeSclaudio imsgbuf_flush(&base->ibuf); 179a7ca44b8Syasuoka } 180a7ca44b8Syasuoka 1813d3cf35cSyasuoka void 182edd79a0eSyasuoka module_drop_privilege(struct module_base *base, int nochroot) 1833d3cf35cSyasuoka { 1843d3cf35cSyasuoka struct passwd *pw; 1853d3cf35cSyasuoka 18661125a90Syasuoka tzset(); 18761125a90Syasuoka 1883d3cf35cSyasuoka /* Drop the privilege */ 1893d3cf35cSyasuoka if ((pw = getpwnam(RADIUSD_USER)) == NULL) 1903d3cf35cSyasuoka goto on_fail; 191edd79a0eSyasuoka if (nochroot == 0 && chroot(pw->pw_dir) == -1) 1923d3cf35cSyasuoka goto on_fail; 1933d3cf35cSyasuoka if (chdir("/") == -1) 1943d3cf35cSyasuoka goto on_fail; 1953d3cf35cSyasuoka if (setgroups(1, &pw->pw_gid) || 1963d3cf35cSyasuoka setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 1973d3cf35cSyasuoka setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 1983d3cf35cSyasuoka goto on_fail; 1993d3cf35cSyasuoka base->priv_dropped = true; 2003d3cf35cSyasuoka 2013d3cf35cSyasuoka on_fail: 2023d3cf35cSyasuoka return; 2033d3cf35cSyasuoka } 2043d3cf35cSyasuoka 205a7ca44b8Syasuoka int 206a7ca44b8Syasuoka module_notify_secret(struct module_base *base, const char *secret) 207a7ca44b8Syasuoka { 208a7ca44b8Syasuoka int ret; 209a7ca44b8Syasuoka 210a7ca44b8Syasuoka ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_NOTIFY_SECRET, 211a7ca44b8Syasuoka 0, 0, -1, secret, strlen(secret) + 1); 212a7ca44b8Syasuoka module_reset_event(base); 213a7ca44b8Syasuoka 214a7ca44b8Syasuoka return (ret); 215a7ca44b8Syasuoka } 216a7ca44b8Syasuoka 217a7ca44b8Syasuoka int 218a7ca44b8Syasuoka module_send_message(struct module_base *base, uint32_t cmd, const char *fmt, 219a7ca44b8Syasuoka ...) 220a7ca44b8Syasuoka { 221a7ca44b8Syasuoka char *msg; 222a7ca44b8Syasuoka va_list ap; 223a7ca44b8Syasuoka int ret; 224a7ca44b8Syasuoka 225a7ca44b8Syasuoka if (fmt == NULL) 226a7ca44b8Syasuoka ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, NULL, 0); 227a7ca44b8Syasuoka else { 228a7ca44b8Syasuoka va_start(ap, fmt); 229a7ca44b8Syasuoka vasprintf(&msg, fmt, ap); 230a7ca44b8Syasuoka va_end(ap); 231a7ca44b8Syasuoka if (msg == NULL) 232a7ca44b8Syasuoka return (-1); 233a7ca44b8Syasuoka ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, msg, 234a7ca44b8Syasuoka strlen(msg) + 1); 235a7ca44b8Syasuoka free(msg); 236a7ca44b8Syasuoka } 237a7ca44b8Syasuoka module_reset_event(base); 238a7ca44b8Syasuoka 239a7ca44b8Syasuoka return (ret); 240a7ca44b8Syasuoka } 241a7ca44b8Syasuoka 242a7ca44b8Syasuoka int 243a7ca44b8Syasuoka module_userpass_ok(struct module_base *base, u_int q_id, const char *msg) 244a7ca44b8Syasuoka { 245a7ca44b8Syasuoka int ret; 246a7ca44b8Syasuoka struct iovec iov[2]; 247a7ca44b8Syasuoka 248a7ca44b8Syasuoka iov[0].iov_base = &q_id; 249a7ca44b8Syasuoka iov[0].iov_len = sizeof(q_id); 250a7ca44b8Syasuoka iov[1].iov_base = (char *)msg; 251a7ca44b8Syasuoka iov[1].iov_len = strlen(msg) + 1; 252a7ca44b8Syasuoka ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_OK, 253a7ca44b8Syasuoka 0, 0, -1, iov, 2); 254a7ca44b8Syasuoka module_reset_event(base); 255a7ca44b8Syasuoka 256a7ca44b8Syasuoka return (ret); 257a7ca44b8Syasuoka } 258a7ca44b8Syasuoka 259a7ca44b8Syasuoka int 260a7ca44b8Syasuoka module_userpass_fail(struct module_base *base, u_int q_id, const char *msg) 261a7ca44b8Syasuoka { 262a7ca44b8Syasuoka int ret; 263a7ca44b8Syasuoka struct iovec iov[2]; 264a7ca44b8Syasuoka 265a7ca44b8Syasuoka iov[0].iov_base = &q_id; 266a7ca44b8Syasuoka iov[0].iov_len = sizeof(q_id); 267a7ca44b8Syasuoka iov[1].iov_base = (char *)msg; 268a7ca44b8Syasuoka iov[1].iov_len = strlen(msg) + 1; 269a7ca44b8Syasuoka ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_FAIL, 270a7ca44b8Syasuoka 0, 0, -1, iov, 2); 271a7ca44b8Syasuoka module_reset_event(base); 272a7ca44b8Syasuoka 273a7ca44b8Syasuoka return (ret); 274a7ca44b8Syasuoka } 275a7ca44b8Syasuoka 276a7ca44b8Syasuoka int 277d7548b59Syasuoka module_accsreq_answer(struct module_base *base, u_int q_id, const u_char *pkt, 278d7548b59Syasuoka size_t pktlen) 279a7ca44b8Syasuoka { 280a7ca44b8Syasuoka return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER, 281d7548b59Syasuoka q_id, pkt, pktlen)); 282a7ca44b8Syasuoka } 283a7ca44b8Syasuoka 284a7ca44b8Syasuoka int 285ed1dc925Syasuoka module_accsreq_next(struct module_base *base, u_int q_id, const u_char *pkt, 286ed1dc925Syasuoka size_t pktlen) 287ed1dc925Syasuoka { 288ed1dc925Syasuoka return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_NEXT, 289ed1dc925Syasuoka q_id, pkt, pktlen)); 290ed1dc925Syasuoka } 291ed1dc925Syasuoka 292ed1dc925Syasuoka int 293a7ca44b8Syasuoka module_accsreq_aborted(struct module_base *base, u_int q_id) 294a7ca44b8Syasuoka { 295a7ca44b8Syasuoka int ret; 296a7ca44b8Syasuoka 297a7ca44b8Syasuoka ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED, 298a7ca44b8Syasuoka 0, 0, -1, &q_id, sizeof(u_int)); 299a7ca44b8Syasuoka module_reset_event(base); 300a7ca44b8Syasuoka 301a7ca44b8Syasuoka return (ret); 302a7ca44b8Syasuoka } 303a7ca44b8Syasuoka 304237e61d9Syasuoka int 305237e61d9Syasuoka module_reqdeco_done(struct module_base *base, u_int q_id, const u_char *pkt, 306237e61d9Syasuoka size_t pktlen) 307237e61d9Syasuoka { 308237e61d9Syasuoka return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_REQDECO_DONE, 309237e61d9Syasuoka q_id, pkt, pktlen)); 310237e61d9Syasuoka } 311237e61d9Syasuoka 312237e61d9Syasuoka int 313237e61d9Syasuoka module_resdeco_done(struct module_base *base, u_int q_id, const u_char *pkt, 314237e61d9Syasuoka size_t pktlen) 315237e61d9Syasuoka { 316237e61d9Syasuoka return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_RESDECO_DONE, 317237e61d9Syasuoka q_id, pkt, pktlen)); 318237e61d9Syasuoka } 319237e61d9Syasuoka 320a7ca44b8Syasuoka static int 321a7ca44b8Syasuoka module_common_radpkt(struct module_base *base, uint32_t imsg_type, u_int q_id, 322d7548b59Syasuoka const u_char *pkt, size_t pktlen) 323a7ca44b8Syasuoka { 324a7ca44b8Syasuoka int ret = 0, off = 0, len, siz; 325a7ca44b8Syasuoka struct iovec iov[2]; 326a7ca44b8Syasuoka struct radiusd_module_radpkt_arg ans; 327a7ca44b8Syasuoka 328a7ca44b8Syasuoka len = pktlen; 329a7ca44b8Syasuoka ans.q_id = q_id; 3303f8258c8Syasuoka ans.pktlen = pktlen; 3313f8258c8Syasuoka ans.final = false; 332237e61d9Syasuoka 333237e61d9Syasuoka while (!ans.final) { 334237e61d9Syasuoka siz = MAX_IMSGSIZE - sizeof(ans); 335237e61d9Syasuoka if (len - off <= siz) { 336a7ca44b8Syasuoka ans.final = true; 3373f8258c8Syasuoka siz = len - off; 338a7ca44b8Syasuoka } 339a7ca44b8Syasuoka iov[0].iov_base = &ans; 340a7ca44b8Syasuoka iov[0].iov_len = sizeof(ans); 341237e61d9Syasuoka if (siz > 0) { 342a7ca44b8Syasuoka iov[1].iov_base = (u_char *)pkt + off; 3433f8258c8Syasuoka iov[1].iov_len = siz; 344237e61d9Syasuoka } 345237e61d9Syasuoka ret = imsg_composev(&base->ibuf, imsg_type, 0, 0, -1, iov, 346237e61d9Syasuoka (siz > 0)? 2 : 1); 347a7ca44b8Syasuoka if (ret == -1) 348a7ca44b8Syasuoka break; 3493f8258c8Syasuoka off += siz; 350a7ca44b8Syasuoka } 351a7ca44b8Syasuoka module_reset_event(base); 352a7ca44b8Syasuoka 353a7ca44b8Syasuoka return (ret); 354a7ca44b8Syasuoka } 355a7ca44b8Syasuoka 356a7ca44b8Syasuoka static int 357a7ca44b8Syasuoka module_recv_imsg(struct module_base *base) 358a7ca44b8Syasuoka { 359a7ca44b8Syasuoka ssize_t n; 360a7ca44b8Syasuoka struct imsg imsg; 361a7ca44b8Syasuoka 3624f3fb1ffSclaudio if ((n = imsgbuf_read(&base->ibuf)) != 1) { 3634f3fb1ffSclaudio if (n == -1) 364dd7efffeSclaudio syslog(LOG_ERR, "%s: imsgbuf_read(): %m", __func__); 3654655f5f2Syasuoka module_stop(base); 366a7ca44b8Syasuoka return (-1); 367a7ca44b8Syasuoka } 368a7ca44b8Syasuoka for (;;) { 3694655f5f2Syasuoka if ((n = imsg_get(&base->ibuf, &imsg)) == -1) { 3704655f5f2Syasuoka syslog(LOG_ERR, "%s: imsg_get(): %m", __func__); 3714655f5f2Syasuoka module_stop(base); 372a7ca44b8Syasuoka return (-1); 3734655f5f2Syasuoka } 374a7ca44b8Syasuoka if (n == 0) 375a7ca44b8Syasuoka break; 376a7ca44b8Syasuoka module_imsg_handler(base, &imsg); 377a7ca44b8Syasuoka imsg_free(&imsg); 378a7ca44b8Syasuoka } 379a7ca44b8Syasuoka module_reset_event(base); 380a7ca44b8Syasuoka 381a7ca44b8Syasuoka return (0); 382a7ca44b8Syasuoka } 383a7ca44b8Syasuoka 384a7ca44b8Syasuoka static int 385a7ca44b8Syasuoka module_imsg_handler(struct module_base *base, struct imsg *imsg) 386a7ca44b8Syasuoka { 387a7ca44b8Syasuoka ssize_t datalen; 388a7ca44b8Syasuoka 389a7ca44b8Syasuoka datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 390a7ca44b8Syasuoka switch (imsg->hdr.type) { 391a7ca44b8Syasuoka case IMSG_RADIUSD_MODULE_SET_CONFIG: 392a7ca44b8Syasuoka { 393a7ca44b8Syasuoka struct radiusd_module_set_arg *arg; 394a7ca44b8Syasuoka struct radiusd_module_object *val; 395a7ca44b8Syasuoka u_int i; 396a7ca44b8Syasuoka size_t off; 397a7ca44b8Syasuoka char **argv; 398a7ca44b8Syasuoka 399a7ca44b8Syasuoka arg = (struct radiusd_module_set_arg *)imsg->data; 400a7ca44b8Syasuoka off = sizeof(struct radiusd_module_set_arg); 401a7ca44b8Syasuoka 402a7ca44b8Syasuoka if ((argv = calloc(sizeof(const char *), arg->nparamval)) 403a7ca44b8Syasuoka == NULL) { 404a7ca44b8Syasuoka module_send_message(base, IMSG_NG, 405a7ca44b8Syasuoka "Out of memory: %s", strerror(errno)); 406a7ca44b8Syasuoka break; 407a7ca44b8Syasuoka } 408a7ca44b8Syasuoka for (i = 0; i < arg->nparamval; i++) { 409a7ca44b8Syasuoka if (datalen - off < 410a7ca44b8Syasuoka sizeof(struct radiusd_module_object)) 411a7ca44b8Syasuoka break; 412a7ca44b8Syasuoka val = (struct radiusd_module_object *) 413a7ca44b8Syasuoka ((caddr_t)imsg->data + off); 414a7ca44b8Syasuoka if (datalen - off < val->size) 415a7ca44b8Syasuoka break; 416a7ca44b8Syasuoka argv[i] = (char *)(val + 1); 417a7ca44b8Syasuoka off += val->size; 418a7ca44b8Syasuoka } 419a7ca44b8Syasuoka if (i >= arg->nparamval) 420a7ca44b8Syasuoka module_config_set(base->ctx, arg->paramname, 421a7ca44b8Syasuoka arg->nparamval, argv); 422a7ca44b8Syasuoka else 423a7ca44b8Syasuoka module_send_message(base, IMSG_NG, 424a7ca44b8Syasuoka "Internal protocol error"); 425a7ca44b8Syasuoka free(argv); 426a7ca44b8Syasuoka 427a7ca44b8Syasuoka break; 428a7ca44b8Syasuoka } 429a7ca44b8Syasuoka case IMSG_RADIUSD_MODULE_START: 4303d3cf35cSyasuoka if (module_start_module != NULL) { 431a7ca44b8Syasuoka module_start_module(base->ctx); 4323d3cf35cSyasuoka if (!base->priv_dropped) { 4333d3cf35cSyasuoka syslog(LOG_ERR, "Module tried to start with " 434fc72e2f5Smmcc "root privileges"); 4353d3cf35cSyasuoka abort(); 4363d3cf35cSyasuoka } 4373d3cf35cSyasuoka } else { 4383d3cf35cSyasuoka if (!base->priv_dropped) { 4393d3cf35cSyasuoka syslog(LOG_ERR, "Module tried to start with " 440fc72e2f5Smmcc "root privileges"); 4413d3cf35cSyasuoka abort(); 4423d3cf35cSyasuoka } 443a7ca44b8Syasuoka module_send_message(base, IMSG_OK, NULL); 4443d3cf35cSyasuoka } 445a7ca44b8Syasuoka break; 446a7ca44b8Syasuoka case IMSG_RADIUSD_MODULE_STOP: 4474655f5f2Syasuoka module_stop(base); 448a7ca44b8Syasuoka break; 449a7ca44b8Syasuoka case IMSG_RADIUSD_MODULE_USERPASS: 450a7ca44b8Syasuoka { 451a7ca44b8Syasuoka struct radiusd_module_userpass_arg *userpass; 452a7ca44b8Syasuoka 453a7ca44b8Syasuoka if (module_userpass == NULL) { 454a7ca44b8Syasuoka syslog(LOG_ERR, "Received USERPASS message, but " 455a7ca44b8Syasuoka "module doesn't support"); 456a7ca44b8Syasuoka break; 457a7ca44b8Syasuoka } 458a7ca44b8Syasuoka if (datalen < 459a7ca44b8Syasuoka (ssize_t)sizeof(struct radiusd_module_userpass_arg)) { 460a7ca44b8Syasuoka syslog(LOG_ERR, "Received USERPASS message, but " 461a7ca44b8Syasuoka "length is wrong"); 462a7ca44b8Syasuoka break; 463a7ca44b8Syasuoka } 464a7ca44b8Syasuoka userpass = (struct radiusd_module_userpass_arg *)imsg->data; 465a7ca44b8Syasuoka module_userpass(base->ctx, userpass->q_id, userpass->user, 466a7ca44b8Syasuoka (userpass->has_pass)? userpass->pass : NULL); 467a7ca44b8Syasuoka explicit_bzero(userpass, 468a7ca44b8Syasuoka sizeof(struct radiusd_module_userpass_arg)); 469a7ca44b8Syasuoka break; 470a7ca44b8Syasuoka } 471a7ca44b8Syasuoka case IMSG_RADIUSD_MODULE_ACCSREQ: 472ed1dc925Syasuoka case IMSG_RADIUSD_MODULE_NEXTRES: 473237e61d9Syasuoka case IMSG_RADIUSD_MODULE_REQDECO: 47476e157acSyasuoka case IMSG_RADIUSD_MODULE_RESDECO0_REQ: 475237e61d9Syasuoka case IMSG_RADIUSD_MODULE_RESDECO: 476747da5e9Syasuoka case IMSG_RADIUSD_MODULE_ACCTREQ: 477a7ca44b8Syasuoka { 478a7ca44b8Syasuoka struct radiusd_module_radpkt_arg *accessreq; 479a7ca44b8Syasuoka int chunklen; 480237e61d9Syasuoka const char *typestr; 481a7ca44b8Syasuoka 482237e61d9Syasuoka if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ) { 483a7ca44b8Syasuoka if (module_access_request == NULL) { 484a7ca44b8Syasuoka syslog(LOG_ERR, "Received ACCSREQ message, but " 485a7ca44b8Syasuoka "module doesn't support"); 486a7ca44b8Syasuoka break; 487a7ca44b8Syasuoka } 488237e61d9Syasuoka typestr = "ACCSREQ"; 489ed1dc925Syasuoka } else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_NEXTRES) { 490ed1dc925Syasuoka if (module_next_response == NULL) { 491ed1dc925Syasuoka syslog(LOG_ERR, "Received NEXTRES message, but " 492ed1dc925Syasuoka "module doesn't support"); 493ed1dc925Syasuoka break; 494ed1dc925Syasuoka } 495ed1dc925Syasuoka typestr = "NEXTRES"; 496747da5e9Syasuoka } else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCTREQ) { 497747da5e9Syasuoka if (module_accounting_request == NULL) { 498747da5e9Syasuoka syslog(LOG_ERR, "Received ACCTREQ message, but " 499747da5e9Syasuoka "module doesn't support"); 500747da5e9Syasuoka break; 501747da5e9Syasuoka } 502747da5e9Syasuoka typestr = "ACCTREQ"; 503237e61d9Syasuoka } else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) { 504237e61d9Syasuoka if (module_request_decoration == NULL) { 505237e61d9Syasuoka syslog(LOG_ERR, "Received REQDECO message, but " 506237e61d9Syasuoka "module doesn't support"); 507237e61d9Syasuoka break; 508237e61d9Syasuoka } 509237e61d9Syasuoka typestr = "REQDECO"; 510237e61d9Syasuoka } else { 511237e61d9Syasuoka if (module_response_decoration == NULL) { 512237e61d9Syasuoka syslog(LOG_ERR, "Received RESDECO message, but " 513237e61d9Syasuoka "module doesn't support"); 514237e61d9Syasuoka break; 515237e61d9Syasuoka } 51676e157acSyasuoka if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO0_REQ) 51776e157acSyasuoka typestr = "RESDECO0_REQ"; 51876e157acSyasuoka else 519237e61d9Syasuoka typestr = "RESDECO"; 520237e61d9Syasuoka } 521237e61d9Syasuoka 522a7ca44b8Syasuoka if (datalen < 523a7ca44b8Syasuoka (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) { 524237e61d9Syasuoka syslog(LOG_ERR, "Received %s message, but " 525237e61d9Syasuoka "length is wrong", typestr); 526a7ca44b8Syasuoka break; 527a7ca44b8Syasuoka } 528a7ca44b8Syasuoka accessreq = (struct radiusd_module_radpkt_arg *)imsg->data; 5293f8258c8Syasuoka if (base->radpktsiz < accessreq->pktlen) { 530a7ca44b8Syasuoka u_char *nradpkt; 531a7ca44b8Syasuoka if ((nradpkt = realloc(base->radpkt, 5323f8258c8Syasuoka accessreq->pktlen)) == NULL) { 533a7ca44b8Syasuoka syslog(LOG_ERR, "Could not handle received " 534237e61d9Syasuoka "%s message: %m", typestr); 535a7ca44b8Syasuoka base->radpktoff = 0; 536a7ca44b8Syasuoka goto accsreq_out; 537a7ca44b8Syasuoka } 538a7ca44b8Syasuoka base->radpkt = nradpkt; 5393f8258c8Syasuoka base->radpktsiz = accessreq->pktlen; 540a7ca44b8Syasuoka } 541a7ca44b8Syasuoka chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg); 542a7ca44b8Syasuoka if (chunklen > base->radpktsiz - base->radpktoff){ 543a7ca44b8Syasuoka syslog(LOG_ERR, 544237e61d9Syasuoka "Could not handle received %s message: " 545237e61d9Syasuoka "received length is too big", typestr); 546a7ca44b8Syasuoka base->radpktoff = 0; 547a7ca44b8Syasuoka goto accsreq_out; 548a7ca44b8Syasuoka } 549a7ca44b8Syasuoka memcpy(base->radpkt + base->radpktoff, 550a7ca44b8Syasuoka (caddr_t)(accessreq + 1), chunklen); 551a7ca44b8Syasuoka base->radpktoff += chunklen; 552a7ca44b8Syasuoka if (!accessreq->final) 553a7ca44b8Syasuoka goto accsreq_out; 5543f8258c8Syasuoka if (base->radpktoff != accessreq->pktlen) { 555a7ca44b8Syasuoka syslog(LOG_ERR, 556237e61d9Syasuoka "Could not handle received %s " 557237e61d9Syasuoka "message: length is mismatch", typestr); 558a7ca44b8Syasuoka base->radpktoff = 0; 559a7ca44b8Syasuoka goto accsreq_out; 560a7ca44b8Syasuoka } 561237e61d9Syasuoka if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ) 562a7ca44b8Syasuoka module_access_request(base->ctx, accessreq->q_id, 563a7ca44b8Syasuoka base->radpkt, base->radpktoff); 564ed1dc925Syasuoka else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_NEXTRES) 565ed1dc925Syasuoka module_next_response(base->ctx, accessreq->q_id, 566ed1dc925Syasuoka base->radpkt, base->radpktoff); 567237e61d9Syasuoka else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) 568237e61d9Syasuoka module_request_decoration(base->ctx, accessreq->q_id, 569237e61d9Syasuoka base->radpkt, base->radpktoff); 57076e157acSyasuoka else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO0_REQ) { 57176e157acSyasuoka /* preserve request */ 57276e157acSyasuoka if (base->radpktoff > base->radpkt2siz) { 57376e157acSyasuoka u_char *nradpkt; 57476e157acSyasuoka if ((nradpkt = realloc(base->radpkt2, 57576e157acSyasuoka base->radpktoff)) == NULL) { 57676e157acSyasuoka syslog(LOG_ERR, "Could not handle " 57776e157acSyasuoka "received %s message: %m", typestr); 57876e157acSyasuoka base->radpktoff = 0; 57976e157acSyasuoka goto accsreq_out; 58076e157acSyasuoka } 58176e157acSyasuoka base->radpkt2 = nradpkt; 58276e157acSyasuoka base->radpkt2siz = base->radpktoff; 58376e157acSyasuoka } 58476e157acSyasuoka memcpy(base->radpkt2, base->radpkt, base->radpktoff); 58576e157acSyasuoka base->radpkt2len = base->radpktoff; 586747da5e9Syasuoka } else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO) { 587237e61d9Syasuoka module_response_decoration(base->ctx, accessreq->q_id, 58876e157acSyasuoka base->radpkt2, base->radpkt2len, base->radpkt, 58976e157acSyasuoka base->radpktoff); 59076e157acSyasuoka base->radpkt2len = 0; 591747da5e9Syasuoka } else 592747da5e9Syasuoka module_accounting_request(base->ctx, accessreq->q_id, 593747da5e9Syasuoka base->radpkt, base->radpktoff); 594a7ca44b8Syasuoka base->radpktoff = 0; 595a7ca44b8Syasuoka accsreq_out: 596a7ca44b8Syasuoka break; 597a7ca44b8Syasuoka } 598842565f2Syasuoka case IMSG_RADIUSD_MODULE_CTRL_UNBIND: 599842565f2Syasuoka goto forward_msg; 600842565f2Syasuoka break; 601842565f2Syasuoka default: 602842565f2Syasuoka if (imsg->hdr.type >= IMSG_RADIUSD_MODULE_MIN) { 603842565f2Syasuoka forward_msg: 604842565f2Syasuoka if (module_dispatch_control == NULL) { 605842565f2Syasuoka const char msg[] = 606842565f2Syasuoka "the module doesn't handle any controls"; 607842565f2Syasuoka imsg_compose(&base->ibuf, IMSG_NG, 608842565f2Syasuoka imsg->hdr.peerid, 0, -1, msg, sizeof(msg)); 609842565f2Syasuoka } else 610842565f2Syasuoka module_dispatch_control(base->ctx, imsg); 611842565f2Syasuoka } 612a7ca44b8Syasuoka } 613a7ca44b8Syasuoka 614a7ca44b8Syasuoka return (0); 615a7ca44b8Syasuoka } 616a7ca44b8Syasuoka 617237e61d9Syasuoka void 6184655f5f2Syasuoka module_stop(struct module_base *base) 6194655f5f2Syasuoka { 6204655f5f2Syasuoka if (module_stop_module != NULL) 6214655f5f2Syasuoka module_stop_module(base->ctx); 6224655f5f2Syasuoka #ifdef USE_LIBEVENT 6234655f5f2Syasuoka event_del(&base->ev); 6244655f5f2Syasuoka base->stopped = true; 6254655f5f2Syasuoka #endif 6264655f5f2Syasuoka close(base->ibuf.fd); 6274655f5f2Syasuoka } 6284655f5f2Syasuoka 629a7ca44b8Syasuoka #ifdef USE_LIBEVENT 630a7ca44b8Syasuoka static void 631a7ca44b8Syasuoka module_on_event(int fd, short evmask, void *ctx) 632a7ca44b8Syasuoka { 633a7ca44b8Syasuoka struct module_base *base = ctx; 634a7ca44b8Syasuoka int ret; 635a7ca44b8Syasuoka 636a7ca44b8Syasuoka base->ev_onhandler = true; 637c1aa9554Sclaudio if (evmask & EV_WRITE) { 638a7ca44b8Syasuoka base->writeready = true; 639dd7efffeSclaudio if (imsgbuf_write(&base->ibuf) == -1) { 640dd7efffeSclaudio syslog(LOG_ERR, "%s: imsgbuf_write: %m", __func__); 641c1aa9554Sclaudio module_stop(base); 642c1aa9554Sclaudio return; 643c1aa9554Sclaudio } 644c1aa9554Sclaudio base->writeready = false; 645c1aa9554Sclaudio } 646a7ca44b8Syasuoka if (evmask & EV_READ) { 647a7ca44b8Syasuoka ret = module_recv_imsg(base); 648a7ca44b8Syasuoka if (ret < 0) 6494655f5f2Syasuoka return; 650a7ca44b8Syasuoka } 651a7ca44b8Syasuoka base->ev_onhandler = false; 652a7ca44b8Syasuoka module_reset_event(base); 653a7ca44b8Syasuoka return; 654a7ca44b8Syasuoka } 655a7ca44b8Syasuoka #endif 656a7ca44b8Syasuoka 657a7ca44b8Syasuoka static void 658a7ca44b8Syasuoka module_reset_event(struct module_base *base) 659a7ca44b8Syasuoka { 660a7ca44b8Syasuoka #ifdef USE_LIBEVENT 661a7ca44b8Syasuoka short evmask = 0; 662a7ca44b8Syasuoka struct timeval *tvp = NULL, tv = { 0, 0 }; 663a7ca44b8Syasuoka 664a7ca44b8Syasuoka if (base->ev_onhandler) 665a7ca44b8Syasuoka return; 6664655f5f2Syasuoka if (base->stopped) 6674655f5f2Syasuoka return; 668a7ca44b8Syasuoka event_del(&base->ev); 669a7ca44b8Syasuoka 670a7ca44b8Syasuoka evmask |= EV_READ; 67131be28caSclaudio if (imsgbuf_queuelen(&base->ibuf) > 0) { 672a7ca44b8Syasuoka if (!base->writeready) 673a7ca44b8Syasuoka evmask |= EV_WRITE; 674a7ca44b8Syasuoka else 675a7ca44b8Syasuoka tvp = &tv; /* fire immediately */ 676a7ca44b8Syasuoka } 677a7ca44b8Syasuoka event_set(&base->ev, base->ibuf.fd, evmask, module_on_event, base); 678a7ca44b8Syasuoka if (event_add(&base->ev, tvp) == -1) 679a7ca44b8Syasuoka syslog(LOG_ERR, "event_add() failed in %s()", __func__); 680a7ca44b8Syasuoka #endif 681a7ca44b8Syasuoka } 682842565f2Syasuoka 683842565f2Syasuoka int 684842565f2Syasuoka module_imsg_compose(struct module_base *base, uint32_t type, uint32_t id, 685842565f2Syasuoka pid_t pid, int fd, const void *data, size_t datalen) 686842565f2Syasuoka { 687842565f2Syasuoka int ret; 688842565f2Syasuoka 689842565f2Syasuoka if ((ret = imsg_compose(&base->ibuf, type, id, pid, fd, data, datalen)) 690842565f2Syasuoka != -1) 691842565f2Syasuoka module_reset_event(base); 692842565f2Syasuoka 693842565f2Syasuoka return (ret); 694842565f2Syasuoka } 695842565f2Syasuoka 696842565f2Syasuoka int 697842565f2Syasuoka module_imsg_composev(struct module_base *base, uint32_t type, uint32_t id, 698842565f2Syasuoka pid_t pid, int fd, const struct iovec *iov, int iovcnt) 699842565f2Syasuoka { 700842565f2Syasuoka int ret; 701842565f2Syasuoka 702842565f2Syasuoka if ((ret = imsg_composev(&base->ibuf, type, id, pid, fd, iov, iovcnt)) 703842565f2Syasuoka != -1) 704842565f2Syasuoka module_reset_event(base); 705842565f2Syasuoka 706842565f2Syasuoka return (ret); 707842565f2Syasuoka } 708