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