1 /* $OpenBSD: radiusd_radius.c,v 1.20 2024/02/09 07:41:32 yasuoka Exp $ */ 2 3 /* 4 * Copyright (c) 2013 Internet Initiative Japan Inc. 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 #include <sys/types.h> 20 #include <sys/queue.h> 21 #include <sys/socket.h> 22 #include <netinet/in.h> 23 24 #include <err.h> 25 #include <errno.h> 26 #include <event.h> 27 #include <fcntl.h> 28 #include <stdbool.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <syslog.h> 33 #include <unistd.h> 34 35 #include <radius.h> 36 37 #include "radiusd.h" 38 #include "radiusd_module.h" 39 #include "util.h" 40 #include "log.h" 41 42 struct radius_server { 43 struct module_radius *module; 44 int sock; 45 union { 46 struct sockaddr_in6 sin6; 47 struct sockaddr_in sin4; 48 } addr; 49 union { 50 struct sockaddr_in6 sin6; 51 struct sockaddr_in sin4; 52 } local; 53 struct event ev; 54 u_char req_id_seq; 55 }; 56 57 struct module_radius { 58 struct module_base *base; 59 struct radius_server server[4]; 60 char secret[RADIUSD_SECRET_MAX]; 61 u_int nserver; 62 u_int curr_server; 63 u_int req_timeout; 64 u_int max_tries; 65 u_int max_failovers; 66 u_int nfailover; 67 TAILQ_HEAD(,module_radius_req) req; 68 }; 69 70 struct module_radius_req { 71 struct module_radius *module; 72 struct radius_server *server; 73 u_int q_id; 74 RADIUS_PACKET *q_pkt; 75 u_int ntry; 76 u_int nfailover; 77 u_char req_id; 78 struct event ev; 79 TAILQ_ENTRY(module_radius_req) next; 80 }; 81 82 static void module_radius_init(struct module_radius *); 83 static void module_radius_config_set(void *, const char *, int, 84 char * const *); 85 static void module_radius_start(void *); 86 static void module_radius_stop(void *); 87 static void module_radius_access_request(void *, u_int, const u_char *, 88 size_t); 89 static int radius_server_start(struct radius_server *); 90 static void radius_server_stop(struct radius_server *); 91 static void radius_server_on_event(int, short, void *); 92 static void radius_server_on_fail(struct radius_server *, const char *); 93 static void module_radius_req_send(struct module_radius_req *); 94 static int module_radius_req_reset_event(struct module_radius_req *); 95 static void module_radius_req_on_timeout(int, short, void *); 96 static void module_radius_req_on_success(struct module_radius_req *, 97 const u_char *, size_t); 98 static void module_radius_req_on_failure(struct module_radius_req *); 99 100 static void module_radius_req_free(struct module_radius_req *); 101 static void module_radius_req_select_server(struct module_radius_req *); 102 103 static void module_radius_req_reset_msgauth(struct module_radius_req *); 104 static void module_radius_log(struct module_radius *, int, const char *, ...); 105 106 static struct module_handlers module_radius_handlers = { 107 .config_set = module_radius_config_set, 108 .start = module_radius_start, 109 .stop = module_radius_stop, 110 .access_request = module_radius_access_request 111 }; 112 113 #ifndef nitems 114 #define nitems(_x) (sizeof((_x)) / sizeof((_x)[0])) 115 #endif 116 117 int 118 main(int argc, char *argv[]) 119 { 120 static struct module_radius module_radius; 121 122 module_radius_init(&module_radius); 123 openlog(NULL, LOG_PID, LOG_DAEMON); 124 125 if ((module_radius.base = module_create( 126 STDIN_FILENO, &module_radius, &module_radius_handlers)) == NULL) 127 err(1, "Could not create a module instance"); 128 module_drop_privilege(module_radius.base, 0); 129 setproctitle("[main]"); 130 131 module_load(module_radius.base); 132 log_init(0); 133 event_init(); 134 135 if (pledge("stdio inet", NULL) == -1) 136 err(EXIT_FAILURE, "pledge"); 137 138 module_start(module_radius.base); 139 event_loop(0); 140 141 module_destroy(module_radius.base); 142 143 exit(EXIT_SUCCESS); 144 } 145 146 static void 147 module_radius_init(struct module_radius *module) 148 { 149 memset(module, 0, sizeof(struct module_radius)); 150 TAILQ_INIT(&module->req); 151 } 152 153 static void 154 module_radius_config_set(void *ctx, const char *paramname, int paramvalc, 155 char * const * paramvalv) 156 { 157 const char *errmsg = NULL; 158 struct addrinfo *res; 159 struct module_radius *module = ctx; 160 161 if (strcmp(paramname, "server") == 0) { 162 SYNTAX_ASSERT(paramvalc == 1, 163 "`server' must have just one argument"); 164 SYNTAX_ASSERT(module->nserver < (int)nitems(module->server), 165 "number of server reached limit"); 166 167 if (addrport_parse(paramvalv[0], IPPROTO_UDP, &res) != 0) 168 SYNTAX_ASSERT(0, "could not parse address and port"); 169 memcpy(&module->server[module->nserver].addr, res->ai_addr, 170 res->ai_addrlen); 171 172 if (ntohs(module->server[module->nserver].addr.sin4.sin_port) 173 == 0) 174 module->server[module->nserver].addr.sin4.sin_port 175 = htons(RADIUS_DEFAULT_PORT); 176 177 module->server[module->nserver].sock = -1; 178 module->nserver++; 179 freeaddrinfo(res); 180 } else if (strcmp(paramname, "request-timeout") == 0) { 181 SYNTAX_ASSERT(paramvalc == 1, 182 "`request-timeout' must have just one argument"); 183 module->req_timeout = (int)strtonum(paramvalv[0], 0, 184 UINT16_MAX, &errmsg); 185 if (module->req_timeout == 0 && errmsg != NULL) { 186 module_send_message(module->base, IMSG_NG, 187 "`request-timeout must be 0-%d", UINT16_MAX); 188 return; 189 } 190 } else if (strcmp(paramname, "max-tries") == 0) { 191 SYNTAX_ASSERT(paramvalc == 1, 192 "`max-tries' must have just one argument"); 193 module->max_tries = (int)strtonum(paramvalv[0], 0, 194 UINT16_MAX, &errmsg); 195 if (module->max_tries == 0 && errmsg != NULL) { 196 module_send_message(module->base, IMSG_NG, 197 "`max-tries must be 0-%d", UINT16_MAX); 198 return; 199 } 200 201 } else if (strcmp(paramname, "max-failovers") == 0) { 202 SYNTAX_ASSERT(paramvalc == 1, 203 "`max-failovers' must have just one argument"); 204 module->max_failovers = (int)strtonum(paramvalv[0], 0, 205 UINT16_MAX, &errmsg); 206 if (module->max_failovers == 0 && errmsg != NULL) { 207 module_send_message(module->base, IMSG_NG, 208 "`max-failovers' must be 0-%d", UINT16_MAX); 209 return; 210 } 211 } else if (strcmp(paramname, "secret") == 0) { 212 SYNTAX_ASSERT(paramvalc == 1, 213 "`secret' must have just one argument"); 214 if (strlcpy(module->secret, paramvalv[0], 215 sizeof(module->secret)) >= sizeof(module->secret)) { 216 module_send_message(module->base, IMSG_NG, 217 "`secret' length must be 0-%lu", 218 (u_long) sizeof(module->secret) - 1); 219 return; 220 } 221 } else if (strcmp(paramname, "_debug") == 0) 222 log_init(1); 223 else if (strncmp(paramname, "_", 1) == 0) 224 /* nothing */; /* ignore all internal messages */ 225 else { 226 module_send_message(module->base, IMSG_NG, 227 "Unknown config parameter name `%s'", paramname); 228 return; 229 } 230 module_send_message(module->base, IMSG_OK, NULL); 231 232 return; 233 syntax_error: 234 module_send_message(module->base, IMSG_NG, "%s", errmsg); 235 } 236 237 static void 238 module_radius_start(void *ctx) 239 { 240 u_int i; 241 struct module_radius *module = ctx; 242 243 if (module->nserver <= 0) { 244 module_send_message(module->base, IMSG_NG, 245 "needs one `server' at least"); 246 return; 247 } 248 249 if (module->secret[0] == '\0') { 250 module_send_message(module->base, IMSG_NG, 251 "`secret' configuration is required"); 252 return; 253 } 254 255 for (i = 0; i < module->nserver; i++) { 256 module->server[i].module = module; 257 if (radius_server_start(&module->server[i]) != 0) { 258 module_send_message(module->base, IMSG_NG, 259 "module `radius' failed to start one of " 260 "the servers"); 261 return; 262 } 263 } 264 module_send_message(module->base, IMSG_OK, NULL); 265 266 module_notify_secret(module->base, module->secret); 267 } 268 269 static void 270 module_radius_stop(void *ctx) 271 { 272 u_int i; 273 struct module_radius_req *req, *treq; 274 struct module_radius *module = ctx; 275 276 TAILQ_FOREACH_SAFE(req, &module->req, next, treq) 277 module_radius_req_on_failure(req); 278 279 for (i = 0; i < module->nserver; i++) 280 radius_server_stop(&module->server[i]); 281 } 282 283 static void 284 module_radius_access_request(void *ctx, u_int q_id, const u_char *pkt, 285 size_t pktlen) 286 { 287 struct module_radius *module = ctx; 288 struct module_radius_req *req; 289 u_char attrbuf[256]; 290 ssize_t attrlen; 291 292 req = calloc(1, sizeof(struct module_radius_req)); 293 if (req == NULL) { 294 module_radius_log(module, LOG_WARNING, 295 "%s: Out of memory: %m", __func__); 296 goto on_fail; 297 } 298 299 req->ntry = 0; 300 req->module = module; 301 req->q_id = q_id; 302 if ((req->q_pkt = radius_convert_packet(pkt, pktlen)) == NULL) { 303 module_radius_log(module, LOG_WARNING, 304 "%s: radius_convert_packet() failed: %m", __func__); 305 goto on_fail; 306 } 307 evtimer_set(&req->ev, module_radius_req_on_timeout, req); 308 TAILQ_INSERT_TAIL(&req->module->req, req, next); 309 310 /* 311 * radiusd decrypt User-Password attribute. crypt it again with our 312 * secret. 313 */ 314 attrlen = sizeof(attrbuf); 315 if (radius_get_raw_attr(req->q_pkt, RADIUS_TYPE_USER_PASSWORD, 316 attrbuf, &attrlen) == 0) { 317 attrbuf[attrlen] = '\0'; 318 radius_del_attr_all(req->q_pkt, RADIUS_TYPE_USER_PASSWORD); 319 radius_put_user_password_attr(req->q_pkt, attrbuf, 320 module->secret); 321 } 322 323 /* select current server */ 324 module_radius_req_select_server(req); 325 326 module_radius_req_send(req); 327 328 return; 329 330 on_fail: 331 free(req); 332 module_accsreq_aborted(module->base, q_id); 333 } 334 335 /* 336 * radius_server 337 */ 338 static int 339 radius_server_start(struct radius_server *server) 340 { 341 socklen_t locallen; 342 char buf0[NI_MAXHOST + NI_MAXSERV + 32]; 343 char buf1[NI_MAXHOST + NI_MAXSERV + 32]; 344 345 if ((server->sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)) 346 == -1) { 347 module_radius_log(server->module, LOG_WARNING, 348 "%s: socket() failed", __func__); 349 goto on_error; 350 } 351 if (connect(server->sock, (struct sockaddr *)&server->addr, 352 server->addr.sin4.sin_len) != 0) { 353 module_radius_log(server->module, LOG_WARNING, 354 "%s: connect to %s failed", __func__, 355 addrport_tostring((struct sockaddr *)&server->addr, 356 server->addr.sin4.sin_len, buf1, sizeof(buf1))); 357 goto on_error; 358 } 359 locallen = sizeof(server->local); 360 if (getsockname(server->sock, (struct sockaddr *)&server->local, 361 &locallen) != 0) { 362 module_radius_log(server->module, LOG_WARNING, 363 "%s: getsockanme() failed", __func__); 364 goto on_error; 365 } 366 module_radius_log(server->module, LOG_INFO, 367 "Use %s to send requests for %s", 368 addrport_tostring((struct sockaddr *)&server->local, 369 locallen, buf0, sizeof(buf0)), 370 addrport_tostring((struct sockaddr *)&server->addr, 371 server->addr.sin4.sin_len, buf1, sizeof(buf1))); 372 373 event_set(&server->ev, server->sock, EV_READ | EV_PERSIST, 374 radius_server_on_event, server); 375 if (event_add(&server->ev, NULL)) { 376 module_radius_log(server->module, LOG_WARNING, 377 "%s: event_add() failed", __func__); 378 goto on_error; 379 } 380 381 return (0); 382 on_error: 383 if (server->sock >= 0) 384 close(server->sock); 385 server->sock = -1; 386 return (-1); 387 } 388 389 static void 390 radius_server_stop(struct radius_server *server) 391 { 392 event_del(&server->ev); 393 if (server->sock >= 0) 394 close(server->sock); 395 server->sock = -1; 396 } 397 398 static void 399 radius_server_on_event(int fd, short evmask, void *ctx) 400 { 401 int sz, res_id; 402 u_char pkt[65535]; 403 char buf[NI_MAXHOST + NI_MAXSERV + 32]; 404 struct radius_server *server = ctx; 405 RADIUS_PACKET *radpkt = NULL; 406 struct module_radius_req *req; 407 struct sockaddr *peer; 408 409 peer = (struct sockaddr *)&server->addr; 410 if ((sz = recv(server->sock, pkt, sizeof(pkt), 0)) == -1) { 411 if (errno == EAGAIN) 412 return; 413 module_radius_log(server->module, LOG_WARNING, 414 "server=%s recv() failed: %m", 415 addrport_tostring(peer, peer->sa_len, buf, sizeof(buf))); 416 return; 417 } 418 if ((radpkt = radius_convert_packet(pkt, sz)) == NULL) { 419 module_radius_log(server->module, LOG_WARNING, 420 "server=%s could not convert the received message to a " 421 "RADIUS packet object: %m", 422 addrport_tostring(peer, peer->sa_len, buf, sizeof(buf))); 423 return; 424 } 425 res_id = radius_get_id(radpkt); 426 TAILQ_FOREACH(req, &server->module->req, next) { 427 if (req->server == server && req->req_id == res_id) 428 break; 429 } 430 if (req == NULL) { 431 module_radius_log(server->module, LOG_WARNING, 432 "server=%s Received radius message has unknown id=%d", 433 addrport_tostring(peer, peer->sa_len, buf, sizeof(buf)), 434 res_id); 435 goto out; 436 } 437 radius_set_request_packet(radpkt, req->q_pkt); 438 439 if (radius_check_response_authenticator(radpkt, 440 server->module->secret) != 0) { 441 module_radius_log(server->module, LOG_WARNING, 442 "server=%s Received radius message(id=%d) has bad " 443 "authenticator", 444 addrport_tostring(peer, peer->sa_len, buf, 445 sizeof(buf)), res_id); 446 goto out; 447 } 448 if (radius_has_attr(radpkt, 449 RADIUS_TYPE_MESSAGE_AUTHENTICATOR) && 450 radius_check_message_authenticator(radpkt, 451 server->module->secret) != 0) { 452 module_radius_log(server->module, LOG_WARNING, 453 "server=%s Received radius message(id=%d) has bad " 454 "message authenticator", 455 addrport_tostring(peer, peer->sa_len, buf, 456 sizeof(buf)), res_id); 457 goto out; 458 } 459 460 module_radius_log(server->module, LOG_INFO, 461 "q=%u received a response from server %s", req->q_id, 462 addrport_tostring(peer, peer->sa_len, buf, sizeof(buf))); 463 464 module_radius_req_on_success(req, radius_get_data(radpkt), 465 radius_get_length(radpkt)); 466 out: 467 if (radpkt != NULL) 468 radius_delete_packet(radpkt); 469 } 470 471 static void 472 radius_server_on_fail(struct radius_server *server, const char *failmsg) 473 { 474 char buf0[NI_MAXHOST + NI_MAXSERV + 32]; 475 char buf1[NI_MAXHOST + NI_MAXSERV + 32]; 476 struct sockaddr *caddr, *naddr; 477 478 caddr = (struct sockaddr *)&server->addr; 479 if (server->module->nserver <= 1) { 480 module_radius_log(server->module, LOG_WARNING, 481 "Server %s failed: %s", 482 addrport_tostring(caddr, caddr->sa_len, buf0, sizeof(buf0)), 483 failmsg); 484 return; 485 } 486 server->module->curr_server++; 487 server->module->curr_server %= server->module->nserver; 488 naddr = (struct sockaddr *) 489 &server->module->server[server->module->curr_server].addr; 490 491 module_radius_log(server->module, LOG_WARNING, 492 "Server %s failed: %s Fail over to %s", 493 addrport_tostring(caddr, caddr->sa_len, buf0, sizeof(buf0)), 494 failmsg, 495 addrport_tostring(naddr, naddr->sa_len, buf1, sizeof(buf1))); 496 } 497 498 /* module_radius_req */ 499 500 static void 501 module_radius_req_send(struct module_radius_req *req) 502 { 503 int sz; 504 struct sockaddr *peer; 505 char msg[BUFSIZ]; 506 507 peer = (struct sockaddr *)&req->server->addr; 508 if ((sz = send(req->server->sock, radius_get_data(req->q_pkt), 509 radius_get_length(req->q_pkt), 0)) < 0) { 510 module_radius_log(req->module, LOG_WARNING, 511 "Sending RADIUS query q=%u to %s failed: %m", 512 req->q_id, 513 addrport_tostring(peer, peer->sa_len, msg, sizeof(msg))); 514 /* retry anyway */ 515 } 516 module_radius_log(req->module, LOG_INFO, 517 "Send RADIUS query q=%u id=%d to %s successfully", 518 req->q_id, req->req_id, 519 addrport_tostring(peer, peer->sa_len, msg, sizeof(msg))); 520 if (module_radius_req_reset_event(req) != -1) 521 req->ntry++; 522 } 523 524 static int 525 module_radius_req_reset_event(struct module_radius_req *req) 526 { 527 struct timeval tv; 528 static int timeouts[] = { 2, 4, 8 }; 529 530 tv.tv_usec = 0; 531 if (req->module->req_timeout != 0) 532 tv.tv_sec = req->module->req_timeout; 533 else { 534 if (req->ntry < nitems(timeouts)) 535 tv.tv_sec = timeouts[req->ntry]; 536 else 537 tv.tv_sec = timeouts[nitems(timeouts) - 1]; 538 } 539 if (evtimer_add(&req->ev, &tv) != 0) { 540 module_radius_log(req->module, LOG_WARNING, 541 "Cannot process the request for q=%u: " 542 "evtimer_add() failed: %m", req->q_id); 543 module_radius_req_on_failure(req); 544 return (-1); 545 } 546 return (0); 547 } 548 549 static void 550 module_radius_req_on_timeout(int fd, short evmask, void *ctx) 551 { 552 struct module_radius_req *req = ctx; 553 char msg[BUFSIZ]; 554 555 556 if (req->module->max_tries <= req->ntry) { 557 snprintf(msg, sizeof(msg), "q=%u didn't response RADIUS query " 558 "%d time%s", req->q_id, req->ntry, 559 (req->ntry > 0)? "s" : ""); 560 radius_server_on_fail(req->server, msg); 561 if (++req->nfailover >= req->module->max_failovers) { 562 module_radius_log(req->module, 563 LOG_WARNING, "RADIUS query q=%u time out", 564 req->q_id); 565 module_radius_req_on_failure(req); 566 return; 567 } 568 /* select the next server */ 569 module_radius_req_select_server(req); 570 } 571 module_radius_req_send(req); 572 } 573 574 static void 575 module_radius_req_on_success(struct module_radius_req *req, 576 const u_char *pkt, size_t pktlen) 577 { 578 module_accsreq_answer(req->module->base, req->q_id, pkt, pktlen); 579 module_radius_req_free(req); 580 } 581 582 static void 583 module_radius_req_on_failure(struct module_radius_req *req) 584 { 585 module_accsreq_aborted(req->module->base, req->q_id); 586 module_radius_req_free(req); 587 } 588 589 590 static void 591 module_radius_req_free(struct module_radius_req *req) 592 { 593 evtimer_del(&req->ev); 594 TAILQ_REMOVE(&req->module->req, req, next); 595 if (req->q_pkt != NULL) 596 radius_delete_packet(req->q_pkt); 597 free(req); 598 } 599 600 static void 601 module_radius_req_select_server(struct module_radius_req *req) 602 { 603 req->server = &req->module->server[req->module->curr_server]; 604 req->ntry = 0; 605 req->req_id = req->server->req_id_seq++; 606 radius_set_id(req->q_pkt, req->req_id); 607 module_radius_req_reset_msgauth(req); 608 } 609 610 static void 611 module_radius_req_reset_msgauth(struct module_radius_req *req) 612 { 613 if (radius_has_attr(req->q_pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) 614 radius_del_attr_all(req->q_pkt, 615 RADIUS_TYPE_MESSAGE_AUTHENTICATOR); 616 radius_put_message_authenticator(req->q_pkt, 617 req->module->secret); 618 } 619 620 static void 621 module_radius_log(struct module_radius *module, int pri, const char *fmt, ...) 622 { 623 char fmt0[BUFSIZ]; 624 va_list va; 625 626 snprintf(fmt0, sizeof(fmt0), "radius: %s", fmt); 627 va_start(va, fmt); 628 vlog(pri, fmt0, va); 629 va_end(va); 630 } 631