1 /* $OpenBSD: radiusd_module.c,v 1.14 2023/09/08 05:56:22 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) = 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 64 #ifdef USE_LIBEVENT 65 struct module_imsgbuf *module_imsgbuf; 66 bool writeready; 67 bool stopped; 68 bool ev_onhandler; 69 struct event ev; 70 #endif 71 }; 72 73 static int module_common_radpkt(struct module_base *, uint32_t, u_int, 74 const u_char *, size_t); 75 static int module_recv_imsg(struct module_base *); 76 static int module_imsg_handler(struct module_base *, struct imsg *); 77 #ifdef USE_LIBEVENT 78 static void module_on_event(int, short, void *); 79 #endif 80 static void module_reset_event(struct module_base *); 81 82 struct module_base * 83 module_create(int sock, void *ctx, struct module_handlers *handler) 84 { 85 struct module_base *base; 86 87 if ((base = calloc(1, sizeof(struct module_base))) == NULL) 88 return (NULL); 89 90 imsg_init(&base->ibuf, sock); 91 base->ctx = ctx; 92 93 module_userpass = handler->userpass; 94 module_access_request = handler->access_request; 95 module_config_set = handler->config_set; 96 module_request_decoration = handler->request_decoration; 97 module_response_decoration = handler->response_decoration; 98 module_start_module = handler->start; 99 module_stop_module = handler->stop; 100 101 return (base); 102 } 103 104 void 105 module_start(struct module_base *base) 106 { 107 #ifdef USE_LIBEVENT 108 int ival; 109 110 if ((ival = fcntl(base->ibuf.fd, F_GETFL)) == -1) 111 err(1, "Failed to F_GETFL"); 112 if (fcntl(base->ibuf.fd, F_SETFL, ival | O_NONBLOCK) == -1) 113 err(1, "Failed to setup NONBLOCK"); 114 event_set(&base->ev, base->ibuf.fd, EV_READ, module_on_event, base); 115 event_add(&base->ev, NULL); 116 #endif 117 } 118 119 int 120 module_run(struct module_base *base) 121 { 122 int ret; 123 124 ret = module_recv_imsg(base); 125 if (ret == 0) 126 imsg_flush(&base->ibuf); 127 128 return (ret); 129 } 130 131 void 132 module_destroy(struct module_base *base) 133 { 134 imsg_clear(&base->ibuf); 135 free(base); 136 } 137 138 void 139 module_load(struct module_base *base) 140 { 141 struct radiusd_module_load_arg load; 142 143 memset(&load, 0, sizeof(load)); 144 if (module_userpass != NULL) 145 load.cap |= RADIUSD_MODULE_CAP_USERPASS; 146 if (module_access_request != NULL) 147 load.cap |= RADIUSD_MODULE_CAP_ACCSREQ; 148 if (module_request_decoration != NULL) 149 load.cap |= RADIUSD_MODULE_CAP_REQDECO; 150 if (module_response_decoration != NULL) 151 load.cap |= RADIUSD_MODULE_CAP_RESDECO; 152 imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_LOAD, 0, 0, -1, &load, 153 sizeof(load)); 154 imsg_flush(&base->ibuf); 155 } 156 157 void 158 module_drop_privilege(struct module_base *base) 159 { 160 struct passwd *pw; 161 162 tzset(); 163 164 /* Drop the privilege */ 165 if ((pw = getpwnam(RADIUSD_USER)) == NULL) 166 goto on_fail; 167 if (chroot(pw->pw_dir) == -1) 168 goto on_fail; 169 if (chdir("/") == -1) 170 goto on_fail; 171 if (setgroups(1, &pw->pw_gid) || 172 setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 173 setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 174 goto on_fail; 175 base->priv_dropped = true; 176 177 on_fail: 178 return; 179 } 180 181 int 182 module_notify_secret(struct module_base *base, const char *secret) 183 { 184 int ret; 185 186 ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_NOTIFY_SECRET, 187 0, 0, -1, secret, strlen(secret) + 1); 188 module_reset_event(base); 189 190 return (ret); 191 } 192 193 int 194 module_send_message(struct module_base *base, uint32_t cmd, const char *fmt, 195 ...) 196 { 197 char *msg; 198 va_list ap; 199 int ret; 200 201 if (fmt == NULL) 202 ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, NULL, 0); 203 else { 204 va_start(ap, fmt); 205 vasprintf(&msg, fmt, ap); 206 va_end(ap); 207 if (msg == NULL) 208 return (-1); 209 ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, msg, 210 strlen(msg) + 1); 211 free(msg); 212 } 213 module_reset_event(base); 214 215 return (ret); 216 } 217 218 int 219 module_userpass_ok(struct module_base *base, u_int q_id, const char *msg) 220 { 221 int ret; 222 struct iovec iov[2]; 223 224 iov[0].iov_base = &q_id; 225 iov[0].iov_len = sizeof(q_id); 226 iov[1].iov_base = (char *)msg; 227 iov[1].iov_len = strlen(msg) + 1; 228 ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_OK, 229 0, 0, -1, iov, 2); 230 module_reset_event(base); 231 232 return (ret); 233 } 234 235 int 236 module_userpass_fail(struct module_base *base, u_int q_id, const char *msg) 237 { 238 int ret; 239 struct iovec iov[2]; 240 241 iov[0].iov_base = &q_id; 242 iov[0].iov_len = sizeof(q_id); 243 iov[1].iov_base = (char *)msg; 244 iov[1].iov_len = strlen(msg) + 1; 245 ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_FAIL, 246 0, 0, -1, iov, 2); 247 module_reset_event(base); 248 249 return (ret); 250 } 251 252 int 253 module_accsreq_answer(struct module_base *base, u_int q_id, const u_char *pkt, 254 size_t pktlen) 255 { 256 return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER, 257 q_id, pkt, pktlen)); 258 } 259 260 int 261 module_accsreq_aborted(struct module_base *base, u_int q_id) 262 { 263 int ret; 264 265 ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED, 266 0, 0, -1, &q_id, sizeof(u_int)); 267 module_reset_event(base); 268 269 return (ret); 270 } 271 272 int 273 module_reqdeco_done(struct module_base *base, u_int q_id, const u_char *pkt, 274 size_t pktlen) 275 { 276 return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_REQDECO_DONE, 277 q_id, pkt, pktlen)); 278 } 279 280 int 281 module_resdeco_done(struct module_base *base, u_int q_id, const u_char *pkt, 282 size_t pktlen) 283 { 284 return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_RESDECO_DONE, 285 q_id, pkt, pktlen)); 286 } 287 288 static int 289 module_common_radpkt(struct module_base *base, uint32_t imsg_type, u_int q_id, 290 const u_char *pkt, size_t pktlen) 291 { 292 int ret = 0, off = 0, len, siz; 293 struct iovec iov[2]; 294 struct radiusd_module_radpkt_arg ans; 295 296 len = pktlen; 297 ans.q_id = q_id; 298 ans.pktlen = pktlen; 299 ans.final = false; 300 301 while (!ans.final) { 302 siz = MAX_IMSGSIZE - sizeof(ans); 303 if (len - off <= siz) { 304 ans.final = true; 305 siz = len - off; 306 } 307 iov[0].iov_base = &ans; 308 iov[0].iov_len = sizeof(ans); 309 if (siz > 0) { 310 iov[1].iov_base = (u_char *)pkt + off; 311 iov[1].iov_len = siz; 312 } 313 ret = imsg_composev(&base->ibuf, imsg_type, 0, 0, -1, iov, 314 (siz > 0)? 2 : 1); 315 if (ret == -1) 316 break; 317 off += siz; 318 } 319 module_reset_event(base); 320 321 return (ret); 322 } 323 324 static int 325 module_recv_imsg(struct module_base *base) 326 { 327 ssize_t n; 328 struct imsg imsg; 329 330 if (((n = imsg_read(&base->ibuf)) == -1 && errno != EAGAIN) || n == 0) { 331 if (n != 0) 332 syslog(LOG_ERR, "%s: imsg_read(): %m", __func__); 333 module_stop(base); 334 return (-1); 335 } 336 for (;;) { 337 if ((n = imsg_get(&base->ibuf, &imsg)) == -1) { 338 syslog(LOG_ERR, "%s: imsg_get(): %m", __func__); 339 module_stop(base); 340 return (-1); 341 } 342 if (n == 0) 343 break; 344 module_imsg_handler(base, &imsg); 345 imsg_free(&imsg); 346 } 347 module_reset_event(base); 348 349 return (0); 350 } 351 352 static int 353 module_imsg_handler(struct module_base *base, struct imsg *imsg) 354 { 355 ssize_t datalen; 356 357 datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 358 switch (imsg->hdr.type) { 359 case IMSG_RADIUSD_MODULE_SET_CONFIG: 360 { 361 struct radiusd_module_set_arg *arg; 362 struct radiusd_module_object *val; 363 u_int i; 364 size_t off; 365 char **argv; 366 367 arg = (struct radiusd_module_set_arg *)imsg->data; 368 off = sizeof(struct radiusd_module_set_arg); 369 370 if ((argv = calloc(sizeof(const char *), arg->nparamval)) 371 == NULL) { 372 module_send_message(base, IMSG_NG, 373 "Out of memory: %s", strerror(errno)); 374 break; 375 } 376 for (i = 0; i < arg->nparamval; i++) { 377 if (datalen - off < 378 sizeof(struct radiusd_module_object)) 379 break; 380 val = (struct radiusd_module_object *) 381 ((caddr_t)imsg->data + off); 382 if (datalen - off < val->size) 383 break; 384 argv[i] = (char *)(val + 1); 385 off += val->size; 386 } 387 if (i >= arg->nparamval) 388 module_config_set(base->ctx, arg->paramname, 389 arg->nparamval, argv); 390 else 391 module_send_message(base, IMSG_NG, 392 "Internal protocol error"); 393 free(argv); 394 395 break; 396 } 397 case IMSG_RADIUSD_MODULE_START: 398 if (module_start_module != NULL) { 399 module_start_module(base->ctx); 400 if (!base->priv_dropped) { 401 syslog(LOG_ERR, "Module tried to start with " 402 "root privileges"); 403 abort(); 404 } 405 } else { 406 if (!base->priv_dropped) { 407 syslog(LOG_ERR, "Module tried to start with " 408 "root privileges"); 409 abort(); 410 } 411 module_send_message(base, IMSG_OK, NULL); 412 } 413 break; 414 case IMSG_RADIUSD_MODULE_STOP: 415 module_stop(base); 416 break; 417 case IMSG_RADIUSD_MODULE_USERPASS: 418 { 419 struct radiusd_module_userpass_arg *userpass; 420 421 if (module_userpass == NULL) { 422 syslog(LOG_ERR, "Received USERPASS message, but " 423 "module doesn't support"); 424 break; 425 } 426 if (datalen < 427 (ssize_t)sizeof(struct radiusd_module_userpass_arg)) { 428 syslog(LOG_ERR, "Received USERPASS message, but " 429 "length is wrong"); 430 break; 431 } 432 userpass = (struct radiusd_module_userpass_arg *)imsg->data; 433 module_userpass(base->ctx, userpass->q_id, userpass->user, 434 (userpass->has_pass)? userpass->pass : NULL); 435 explicit_bzero(userpass, 436 sizeof(struct radiusd_module_userpass_arg)); 437 break; 438 } 439 case IMSG_RADIUSD_MODULE_ACCSREQ: 440 case IMSG_RADIUSD_MODULE_REQDECO: 441 case IMSG_RADIUSD_MODULE_RESDECO: 442 { 443 struct radiusd_module_radpkt_arg *accessreq; 444 int chunklen; 445 const char *typestr; 446 447 if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ) { 448 if (module_access_request == NULL) { 449 syslog(LOG_ERR, "Received ACCSREQ message, but " 450 "module doesn't support"); 451 break; 452 } 453 typestr = "ACCSREQ"; 454 } else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) { 455 if (module_request_decoration == NULL) { 456 syslog(LOG_ERR, "Received REQDECO message, but " 457 "module doesn't support"); 458 break; 459 } 460 typestr = "REQDECO"; 461 } else { 462 if (module_response_decoration == NULL) { 463 syslog(LOG_ERR, "Received RESDECO message, but " 464 "module doesn't support"); 465 break; 466 } 467 typestr = "RESDECO"; 468 } 469 470 if (datalen < 471 (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) { 472 syslog(LOG_ERR, "Received %s message, but " 473 "length is wrong", typestr); 474 break; 475 } 476 accessreq = (struct radiusd_module_radpkt_arg *)imsg->data; 477 if (base->radpktsiz < accessreq->pktlen) { 478 u_char *nradpkt; 479 if ((nradpkt = realloc(base->radpkt, 480 accessreq->pktlen)) == NULL) { 481 syslog(LOG_ERR, "Could not handle received " 482 "%s message: %m", typestr); 483 base->radpktoff = 0; 484 goto accsreq_out; 485 } 486 base->radpkt = nradpkt; 487 base->radpktsiz = accessreq->pktlen; 488 } 489 chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg); 490 if (chunklen > base->radpktsiz - base->radpktoff){ 491 syslog(LOG_ERR, 492 "Could not handle received %s message: " 493 "received length is too big", typestr); 494 base->radpktoff = 0; 495 goto accsreq_out; 496 } 497 memcpy(base->radpkt + base->radpktoff, 498 (caddr_t)(accessreq + 1), chunklen); 499 base->radpktoff += chunklen; 500 if (!accessreq->final) 501 goto accsreq_out; 502 if (base->radpktoff != accessreq->pktlen) { 503 syslog(LOG_ERR, 504 "Could not handle received %s " 505 "message: length is mismatch", typestr); 506 base->radpktoff = 0; 507 goto accsreq_out; 508 } 509 if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ) 510 module_access_request(base->ctx, accessreq->q_id, 511 base->radpkt, base->radpktoff); 512 else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) 513 module_request_decoration(base->ctx, accessreq->q_id, 514 base->radpkt, base->radpktoff); 515 else 516 module_response_decoration(base->ctx, accessreq->q_id, 517 base->radpkt, base->radpktoff); 518 base->radpktoff = 0; 519 accsreq_out: 520 break; 521 } 522 } 523 524 return (0); 525 } 526 527 void 528 module_stop(struct module_base *base) 529 { 530 if (module_stop_module != NULL) 531 module_stop_module(base->ctx); 532 #ifdef USE_LIBEVENT 533 event_del(&base->ev); 534 base->stopped = true; 535 #endif 536 close(base->ibuf.fd); 537 } 538 539 #ifdef USE_LIBEVENT 540 static void 541 module_on_event(int fd, short evmask, void *ctx) 542 { 543 struct module_base *base = ctx; 544 int ret; 545 546 base->ev_onhandler = true; 547 if (evmask & EV_WRITE) 548 base->writeready = true; 549 if (evmask & EV_READ) { 550 ret = module_recv_imsg(base); 551 if (ret < 0) 552 return; 553 } 554 while (base->writeready && base->ibuf.w.queued) { 555 ret = msgbuf_write(&base->ibuf.w); 556 if (ret > 0) 557 continue; 558 base->writeready = false; 559 if (ret == 0 && errno == EAGAIN) 560 break; 561 syslog(LOG_ERR, "%s: msgbuf_write: %m", __func__); 562 module_stop(base); 563 return; 564 } 565 base->ev_onhandler = false; 566 module_reset_event(base); 567 return; 568 } 569 #endif 570 571 static void 572 module_reset_event(struct module_base *base) 573 { 574 #ifdef USE_LIBEVENT 575 short evmask = 0; 576 struct timeval *tvp = NULL, tv = { 0, 0 }; 577 578 if (base->ev_onhandler) 579 return; 580 if (base->stopped) 581 return; 582 event_del(&base->ev); 583 584 evmask |= EV_READ; 585 if (base->ibuf.w.queued) { 586 if (!base->writeready) 587 evmask |= EV_WRITE; 588 else 589 tvp = &tv; /* fire immediately */ 590 } 591 event_set(&base->ev, base->ibuf.fd, evmask, module_on_event, base); 592 if (event_add(&base->ev, tvp) == -1) 593 syslog(LOG_ERR, "event_add() failed in %s()", __func__); 594 #endif 595 } 596