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