1 /* $OpenBSD: radius_req.c,v 1.11 2015/12/05 18:43:36 mmcc Exp $ */ 2 3 /*- 4 * Copyright (c) 2009 Internet Initiative Japan Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 /**@file 29 * This file provides functions for RADIUS request using radius(3) and event(3). 30 * @author Yasuoka Masahiko 31 * $Id: radius_req.c,v 1.11 2015/12/05 18:43:36 mmcc Exp $ 32 */ 33 #include <sys/types.h> 34 #include <sys/time.h> 35 #include <sys/socket.h> 36 #include <netinet/in.h> 37 #include <unistd.h> 38 #include <stdio.h> 39 #include <syslog.h> 40 #include <stdlib.h> 41 #include <debugutil.h> 42 #include <time.h> 43 #include <event.h> 44 #include <string.h> 45 #include <errno.h> 46 47 #include "radius_req.h" 48 #include <radius.h> 49 50 #ifndef nitems 51 #define nitems(_a) (sizeof((_a)) / sizeof((_a)[0])) 52 #endif 53 54 struct overlapped { 55 struct event ev_sock; 56 int socket; 57 int ntry; 58 int max_tries; 59 int failovers; 60 int acct_delay_time; 61 int response_fn_calling; 62 struct sockaddr_storage ss; 63 struct timespec req_time; 64 void *context; 65 radius_response *response_fn; 66 char secret[MAX_RADIUS_SECRET]; 67 RADIUS_PACKET *pkt; 68 radius_req_setting *setting; 69 }; 70 71 static int radius_request0 (struct overlapped *, int); 72 static int radius_prepare_socket(struct overlapped *); 73 static void radius_request_io_event (int, short, void *); 74 static void radius_on_response(RADIUS_REQUEST_CTX, RADIUS_PACKET *, int, int); 75 static int select_srcaddr(struct sockaddr const *, struct sockaddr *, socklen_t *); 76 static void radius_req_setting_ref(radius_req_setting *); 77 static void radius_req_setting_unref(radius_req_setting *); 78 79 #ifdef RADIUS_REQ_DEBUG 80 #define RADIUS_REQ_DBG(x) log_printf x 81 #define RADIUS_REQ_ASSERT(cond) \ 82 if (!(cond)) { \ 83 fprintf(stderr, \ 84 "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ 85 , __func__, __FILE__, __LINE__); \ 86 abort(); \ 87 } 88 #else 89 #define RADIUS_REQ_ASSERT(cond) 90 #define RADIUS_REQ_DBG(x) 91 #endif 92 93 /** 94 * Send RADIUS request message. The pkt(RADIUS packet) will be released 95 * by this implementation. 96 */ 97 void 98 radius_request(RADIUS_REQUEST_CTX ctx, RADIUS_PACKET *pkt) 99 { 100 uint32_t ival; 101 struct overlapped *lap; 102 103 RADIUS_REQ_ASSERT(pkt != NULL); 104 RADIUS_REQ_ASSERT(ctx != NULL); 105 lap = ctx; 106 lap->pkt = pkt; 107 if (radius_get_uint32_attr(pkt, RADIUS_TYPE_ACCT_DELAY_TIME, &ival) 108 == 0) 109 lap->acct_delay_time = 1; 110 radius_request0(lap, 0); 111 } 112 113 /** 114 * Prepare NAS-IP-Address or NAS-IPv6-Address. If 115 * setting->server[setting->curr_server].sock is not initialized, address 116 * will be selected automatically. 117 */ 118 int 119 radius_prepare_nas_address(radius_req_setting *setting, 120 RADIUS_PACKET *pkt) 121 { 122 int af; 123 struct sockaddr_in *sin4; 124 struct sockaddr_in6 *sin6; 125 socklen_t socklen; 126 127 /* See RFC 2765, 3162 */ 128 RADIUS_REQ_ASSERT(setting != NULL); 129 130 af = setting->server[setting->curr_server].peer.sin6.sin6_family; 131 RADIUS_REQ_ASSERT(af == AF_INET6 || af == AF_INET); 132 133 sin4 = &setting->server[setting->curr_server].sock.sin4; 134 sin6 = &setting->server[setting->curr_server].sock.sin6; 135 136 switch (af) { 137 case AF_INET: 138 socklen = sizeof(*sin4); 139 if (sin4->sin_addr.s_addr == INADDR_ANY) { 140 if (select_srcaddr((struct sockaddr const *) 141 &setting->server[setting->curr_server].peer, 142 (struct sockaddr *)sin4, &socklen) != 0) { 143 RADIUS_REQ_ASSERT("NOTREACHED" == NULL); 144 goto fail; 145 } 146 } 147 if (radius_put_ipv4_attr(pkt, RADIUS_TYPE_NAS_IP_ADDRESS, 148 sin4->sin_addr) != 0) 149 goto fail; 150 break; 151 case AF_INET6: 152 socklen = sizeof(*sin6); 153 if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 154 if (select_srcaddr((struct sockaddr const *) 155 &setting->server[setting->curr_server].peer, 156 (struct sockaddr *)sin4, &socklen) != 0) { 157 RADIUS_REQ_ASSERT("NOTREACHED" == NULL); 158 goto fail; 159 } 160 } 161 if (radius_put_raw_attr(pkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, 162 sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr.s6_addr)) 163 != 0) 164 goto fail; 165 break; 166 } 167 168 return 0; 169 fail: 170 return 1; 171 } 172 173 174 /** Checks whether the request can fail over to another server */ 175 int 176 radius_request_can_failover(RADIUS_REQUEST_CTX ctx) 177 { 178 struct overlapped *lap; 179 radius_req_setting *setting; 180 181 lap = ctx; 182 setting = lap->setting; 183 184 if (lap->failovers >= setting->max_failovers) 185 return 0; 186 if (memcmp(&lap->ss, &setting->server[setting->curr_server].peer, 187 setting->server[setting->curr_server].peer.sin6.sin6_len) == 0) 188 /* flagged server doesn't differ from the last server. */ 189 return 0; 190 191 return 1; 192 } 193 194 /** Send RADIUS request failing over to another server. */ 195 int 196 radius_request_failover(RADIUS_REQUEST_CTX ctx) 197 { 198 struct overlapped *lap; 199 200 lap = ctx; 201 RADIUS_REQ_ASSERT(lap != NULL); 202 RADIUS_REQ_ASSERT(lap->socket >= 0) 203 204 if (!radius_request_can_failover(lap)) 205 return -1; 206 207 if (radius_prepare_socket(lap) != 0) 208 return -1; 209 210 if (radius_request0(lap, 1) != 0) 211 return -1; 212 213 lap->failovers++; 214 215 return 0; 216 } 217 218 static int 219 radius_prepare_socket(struct overlapped *lap) 220 { 221 int sock; 222 radius_req_setting *setting; 223 struct sockaddr *sa; 224 225 setting = lap->setting; 226 if (lap->socket >= 0) 227 close(lap->socket); 228 lap->socket = -1; 229 230 sa = (struct sockaddr *)&setting->server[setting->curr_server].peer; 231 232 if ((sock = socket(sa->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) { 233 log_printf(LOG_ERR, "socket() failed in %s: %m", __func__); 234 return -1; 235 } 236 if (connect(sock, sa, sa->sa_len) != 0) { 237 log_printf(LOG_ERR, "connect() failed in %s: %m", __func__); 238 close(sock); 239 return -1; 240 } 241 memcpy(&lap->ss, sa, sa->sa_len); 242 lap->socket = sock; 243 memcpy(lap->secret, setting->server[setting->curr_server].secret, 244 sizeof(lap->secret)); 245 lap->ntry = lap->max_tries; 246 247 return 0; 248 } 249 250 /** 251 * Prepare sending RADIUS request. This implementation will call back to 252 * notice that it receives the response or it fails for timeouts to the 253 * The context that is set as 'pctx' and response packet that is given 254 * by the callback function will be released by this implementation internally. 255 * @param setting Setting for RADIUS server or request. 256 * @param context Context for the caller. 257 * @param pctx Pointer to the space for context of RADIUS request 258 * (RADIUS_REQUEST_CTX). This will be used for canceling. 259 * NULL can be specified when you don't need. 260 * @param response_fn Specify callback function as a pointer. The function 261 * will be called when it receives a response or when 262 * request fails for timeouts. 263 * @param timeout response timeout in second. 264 */ 265 int 266 radius_prepare(radius_req_setting *setting, void *context, 267 RADIUS_REQUEST_CTX *pctx, radius_response response_fn) 268 { 269 struct overlapped *lap; 270 271 RADIUS_REQ_ASSERT(setting != NULL); 272 lap = NULL; 273 274 if (setting->server[setting->curr_server].enabled == 0) 275 return 1; 276 if ((lap = calloc(1, sizeof(struct overlapped))) == NULL) { 277 log_printf(LOG_ERR, "calloc() failed in %s: %m", __func__); 278 goto fail; 279 } 280 lap->context = context; 281 lap->response_fn = response_fn; 282 lap->socket = -1; 283 lap->setting = setting; 284 285 lap->max_tries = setting->max_tries; 286 if (lap->max_tries <= 0) 287 lap->max_tries = 3; /* default max tries */ 288 289 if (radius_prepare_socket(lap) != 0) 290 goto fail; 291 292 if (pctx != NULL) 293 *pctx = lap; 294 295 radius_req_setting_ref(setting); 296 297 return 0; 298 fail: 299 free(lap); 300 301 return 1; 302 } 303 304 /** 305 * Cancel the RADIUS request. 306 * @param The context received by {@link radius_request()} 307 */ 308 void 309 radius_cancel_request(RADIUS_REQUEST_CTX ctx) 310 { 311 struct overlapped *lap = ctx; 312 313 /* 314 * Don't call this function from the callback function. 315 * The context will be freed after the callback function is called. 316 */ 317 RADIUS_REQ_ASSERT(lap->response_fn_calling == 0); 318 if (lap->response_fn_calling != 0) 319 return; 320 321 if (lap->socket >= 0) { 322 event_del(&lap->ev_sock); 323 close(lap->socket); 324 lap->socket = -1; 325 } 326 if (lap->pkt != NULL) { 327 radius_delete_packet(lap->pkt); 328 lap->pkt = NULL; 329 } 330 radius_req_setting_unref(lap->setting); 331 332 memset(lap->secret, 0x41, sizeof(lap->secret)); 333 334 free(lap); 335 } 336 337 /** Return the shared secret for RADIUS server that is used by this context. */ 338 const char * 339 radius_get_server_secret(RADIUS_REQUEST_CTX ctx) 340 { 341 struct overlapped *lap; 342 343 lap = ctx; 344 RADIUS_REQ_ASSERT(lap != NULL); 345 346 return lap->secret; 347 } 348 349 /** Return the address of RADIUS server that is used by this context. */ 350 struct sockaddr * 351 radius_get_server_address(RADIUS_REQUEST_CTX ctx) 352 { 353 struct overlapped *lap; 354 355 lap = ctx; 356 RADIUS_REQ_ASSERT(lap != NULL); 357 358 return (struct sockaddr *)&lap->ss; 359 } 360 361 static int 362 radius_request0(struct overlapped *lap, int new_message) 363 { 364 struct timeval tv0; 365 366 RADIUS_REQ_ASSERT(lap->ntry > 0); 367 368 if (lap->acct_delay_time != 0) { 369 struct timespec curr, delta; 370 371 if (clock_gettime(CLOCK_MONOTONIC, &curr) != 0) { 372 log_printf(LOG_CRIT, 373 "clock_gettime(CLOCK_MONOTONIC,) failed: %m"); 374 RADIUS_REQ_ASSERT(0); 375 } 376 if (!timespecisset(&lap->req_time)) 377 lap->req_time = curr; 378 else { 379 timespecsub(&curr, &lap->req_time, &delta); 380 if (radius_set_uint32_attr(lap->pkt, 381 RADIUS_TYPE_ACCT_DELAY_TIME, delta.tv_sec) == 0) { 382 radius_update_id(lap->pkt); 383 new_message = 1; 384 } 385 } 386 } 387 if (new_message) { 388 radius_set_accounting_request_authenticator(lap->pkt, 389 radius_get_server_secret(lap)); 390 } 391 392 lap->ntry--; 393 if (radius_send(lap->socket, lap->pkt, 0) != 0) { 394 log_printf(LOG_ERR, "sendto() failed in %s: %m", 395 __func__); 396 radius_on_response(lap, NULL, RADIUS_REQUEST_ERROR, 1); 397 return 1; 398 } 399 tv0.tv_usec = 0; 400 tv0.tv_sec = lap->setting->timeout; 401 402 event_set(&lap->ev_sock, lap->socket, EV_READ | EV_PERSIST, 403 radius_request_io_event, lap); 404 event_add(&lap->ev_sock, &tv0); 405 406 return 0; 407 } 408 409 static void 410 radius_request_io_event(int fd, short evmask, void *context) 411 { 412 struct overlapped *lap; 413 struct sockaddr_storage ss; 414 int flags; 415 socklen_t len; 416 RADIUS_PACKET *respkt; 417 418 RADIUS_REQ_ASSERT(context != NULL); 419 420 lap = context; 421 respkt = NULL; 422 flags = 0; 423 if ((evmask & EV_READ) != 0) { 424 RADIUS_REQ_ASSERT(lap->socket >= 0); 425 if (lap->socket < 0) 426 return; 427 RADIUS_REQ_ASSERT(lap->pkt != NULL); 428 memset(&ss, 0, sizeof(ss)); 429 len = sizeof(ss); 430 if ((respkt = radius_recv(lap->socket, 0)) == NULL) { 431 RADIUS_REQ_DBG((LOG_DEBUG, 432 "radius_recv() on %s(): %m", __func__)); 433 /* 434 * Ignore error by icmp. Wait a response from the 435 * server anyway, it may eventually become ready. 436 */ 437 switch (errno) { 438 case EHOSTDOWN: case EHOSTUNREACH: case ECONNREFUSED: 439 return; /* sleep the rest of timeout time */ 440 } 441 flags |= RADIUS_REQUEST_ERROR; 442 } else if (lap->secret[0] == '\0') { 443 flags |= RADIUS_REQUEST_CHECK_AUTHENTICATOR_NO_CHECK; 444 } else { 445 radius_set_request_packet(respkt, lap->pkt); 446 if (!radius_check_response_authenticator(respkt, 447 lap->secret)) 448 flags |= RADIUS_REQUEST_CHECK_AUTHENTICATOR_OK; 449 } 450 radius_on_response(lap, respkt, flags, 0); 451 radius_delete_packet(respkt); 452 } else if ((evmask & EV_TIMEOUT) != 0) { 453 if (lap->ntry > 0) { 454 RADIUS_REQ_DBG((LOG_DEBUG, 455 "%s() timed out retry", __func__)); 456 radius_request0(lap, 0); 457 return; 458 } 459 RADIUS_REQ_DBG((LOG_DEBUG, "%s() timed out", __func__)); 460 flags |= RADIUS_REQUEST_TIMEOUT; 461 radius_on_response(lap, NULL, flags, 1); 462 } 463 } 464 465 static void 466 radius_on_response(RADIUS_REQUEST_CTX ctx, RADIUS_PACKET *pkt, int flags, 467 int server_failure) 468 { 469 struct overlapped *lap; 470 int failovers; 471 472 lap = ctx; 473 if (server_failure) { 474 int i, n; 475 struct sockaddr *sa_curr; 476 477 sa_curr = (struct sockaddr *)&lap->setting->server[ 478 lap->setting->curr_server].peer; 479 if (sa_curr->sa_len == lap->ss.ss_len && 480 memcmp(sa_curr, &lap->ss, sa_curr->sa_len) == 0) { 481 /* 482 * The server on failure is flagged as the current. 483 * change the current 484 */ 485 for (i = 1; i < nitems(lap->setting->server); i++) { 486 n = (lap->setting->curr_server + i) % 487 nitems(lap->setting->server); 488 if (lap->setting->server[n].enabled) { 489 lap->setting->curr_server = n; 490 break; 491 } 492 } 493 } 494 } 495 496 failovers = lap->failovers; 497 if (lap->response_fn != NULL) { 498 lap->response_fn_calling++; 499 lap->response_fn(lap->context, pkt, flags, ctx); 500 lap->response_fn_calling--; 501 } 502 if (failovers == lap->failovers) 503 radius_cancel_request(lap); 504 } 505 506 static int 507 select_srcaddr(struct sockaddr const *dst, struct sockaddr *src, 508 socklen_t *srclen) 509 { 510 int sock; 511 512 sock = -1; 513 if ((sock = socket(dst->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) 514 goto fail; 515 if (connect(sock, dst, dst->sa_len) != 0) 516 goto fail; 517 if (getsockname(sock, src, srclen) != 0) 518 goto fail; 519 520 close(sock); 521 522 return 0; 523 fail: 524 if (sock >= 0) 525 close(sock); 526 527 return 1; 528 } 529 530 radius_req_setting * 531 radius_req_setting_create(void) 532 { 533 return calloc(1, sizeof(radius_req_setting)); 534 } 535 536 int 537 radius_req_setting_has_server(radius_req_setting *setting) 538 { 539 return setting->server[setting->curr_server].enabled; 540 } 541 542 void 543 radius_req_setting_destroy(radius_req_setting *setting) 544 { 545 setting->destroyed = 1; 546 547 if (setting->refcnt == 0) 548 free(setting); 549 } 550 551 static void 552 radius_req_setting_ref(radius_req_setting *setting) 553 { 554 setting->refcnt++; 555 } 556 557 static void 558 radius_req_setting_unref(radius_req_setting *setting) 559 { 560 setting->refcnt--; 561 if (setting->destroyed) 562 radius_req_setting_destroy(setting); 563 } 564