1 /* $OpenBSD: radiusd_module.c,v 1.15 2024/01/08 04:16:48 yasuoka Exp $ */ 2 3 /* 4 * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 /* radiusd_module.c -- helper functions for radiusd modules */ 20 21 #include <sys/types.h> 22 #include <sys/queue.h> 23 #include <sys/uio.h> 24 25 #include <err.h> 26 #include <errno.h> 27 #include <event.h> 28 #include <fcntl.h> 29 #include <imsg.h> 30 #include <stdio.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <syslog.h> 34 #include <unistd.h> 35 #include <pwd.h> 36 37 #include "radiusd.h" 38 #include "radiusd_module.h" 39 #include "imsg_subr.h" 40 41 static void (*module_config_set) (void *, const char *, int, 42 char * const *) = NULL; 43 static void (*module_start_module) (void *) = NULL; 44 static void (*module_stop_module) (void *) = NULL; 45 static void (*module_userpass) (void *, u_int, const char *, const char *) 46 = NULL; 47 static void (*module_access_request) (void *, u_int, const u_char *, 48 size_t) = NULL; 49 static void (*module_request_decoration) (void *, u_int, const u_char *, 50 size_t) = NULL; 51 static void (*module_response_decoration) (void *, u_int, const u_char *, 52 size_t, const u_char *, size_t) = NULL; 53 54 struct module_base { 55 void *ctx; 56 struct imsgbuf ibuf; 57 bool priv_dropped; 58 59 /* Buffer for receiving the RADIUS packet */ 60 u_char *radpkt; 61 int radpktsiz; 62 int radpktoff; 63 u_char *radpkt2; 64 int radpkt2siz; /* allocated size */ 65 int radpkt2len; /* actual size */ 66 67 #ifdef USE_LIBEVENT 68 struct module_imsgbuf *module_imsgbuf; 69 bool writeready; 70 bool stopped; 71 bool ev_onhandler; 72 struct event ev; 73 #endif 74 }; 75 76 static int module_common_radpkt(struct module_base *, uint32_t, u_int, 77 const u_char *, size_t); 78 static int module_recv_imsg(struct module_base *); 79 static int module_imsg_handler(struct module_base *, struct imsg *); 80 #ifdef USE_LIBEVENT 81 static void module_on_event(int, short, void *); 82 #endif 83 static void module_reset_event(struct module_base *); 84 85 struct module_base * 86 module_create(int sock, void *ctx, struct module_handlers *handler) 87 { 88 struct module_base *base; 89 90 if ((base = calloc(1, sizeof(struct module_base))) == NULL) 91 return (NULL); 92 93 imsg_init(&base->ibuf, sock); 94 base->ctx = ctx; 95 96 module_userpass = handler->userpass; 97 module_access_request = handler->access_request; 98 module_config_set = handler->config_set; 99 module_request_decoration = handler->request_decoration; 100 module_response_decoration = handler->response_decoration; 101 module_start_module = handler->start; 102 module_stop_module = handler->stop; 103 104 return (base); 105 } 106 107 void 108 module_start(struct module_base *base) 109 { 110 #ifdef USE_LIBEVENT 111 int ival; 112 113 if ((ival = fcntl(base->ibuf.fd, F_GETFL)) == -1) 114 err(1, "Failed to F_GETFL"); 115 if (fcntl(base->ibuf.fd, F_SETFL, ival | O_NONBLOCK) == -1) 116 err(1, "Failed to setup NONBLOCK"); 117 event_set(&base->ev, base->ibuf.fd, EV_READ, module_on_event, base); 118 event_add(&base->ev, NULL); 119 #endif 120 } 121 122 int 123 module_run(struct module_base *base) 124 { 125 int ret; 126 127 ret = module_recv_imsg(base); 128 if (ret == 0) 129 imsg_flush(&base->ibuf); 130 131 return (ret); 132 } 133 134 void 135 module_destroy(struct module_base *base) 136 { 137 if (base != NULL) { 138 free(base->radpkt); 139 free(base->radpkt2); 140 imsg_clear(&base->ibuf); 141 } 142 free(base); 143 } 144 145 void 146 module_load(struct module_base *base) 147 { 148 struct radiusd_module_load_arg load; 149 150 memset(&load, 0, sizeof(load)); 151 if (module_userpass != NULL) 152 load.cap |= RADIUSD_MODULE_CAP_USERPASS; 153 if (module_access_request != NULL) 154 load.cap |= RADIUSD_MODULE_CAP_ACCSREQ; 155 if (module_request_decoration != NULL) 156 load.cap |= RADIUSD_MODULE_CAP_REQDECO; 157 if (module_response_decoration != NULL) 158 load.cap |= RADIUSD_MODULE_CAP_RESDECO; 159 imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_LOAD, 0, 0, -1, &load, 160 sizeof(load)); 161 imsg_flush(&base->ibuf); 162 } 163 164 void 165 module_drop_privilege(struct module_base *base) 166 { 167 struct passwd *pw; 168 169 tzset(); 170 171 /* Drop the privilege */ 172 if ((pw = getpwnam(RADIUSD_USER)) == NULL) 173 goto on_fail; 174 if (chroot(pw->pw_dir) == -1) 175 goto on_fail; 176 if (chdir("/") == -1) 177 goto on_fail; 178 if (setgroups(1, &pw->pw_gid) || 179 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 180 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 181 goto on_fail; 182 base->priv_dropped = true; 183 184 on_fail: 185 return; 186 } 187 188 int 189 module_notify_secret(struct module_base *base, const char *secret) 190 { 191 int ret; 192 193 ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_NOTIFY_SECRET, 194 0, 0, -1, secret, strlen(secret) + 1); 195 module_reset_event(base); 196 197 return (ret); 198 } 199 200 int 201 module_send_message(struct module_base *base, uint32_t cmd, const char *fmt, 202 ...) 203 { 204 char *msg; 205 va_list ap; 206 int ret; 207 208 if (fmt == NULL) 209 ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, NULL, 0); 210 else { 211 va_start(ap, fmt); 212 vasprintf(&msg, fmt, ap); 213 va_end(ap); 214 if (msg == NULL) 215 return (-1); 216 ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, msg, 217 strlen(msg) + 1); 218 free(msg); 219 } 220 module_reset_event(base); 221 222 return (ret); 223 } 224 225 int 226 module_userpass_ok(struct module_base *base, u_int q_id, const char *msg) 227 { 228 int ret; 229 struct iovec iov[2]; 230 231 iov[0].iov_base = &q_id; 232 iov[0].iov_len = sizeof(q_id); 233 iov[1].iov_base = (char *)msg; 234 iov[1].iov_len = strlen(msg) + 1; 235 ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_OK, 236 0, 0, -1, iov, 2); 237 module_reset_event(base); 238 239 return (ret); 240 } 241 242 int 243 module_userpass_fail(struct module_base *base, u_int q_id, const char *msg) 244 { 245 int ret; 246 struct iovec iov[2]; 247 248 iov[0].iov_base = &q_id; 249 iov[0].iov_len = sizeof(q_id); 250 iov[1].iov_base = (char *)msg; 251 iov[1].iov_len = strlen(msg) + 1; 252 ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_FAIL, 253 0, 0, -1, iov, 2); 254 module_reset_event(base); 255 256 return (ret); 257 } 258 259 int 260 module_accsreq_answer(struct module_base *base, u_int q_id, const u_char *pkt, 261 size_t pktlen) 262 { 263 return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER, 264 q_id, pkt, pktlen)); 265 } 266 267 int 268 module_accsreq_aborted(struct module_base *base, u_int q_id) 269 { 270 int ret; 271 272 ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED, 273 0, 0, -1, &q_id, sizeof(u_int)); 274 module_reset_event(base); 275 276 return (ret); 277 } 278 279 int 280 module_reqdeco_done(struct module_base *base, u_int q_id, const u_char *pkt, 281 size_t pktlen) 282 { 283 return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_REQDECO_DONE, 284 q_id, pkt, pktlen)); 285 } 286 287 int 288 module_resdeco_done(struct module_base *base, u_int q_id, const u_char *pkt, 289 size_t pktlen) 290 { 291 return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_RESDECO_DONE, 292 q_id, pkt, pktlen)); 293 } 294 295 static int 296 module_common_radpkt(struct module_base *base, uint32_t imsg_type, u_int q_id, 297 const u_char *pkt, size_t pktlen) 298 { 299 int ret = 0, off = 0, len, siz; 300 struct iovec iov[2]; 301 struct radiusd_module_radpkt_arg ans; 302 303 len = pktlen; 304 ans.q_id = q_id; 305 ans.pktlen = pktlen; 306 ans.final = false; 307 308 while (!ans.final) { 309 siz = MAX_IMSGSIZE - sizeof(ans); 310 if (len - off <= siz) { 311 ans.final = true; 312 siz = len - off; 313 } 314 iov[0].iov_base = &ans; 315 iov[0].iov_len = sizeof(ans); 316 if (siz > 0) { 317 iov[1].iov_base = (u_char *)pkt + off; 318 iov[1].iov_len = siz; 319 } 320 ret = imsg_composev(&base->ibuf, imsg_type, 0, 0, -1, iov, 321 (siz > 0)? 2 : 1); 322 if (ret == -1) 323 break; 324 off += siz; 325 } 326 module_reset_event(base); 327 328 return (ret); 329 } 330 331 static int 332 module_recv_imsg(struct module_base *base) 333 { 334 ssize_t n; 335 struct imsg imsg; 336 337 if (((n = imsg_read(&base->ibuf)) == -1 && errno != EAGAIN) || n == 0) { 338 if (n != 0) 339 syslog(LOG_ERR, "%s: imsg_read(): %m", __func__); 340 module_stop(base); 341 return (-1); 342 } 343 for (;;) { 344 if ((n = imsg_get(&base->ibuf, &imsg)) == -1) { 345 syslog(LOG_ERR, "%s: imsg_get(): %m", __func__); 346 module_stop(base); 347 return (-1); 348 } 349 if (n == 0) 350 break; 351 module_imsg_handler(base, &imsg); 352 imsg_free(&imsg); 353 } 354 module_reset_event(base); 355 356 return (0); 357 } 358 359 static int 360 module_imsg_handler(struct module_base *base, struct imsg *imsg) 361 { 362 ssize_t datalen; 363 364 datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 365 switch (imsg->hdr.type) { 366 case IMSG_RADIUSD_MODULE_SET_CONFIG: 367 { 368 struct radiusd_module_set_arg *arg; 369 struct radiusd_module_object *val; 370 u_int i; 371 size_t off; 372 char **argv; 373 374 arg = (struct radiusd_module_set_arg *)imsg->data; 375 off = sizeof(struct radiusd_module_set_arg); 376 377 if ((argv = calloc(sizeof(const char *), arg->nparamval)) 378 == NULL) { 379 module_send_message(base, IMSG_NG, 380 "Out of memory: %s", strerror(errno)); 381 break; 382 } 383 for (i = 0; i < arg->nparamval; i++) { 384 if (datalen - off < 385 sizeof(struct radiusd_module_object)) 386 break; 387 val = (struct radiusd_module_object *) 388 ((caddr_t)imsg->data + off); 389 if (datalen - off < val->size) 390 break; 391 argv[i] = (char *)(val + 1); 392 off += val->size; 393 } 394 if (i >= arg->nparamval) 395 module_config_set(base->ctx, arg->paramname, 396 arg->nparamval, argv); 397 else 398 module_send_message(base, IMSG_NG, 399 "Internal protocol error"); 400 free(argv); 401 402 break; 403 } 404 case IMSG_RADIUSD_MODULE_START: 405 if (module_start_module != NULL) { 406 module_start_module(base->ctx); 407 if (!base->priv_dropped) { 408 syslog(LOG_ERR, "Module tried to start with " 409 "root privileges"); 410 abort(); 411 } 412 } else { 413 if (!base->priv_dropped) { 414 syslog(LOG_ERR, "Module tried to start with " 415 "root privileges"); 416 abort(); 417 } 418 module_send_message(base, IMSG_OK, NULL); 419 } 420 break; 421 case IMSG_RADIUSD_MODULE_STOP: 422 module_stop(base); 423 break; 424 case IMSG_RADIUSD_MODULE_USERPASS: 425 { 426 struct radiusd_module_userpass_arg *userpass; 427 428 if (module_userpass == NULL) { 429 syslog(LOG_ERR, "Received USERPASS message, but " 430 "module doesn't support"); 431 break; 432 } 433 if (datalen < 434 (ssize_t)sizeof(struct radiusd_module_userpass_arg)) { 435 syslog(LOG_ERR, "Received USERPASS message, but " 436 "length is wrong"); 437 break; 438 } 439 userpass = (struct radiusd_module_userpass_arg *)imsg->data; 440 module_userpass(base->ctx, userpass->q_id, userpass->user, 441 (userpass->has_pass)? userpass->pass : NULL); 442 explicit_bzero(userpass, 443 sizeof(struct radiusd_module_userpass_arg)); 444 break; 445 } 446 case IMSG_RADIUSD_MODULE_ACCSREQ: 447 case IMSG_RADIUSD_MODULE_REQDECO: 448 case IMSG_RADIUSD_MODULE_RESDECO0_REQ: 449 case IMSG_RADIUSD_MODULE_RESDECO: 450 { 451 struct radiusd_module_radpkt_arg *accessreq; 452 int chunklen; 453 const char *typestr; 454 455 if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ) { 456 if (module_access_request == NULL) { 457 syslog(LOG_ERR, "Received ACCSREQ message, but " 458 "module doesn't support"); 459 break; 460 } 461 typestr = "ACCSREQ"; 462 } else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) { 463 if (module_request_decoration == NULL) { 464 syslog(LOG_ERR, "Received REQDECO message, but " 465 "module doesn't support"); 466 break; 467 } 468 typestr = "REQDECO"; 469 } else { 470 if (module_response_decoration == NULL) { 471 syslog(LOG_ERR, "Received RESDECO message, but " 472 "module doesn't support"); 473 break; 474 } 475 if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO0_REQ) 476 typestr = "RESDECO0_REQ"; 477 else 478 typestr = "RESDECO"; 479 } 480 481 if (datalen < 482 (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) { 483 syslog(LOG_ERR, "Received %s message, but " 484 "length is wrong", typestr); 485 break; 486 } 487 accessreq = (struct radiusd_module_radpkt_arg *)imsg->data; 488 if (base->radpktsiz < accessreq->pktlen) { 489 u_char *nradpkt; 490 if ((nradpkt = realloc(base->radpkt, 491 accessreq->pktlen)) == NULL) { 492 syslog(LOG_ERR, "Could not handle received " 493 "%s message: %m", typestr); 494 base->radpktoff = 0; 495 goto accsreq_out; 496 } 497 base->radpkt = nradpkt; 498 base->radpktsiz = accessreq->pktlen; 499 } 500 chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg); 501 if (chunklen > base->radpktsiz - base->radpktoff){ 502 syslog(LOG_ERR, 503 "Could not handle received %s message: " 504 "received length is too big", typestr); 505 base->radpktoff = 0; 506 goto accsreq_out; 507 } 508 memcpy(base->radpkt + base->radpktoff, 509 (caddr_t)(accessreq + 1), chunklen); 510 base->radpktoff += chunklen; 511 if (!accessreq->final) 512 goto accsreq_out; 513 if (base->radpktoff != accessreq->pktlen) { 514 syslog(LOG_ERR, 515 "Could not handle received %s " 516 "message: length is mismatch", typestr); 517 base->radpktoff = 0; 518 goto accsreq_out; 519 } 520 if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ) 521 module_access_request(base->ctx, accessreq->q_id, 522 base->radpkt, base->radpktoff); 523 else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) 524 module_request_decoration(base->ctx, accessreq->q_id, 525 base->radpkt, base->radpktoff); 526 else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO0_REQ) { 527 /* preserve request */ 528 if (base->radpktoff > base->radpkt2siz) { 529 u_char *nradpkt; 530 if ((nradpkt = realloc(base->radpkt2, 531 base->radpktoff)) == NULL) { 532 syslog(LOG_ERR, "Could not handle " 533 "received %s message: %m", typestr); 534 base->radpktoff = 0; 535 goto accsreq_out; 536 } 537 base->radpkt2 = nradpkt; 538 base->radpkt2siz = base->radpktoff; 539 } 540 memcpy(base->radpkt2, base->radpkt, base->radpktoff); 541 base->radpkt2len = base->radpktoff; 542 } else { 543 module_response_decoration(base->ctx, accessreq->q_id, 544 base->radpkt2, base->radpkt2len, base->radpkt, 545 base->radpktoff); 546 base->radpkt2len = 0; 547 } 548 base->radpktoff = 0; 549 accsreq_out: 550 break; 551 } 552 } 553 554 return (0); 555 } 556 557 void 558 module_stop(struct module_base *base) 559 { 560 if (module_stop_module != NULL) 561 module_stop_module(base->ctx); 562 #ifdef USE_LIBEVENT 563 event_del(&base->ev); 564 base->stopped = true; 565 #endif 566 close(base->ibuf.fd); 567 } 568 569 #ifdef USE_LIBEVENT 570 static void 571 module_on_event(int fd, short evmask, void *ctx) 572 { 573 struct module_base *base = ctx; 574 int ret; 575 576 base->ev_onhandler = true; 577 if (evmask & EV_WRITE) 578 base->writeready = true; 579 if (evmask & EV_READ) { 580 ret = module_recv_imsg(base); 581 if (ret < 0) 582 return; 583 } 584 while (base->writeready && base->ibuf.w.queued) { 585 ret = msgbuf_write(&base->ibuf.w); 586 if (ret > 0) 587 continue; 588 base->writeready = false; 589 if (ret == 0 && errno == EAGAIN) 590 break; 591 syslog(LOG_ERR, "%s: msgbuf_write: %m", __func__); 592 module_stop(base); 593 return; 594 } 595 base->ev_onhandler = false; 596 module_reset_event(base); 597 return; 598 } 599 #endif 600 601 static void 602 module_reset_event(struct module_base *base) 603 { 604 #ifdef USE_LIBEVENT 605 short evmask = 0; 606 struct timeval *tvp = NULL, tv = { 0, 0 }; 607 608 if (base->ev_onhandler) 609 return; 610 if (base->stopped) 611 return; 612 event_del(&base->ev); 613 614 evmask |= EV_READ; 615 if (base->ibuf.w.queued) { 616 if (!base->writeready) 617 evmask |= EV_WRITE; 618 else 619 tvp = &tv; /* fire immediately */ 620 } 621 event_set(&base->ev, base->ibuf.fd, evmask, module_on_event, base); 622 if (event_add(&base->ev, tvp) == -1) 623 syslog(LOG_ERR, "event_add() failed in %s()", __func__); 624 #endif 625 } 626