1 /* $OpenBSD: radiusd_module.c,v 1.10 2016/04/05 21:24:02 krw 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 int, 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)) < 0) 106 err(1, "Failed to F_GETFL"); 107 if (fcntl(base->ibuf.fd, F_SETFL, ival | O_NONBLOCK) < 0) 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, int modified, 245 const u_char *pkt, size_t pktlen) 246 { 247 return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER, 248 q_id, modified, 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 int modified, 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.modified = modified; 274 while (off < len) { 275 siz = MAX_IMSGSIZE - sizeof(ans); 276 if (len - off > siz) { 277 ans.final = true; 278 ans.datalen = siz; 279 } else { 280 ans.final = true; 281 ans.datalen = len - off; 282 } 283 iov[0].iov_base = &ans; 284 iov[0].iov_len = sizeof(ans); 285 iov[1].iov_base = (u_char *)pkt + off; 286 iov[1].iov_len = ans.datalen; 287 ret = imsg_composev(&base->ibuf, imsg_type, 0, 0, -1, iov, 2); 288 if (ret == -1) 289 break; 290 off += ans.datalen; 291 } 292 module_reset_event(base); 293 294 return (ret); 295 } 296 297 static int 298 module_recv_imsg(struct module_base *base) 299 { 300 ssize_t n; 301 struct imsg imsg; 302 303 if (((n = imsg_read(&base->ibuf)) == -1 && errno != EAGAIN) || n == 0) { 304 if (n != 0) 305 syslog(LOG_ERR, "%s: imsg_read(): %m", __func__); 306 module_stop(base); 307 return (-1); 308 } 309 310 for (;;) { 311 if ((n = imsg_get(&base->ibuf, &imsg)) == -1) { 312 syslog(LOG_ERR, "%s: imsg_get(): %m", __func__); 313 module_stop(base); 314 return (-1); 315 } 316 if (n == 0) 317 break; 318 module_imsg_handler(base, &imsg); 319 imsg_free(&imsg); 320 } 321 module_reset_event(base); 322 323 return (0); 324 } 325 326 static int 327 module_imsg_handler(struct module_base *base, struct imsg *imsg) 328 { 329 ssize_t datalen; 330 331 datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 332 switch (imsg->hdr.type) { 333 case IMSG_RADIUSD_MODULE_SET_CONFIG: 334 { 335 struct radiusd_module_set_arg *arg; 336 struct radiusd_module_object *val; 337 u_int i; 338 size_t off; 339 char **argv; 340 341 arg = (struct radiusd_module_set_arg *)imsg->data; 342 off = sizeof(struct radiusd_module_set_arg); 343 344 if ((argv = calloc(sizeof(const char *), arg->nparamval)) 345 == NULL) { 346 module_send_message(base, IMSG_NG, 347 "Out of memory: %s", strerror(errno)); 348 break; 349 } 350 for (i = 0; i < arg->nparamval; i++) { 351 if (datalen - off < 352 sizeof(struct radiusd_module_object)) 353 break; 354 val = (struct radiusd_module_object *) 355 ((caddr_t)imsg->data + off); 356 if (datalen - off < val->size) 357 break; 358 argv[i] = (char *)(val + 1); 359 off += val->size; 360 } 361 if (i >= arg->nparamval) 362 module_config_set(base->ctx, arg->paramname, 363 arg->nparamval, argv); 364 else 365 module_send_message(base, IMSG_NG, 366 "Internal protocol error"); 367 free(argv); 368 369 break; 370 } 371 case IMSG_RADIUSD_MODULE_START: 372 if (module_start_module != NULL) { 373 module_start_module(base->ctx); 374 if (!base->priv_dropped) { 375 syslog(LOG_ERR, "Module tried to start with " 376 "root privileges"); 377 abort(); 378 } 379 } else { 380 if (!base->priv_dropped) { 381 syslog(LOG_ERR, "Module tried to start with " 382 "root privileges"); 383 abort(); 384 } 385 module_send_message(base, IMSG_OK, NULL); 386 } 387 break; 388 case IMSG_RADIUSD_MODULE_STOP: 389 module_stop(base); 390 break; 391 case IMSG_RADIUSD_MODULE_USERPASS: 392 { 393 struct radiusd_module_userpass_arg *userpass; 394 395 if (module_userpass == NULL) { 396 syslog(LOG_ERR, "Received USERPASS message, but " 397 "module doesn't support"); 398 break; 399 } 400 if (datalen < 401 (ssize_t)sizeof(struct radiusd_module_userpass_arg)) { 402 syslog(LOG_ERR, "Received USERPASS message, but " 403 "length is wrong"); 404 break; 405 } 406 userpass = (struct radiusd_module_userpass_arg *)imsg->data; 407 module_userpass(base->ctx, userpass->q_id, userpass->user, 408 (userpass->has_pass)? userpass->pass : NULL); 409 explicit_bzero(userpass, 410 sizeof(struct radiusd_module_userpass_arg)); 411 break; 412 } 413 case IMSG_RADIUSD_MODULE_ACCSREQ: 414 { 415 struct radiusd_module_radpkt_arg *accessreq; 416 int chunklen; 417 418 if (module_access_request == NULL) { 419 syslog(LOG_ERR, "Received ACCSREQ message, but " 420 "module doesn't support"); 421 break; 422 } 423 if (datalen < 424 (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) { 425 syslog(LOG_ERR, "Received ACCSREQ message, but " 426 "length is wrong"); 427 break; 428 } 429 accessreq = (struct radiusd_module_radpkt_arg *)imsg->data; 430 if (base->radpktsiz < accessreq->datalen) { 431 u_char *nradpkt; 432 if ((nradpkt = realloc(base->radpkt, 433 accessreq->datalen)) == NULL) { 434 syslog(LOG_ERR, "Could not handle received " 435 "ACCSREQ message: %m"); 436 base->radpktoff = 0; 437 goto accsreq_out; 438 } 439 base->radpkt = nradpkt; 440 base->radpktsiz = accessreq->datalen; 441 } 442 chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg); 443 if (chunklen > base->radpktsiz - base->radpktoff){ 444 syslog(LOG_ERR, 445 "Could not handle received ACCSREQ message: " 446 "received length is too big"); 447 base->radpktoff = 0; 448 goto accsreq_out; 449 } 450 memcpy(base->radpkt + base->radpktoff, 451 (caddr_t)(accessreq + 1), chunklen); 452 base->radpktoff += chunklen; 453 if (!accessreq->final) 454 goto accsreq_out; 455 if (base->radpktoff != accessreq->datalen) { 456 syslog(LOG_ERR, 457 "Could not handle received ACCSREQ " 458 "message: length is mismatch"); 459 base->radpktoff = 0; 460 goto accsreq_out; 461 } 462 module_access_request(base->ctx, accessreq->q_id, 463 base->radpkt, base->radpktoff); 464 base->radpktoff = 0; 465 accsreq_out: 466 break; 467 } 468 } 469 470 return (0); 471 } 472 473 static void 474 module_stop(struct module_base *base) 475 { 476 if (module_stop_module != NULL) 477 module_stop_module(base->ctx); 478 #ifdef USE_LIBEVENT 479 event_del(&base->ev); 480 base->stopped = true; 481 #endif 482 close(base->ibuf.fd); 483 } 484 485 #ifdef USE_LIBEVENT 486 static void 487 module_on_event(int fd, short evmask, void *ctx) 488 { 489 struct module_base *base = ctx; 490 int ret; 491 492 base->ev_onhandler = true; 493 if (evmask & EV_WRITE) 494 base->writeready = true; 495 if (evmask & EV_READ) { 496 ret = module_recv_imsg(base); 497 if (ret < 0) 498 return; 499 } 500 while (base->writeready && base->ibuf.w.queued) { 501 ret = msgbuf_write(&base->ibuf.w); 502 if (ret > 0) 503 continue; 504 base->writeready = false; 505 if (ret == 0 && errno == EAGAIN) 506 break; 507 syslog(LOG_ERR, "%s: msgbuf_write: %m", __func__); 508 module_stop(base); 509 return; 510 } 511 base->ev_onhandler = false; 512 module_reset_event(base); 513 return; 514 } 515 #endif 516 517 static void 518 module_reset_event(struct module_base *base) 519 { 520 #ifdef USE_LIBEVENT 521 short evmask = 0; 522 struct timeval *tvp = NULL, tv = { 0, 0 }; 523 524 if (base->ev_onhandler) 525 return; 526 if (base->stopped) 527 return; 528 event_del(&base->ev); 529 530 evmask |= EV_READ; 531 if (base->ibuf.w.queued) { 532 if (!base->writeready) 533 evmask |= EV_WRITE; 534 else 535 tvp = &tv; /* fire immediately */ 536 } 537 event_set(&base->ev, base->ibuf.fd, evmask, module_on_event, base); 538 if (event_add(&base->ev, tvp) == -1) 539 syslog(LOG_ERR, "event_add() failed in %s()", __func__); 540 #endif 541 } 542