1 /* $OpenBSD: radiusd_radius.c,v 1.22 2024/08/16 09:52:16 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 event_base_free(NULL); 143 144 exit(EXIT_SUCCESS); 145 } 146 147 static void 148 module_radius_init(struct module_radius *module) 149 { 150 memset(module, 0, sizeof(struct module_radius)); 151 TAILQ_INIT(&module->req); 152 } 153 154 static void 155 module_radius_config_set(void *ctx, const char *paramname, int paramvalc, 156 char * const * paramvalv) 157 { 158 const char *errmsg = NULL; 159 struct addrinfo *res; 160 struct module_radius *module = ctx; 161 162 if (strcmp(paramname, "server") == 0) { 163 SYNTAX_ASSERT(paramvalc == 1, 164 "`server' must have just one argument"); 165 SYNTAX_ASSERT(module->nserver < (int)nitems(module->server), 166 "number of server reached limit"); 167 168 if (addrport_parse(paramvalv[0], IPPROTO_UDP, &res) != 0) 169 SYNTAX_ASSERT(0, "could not parse address and port"); 170 memcpy(&module->server[module->nserver].addr, res->ai_addr, 171 res->ai_addrlen); 172 173 if (ntohs(module->server[module->nserver].addr.sin4.sin_port) 174 == 0) 175 module->server[module->nserver].addr.sin4.sin_port 176 = htons(RADIUS_DEFAULT_PORT); 177 178 module->server[module->nserver].sock = -1; 179 module->nserver++; 180 freeaddrinfo(res); 181 } else if (strcmp(paramname, "request-timeout") == 0) { 182 SYNTAX_ASSERT(paramvalc == 1, 183 "`request-timeout' must have just one argument"); 184 module->req_timeout = (int)strtonum(paramvalv[0], 0, 185 UINT16_MAX, &errmsg); 186 if (module->req_timeout == 0 && errmsg != NULL) { 187 module_send_message(module->base, IMSG_NG, 188 "`request-timeout must be 0-%d", UINT16_MAX); 189 return; 190 } 191 } else if (strcmp(paramname, "max-tries") == 0) { 192 SYNTAX_ASSERT(paramvalc == 1, 193 "`max-tries' must have just one argument"); 194 module->max_tries = (int)strtonum(paramvalv[0], 0, 195 UINT16_MAX, &errmsg); 196 if (module->max_tries == 0 && errmsg != NULL) { 197 module_send_message(module->base, IMSG_NG, 198 "`max-tries must be 0-%d", UINT16_MAX); 199 return; 200 } 201 202 } else if (strcmp(paramname, "max-failovers") == 0) { 203 SYNTAX_ASSERT(paramvalc == 1, 204 "`max-failovers' must have just one argument"); 205 module->max_failovers = (int)strtonum(paramvalv[0], 0, 206 UINT16_MAX, &errmsg); 207 if (module->max_failovers == 0 && errmsg != NULL) { 208 module_send_message(module->base, IMSG_NG, 209 "`max-failovers' must be 0-%d", UINT16_MAX); 210 return; 211 } 212 } else if (strcmp(paramname, "secret") == 0) { 213 SYNTAX_ASSERT(paramvalc == 1, 214 "`secret' must have just one argument"); 215 if (strlcpy(module->secret, paramvalv[0], 216 sizeof(module->secret)) >= sizeof(module->secret)) { 217 module_send_message(module->base, IMSG_NG, 218 "`secret' length must be 0-%lu", 219 (u_long) sizeof(module->secret) - 1); 220 return; 221 } 222 } else if (strcmp(paramname, "_debug") == 0) 223 log_init(1); 224 else if (strncmp(paramname, "_", 1) == 0) 225 /* nothing */; /* ignore all internal messages */ 226 else { 227 module_send_message(module->base, IMSG_NG, 228 "Unknown config parameter name `%s'", paramname); 229 return; 230 } 231 module_send_message(module->base, IMSG_OK, NULL); 232 233 return; 234 syntax_error: 235 module_send_message(module->base, IMSG_NG, "%s", errmsg); 236 } 237 238 static void 239 module_radius_start(void *ctx) 240 { 241 u_int i; 242 struct module_radius *module = ctx; 243 244 if (module->nserver <= 0) { 245 module_send_message(module->base, IMSG_NG, 246 "needs one `server' at least"); 247 return; 248 } 249 250 if (module->secret[0] == '\0') { 251 module_send_message(module->base, IMSG_NG, 252 "`secret' configuration is required"); 253 return; 254 } 255 256 for (i = 0; i < module->nserver; i++) { 257 module->server[i].module = module; 258 if (radius_server_start(&module->server[i]) != 0) { 259 module_send_message(module->base, IMSG_NG, 260 "module `radius' failed to start one of " 261 "the servers"); 262 return; 263 } 264 } 265 module_send_message(module->base, IMSG_OK, NULL); 266 267 module_notify_secret(module->base, module->secret); 268 } 269 270 static void 271 module_radius_stop(void *ctx) 272 { 273 u_int i; 274 struct module_radius_req *req, *treq; 275 struct module_radius *module = ctx; 276 277 TAILQ_FOREACH_SAFE(req, &module->req, next, treq) 278 module_radius_req_on_failure(req); 279 280 for (i = 0; i < module->nserver; i++) 281 radius_server_stop(&module->server[i]); 282 } 283 284 static void 285 module_radius_access_request(void *ctx, u_int q_id, const u_char *pkt, 286 size_t pktlen) 287 { 288 struct module_radius *module = ctx; 289 struct module_radius_req *req; 290 u_char attrbuf[256]; 291 ssize_t attrlen; 292 293 req = calloc(1, sizeof(struct module_radius_req)); 294 if (req == NULL) { 295 module_radius_log(module, LOG_WARNING, 296 "%s: Out of memory: %m", __func__); 297 goto on_fail; 298 } 299 300 req->ntry = 0; 301 req->module = module; 302 req->q_id = q_id; 303 if ((req->q_pkt = radius_convert_packet(pkt, pktlen)) == NULL) { 304 module_radius_log(module, LOG_WARNING, 305 "%s: radius_convert_packet() failed: %m", __func__); 306 goto on_fail; 307 } 308 evtimer_set(&req->ev, module_radius_req_on_timeout, req); 309 TAILQ_INSERT_TAIL(&req->module->req, req, next); 310 311 /* 312 * radiusd decrypt User-Password attribute. crypt it again with our 313 * secret. 314 */ 315 attrlen = sizeof(attrbuf); 316 if (radius_get_raw_attr(req->q_pkt, RADIUS_TYPE_USER_PASSWORD, 317 attrbuf, &attrlen) == 0) { 318 attrbuf[attrlen] = '\0'; 319 radius_del_attr_all(req->q_pkt, RADIUS_TYPE_USER_PASSWORD); 320 radius_put_user_password_attr(req->q_pkt, attrbuf, 321 module->secret); 322 } 323 324 /* select current server */ 325 module_radius_req_select_server(req); 326 327 module_radius_req_send(req); 328 329 return; 330 331 on_fail: 332 free(req); 333 module_accsreq_aborted(module->base, q_id); 334 } 335 336 /* 337 * radius_server 338 */ 339 static int 340 radius_server_start(struct radius_server *server) 341 { 342 socklen_t locallen; 343 char buf0[NI_MAXHOST + NI_MAXSERV + 32]; 344 char buf1[NI_MAXHOST + NI_MAXSERV + 32]; 345 346 if ((server->sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0)) 347 == -1) { 348 module_radius_log(server->module, LOG_WARNING, 349 "%s: socket() failed", __func__); 350 goto on_error; 351 } 352 if (connect(server->sock, (struct sockaddr *)&server->addr, 353 server->addr.sin4.sin_len) != 0) { 354 module_radius_log(server->module, LOG_WARNING, 355 "%s: connect to %s failed", __func__, 356 addrport_tostring((struct sockaddr *)&server->addr, 357 server->addr.sin4.sin_len, buf1, sizeof(buf1))); 358 goto on_error; 359 } 360 locallen = sizeof(server->local); 361 if (getsockname(server->sock, (struct sockaddr *)&server->local, 362 &locallen) != 0) { 363 module_radius_log(server->module, LOG_WARNING, 364 "%s: getsockanme() failed", __func__); 365 goto on_error; 366 } 367 module_radius_log(server->module, LOG_INFO, 368 "Use %s to send requests for %s", 369 addrport_tostring((struct sockaddr *)&server->local, 370 locallen, buf0, sizeof(buf0)), 371 addrport_tostring((struct sockaddr *)&server->addr, 372 server->addr.sin4.sin_len, buf1, sizeof(buf1))); 373 374 event_set(&server->ev, server->sock, EV_READ | EV_PERSIST, 375 radius_server_on_event, server); 376 if (event_add(&server->ev, NULL)) { 377 module_radius_log(server->module, LOG_WARNING, 378 "%s: event_add() failed", __func__); 379 goto on_error; 380 } 381 382 return (0); 383 on_error: 384 if (server->sock >= 0) 385 close(server->sock); 386 server->sock = -1; 387 return (-1); 388 } 389 390 static void 391 radius_server_stop(struct radius_server *server) 392 { 393 event_del(&server->ev); 394 if (server->sock >= 0) 395 close(server->sock); 396 server->sock = -1; 397 } 398 399 static void 400 radius_server_on_event(int fd, short evmask, void *ctx) 401 { 402 int sz, res_id; 403 u_char pkt[65535]; 404 char buf[NI_MAXHOST + NI_MAXSERV + 32]; 405 struct radius_server *server = ctx; 406 RADIUS_PACKET *radpkt = NULL; 407 struct module_radius_req *req; 408 struct sockaddr *peer; 409 410 peer = (struct sockaddr *)&server->addr; 411 if ((sz = recv(server->sock, pkt, sizeof(pkt), 0)) == -1) { 412 if (errno == EAGAIN) 413 return; 414 module_radius_log(server->module, LOG_WARNING, 415 "server=%s recv() failed: %m", 416 addrport_tostring(peer, peer->sa_len, buf, sizeof(buf))); 417 return; 418 } 419 if ((radpkt = radius_convert_packet(pkt, sz)) == NULL) { 420 module_radius_log(server->module, LOG_WARNING, 421 "server=%s could not convert the received message to a " 422 "RADIUS packet object: %m", 423 addrport_tostring(peer, peer->sa_len, buf, sizeof(buf))); 424 return; 425 } 426 res_id = radius_get_id(radpkt); 427 TAILQ_FOREACH(req, &server->module->req, next) { 428 if (req->server == server && req->req_id == res_id) 429 break; 430 } 431 if (req == NULL) { 432 module_radius_log(server->module, LOG_WARNING, 433 "server=%s Received radius message has unknown id=%d", 434 addrport_tostring(peer, peer->sa_len, buf, sizeof(buf)), 435 res_id); 436 goto out; 437 } 438 radius_set_request_packet(radpkt, req->q_pkt); 439 440 if (radius_check_response_authenticator(radpkt, 441 server->module->secret) != 0) { 442 module_radius_log(server->module, LOG_WARNING, 443 "server=%s Received radius message(id=%d) has bad " 444 "authenticator", 445 addrport_tostring(peer, peer->sa_len, buf, 446 sizeof(buf)), res_id); 447 goto out; 448 } 449 if (radius_has_attr(radpkt, 450 RADIUS_TYPE_MESSAGE_AUTHENTICATOR) && 451 radius_check_message_authenticator(radpkt, 452 server->module->secret) != 0) { 453 module_radius_log(server->module, LOG_WARNING, 454 "server=%s Received radius message(id=%d) has bad " 455 "message authenticator", 456 addrport_tostring(peer, peer->sa_len, buf, 457 sizeof(buf)), res_id); 458 goto out; 459 } 460 461 module_radius_log(server->module, LOG_INFO, 462 "q=%u received a response from server %s", req->q_id, 463 addrport_tostring(peer, peer->sa_len, buf, sizeof(buf))); 464 465 module_radius_req_on_success(req, radius_get_data(radpkt), 466 radius_get_length(radpkt)); 467 out: 468 if (radpkt != NULL) 469 radius_delete_packet(radpkt); 470 } 471 472 static void 473 radius_server_on_fail(struct radius_server *server, const char *failmsg) 474 { 475 char buf0[NI_MAXHOST + NI_MAXSERV + 32]; 476 char buf1[NI_MAXHOST + NI_MAXSERV + 32]; 477 struct sockaddr *caddr, *naddr; 478 479 caddr = (struct sockaddr *)&server->addr; 480 if (server->module->nserver <= 1) { 481 module_radius_log(server->module, LOG_WARNING, 482 "Server %s failed: %s", 483 addrport_tostring(caddr, caddr->sa_len, buf0, sizeof(buf0)), 484 failmsg); 485 return; 486 } 487 server->module->curr_server++; 488 server->module->curr_server %= server->module->nserver; 489 naddr = (struct sockaddr *) 490 &server->module->server[server->module->curr_server].addr; 491 492 module_radius_log(server->module, LOG_WARNING, 493 "Server %s failed: %s Fail over to %s", 494 addrport_tostring(caddr, caddr->sa_len, buf0, sizeof(buf0)), 495 failmsg, 496 addrport_tostring(naddr, naddr->sa_len, buf1, sizeof(buf1))); 497 } 498 499 /* module_radius_req */ 500 501 static void 502 module_radius_req_send(struct module_radius_req *req) 503 { 504 int sz; 505 struct sockaddr *peer; 506 char msg[BUFSIZ]; 507 508 peer = (struct sockaddr *)&req->server->addr; 509 if ((sz = send(req->server->sock, radius_get_data(req->q_pkt), 510 radius_get_length(req->q_pkt), 0)) < 0) { 511 module_radius_log(req->module, LOG_WARNING, 512 "Sending RADIUS query q=%u to %s failed: %m", 513 req->q_id, 514 addrport_tostring(peer, peer->sa_len, msg, sizeof(msg))); 515 /* retry anyway */ 516 } 517 module_radius_log(req->module, LOG_INFO, 518 "Send RADIUS query q=%u id=%d to %s successfully", 519 req->q_id, req->req_id, 520 addrport_tostring(peer, peer->sa_len, msg, sizeof(msg))); 521 if (module_radius_req_reset_event(req) != -1) 522 req->ntry++; 523 } 524 525 static int 526 module_radius_req_reset_event(struct module_radius_req *req) 527 { 528 struct timeval tv; 529 static int timeouts[] = { 2, 4, 8 }; 530 531 tv.tv_usec = 0; 532 if (req->module->req_timeout != 0) 533 tv.tv_sec = req->module->req_timeout; 534 else { 535 if (req->ntry < nitems(timeouts)) 536 tv.tv_sec = timeouts[req->ntry]; 537 else 538 tv.tv_sec = timeouts[nitems(timeouts) - 1]; 539 } 540 if (evtimer_add(&req->ev, &tv) != 0) { 541 module_radius_log(req->module, LOG_WARNING, 542 "Cannot process the request for q=%u: " 543 "evtimer_add() failed: %m", req->q_id); 544 module_radius_req_on_failure(req); 545 return (-1); 546 } 547 return (0); 548 } 549 550 static void 551 module_radius_req_on_timeout(int fd, short evmask, void *ctx) 552 { 553 struct module_radius_req *req = ctx; 554 char msg[BUFSIZ]; 555 556 557 if (req->module->max_tries <= req->ntry) { 558 snprintf(msg, sizeof(msg), "q=%u didn't response RADIUS query " 559 "%d time%s", req->q_id, req->ntry, 560 (req->ntry > 0)? "s" : ""); 561 radius_server_on_fail(req->server, msg); 562 if (++req->nfailover >= req->module->max_failovers) { 563 module_radius_log(req->module, 564 LOG_WARNING, "RADIUS query q=%u time out", 565 req->q_id); 566 module_radius_req_on_failure(req); 567 return; 568 } 569 /* select the next server */ 570 module_radius_req_select_server(req); 571 } 572 module_radius_req_send(req); 573 } 574 575 static void 576 module_radius_req_on_success(struct module_radius_req *req, 577 const u_char *pkt, size_t pktlen) 578 { 579 module_accsreq_answer(req->module->base, req->q_id, pkt, pktlen); 580 module_radius_req_free(req); 581 } 582 583 static void 584 module_radius_req_on_failure(struct module_radius_req *req) 585 { 586 module_accsreq_aborted(req->module->base, req->q_id); 587 module_radius_req_free(req); 588 } 589 590 591 static void 592 module_radius_req_free(struct module_radius_req *req) 593 { 594 evtimer_del(&req->ev); 595 TAILQ_REMOVE(&req->module->req, req, next); 596 if (req->q_pkt != NULL) 597 radius_delete_packet(req->q_pkt); 598 free(req); 599 } 600 601 static void 602 module_radius_req_select_server(struct module_radius_req *req) 603 { 604 req->server = &req->module->server[req->module->curr_server]; 605 req->ntry = 0; 606 req->req_id = req->server->req_id_seq++; 607 radius_set_id(req->q_pkt, req->req_id); 608 module_radius_req_reset_msgauth(req); 609 } 610 611 static void 612 module_radius_req_reset_msgauth(struct module_radius_req *req) 613 { 614 if (radius_has_attr(req->q_pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) 615 radius_del_attr_all(req->q_pkt, 616 RADIUS_TYPE_MESSAGE_AUTHENTICATOR); 617 radius_put_message_authenticator(req->q_pkt, 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