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