1 /* $OpenBSD: radius_req.c,v 1.3 2010/07/02 21:20:57 yasuoka 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.3 2010/07/02 21:20:57 yasuoka Exp $ 32 */ 33 #include <sys/types.h> 34 #include <sys/param.h> 35 #include <sys/time.h> 36 #include <sys/socket.h> 37 #include <netinet/in.h> 38 #include <unistd.h> 39 #include <stdio.h> 40 #include <syslog.h> 41 #include <stdlib.h> 42 #include <radius+.h> 43 #include <radiusconst.h> 44 #include <debugutil.h> 45 #include <time.h> 46 #include <event.h> 47 #include <string.h> 48 49 #include "radius_req.h" 50 51 struct overlapped { 52 struct event ev_sock; 53 int socket; 54 int ntry; 55 int timeout; 56 struct sockaddr_storage ss; 57 void *context; 58 radius_response *response_fn; 59 char secret[MAX_RADIUS_SECRET]; 60 RADIUS_PACKET *pkt; 61 }; 62 63 static int radius_request0 (struct overlapped *); 64 static void radius_request_io_event (int, short, void *); 65 static int select_srcaddr(struct sockaddr const *, struct sockaddr *, socklen_t *); 66 67 #ifdef RADIUS_REQ_DEBUG 68 #define RADIUS_REQ_ASSERT(cond) \ 69 if (!(cond)) { \ 70 fprintf(stderr, \ 71 "\nASSERT(" #cond ") failed on %s() at %s:%d.\n"\ 72 , __func__, __FILE__, __LINE__); \ 73 abort(); \ 74 } 75 #else 76 #define RADIUS_REQ_ASSERT(cond) 77 #endif 78 79 /** 80 * Send RADIUS request message. The pkt(RADIUS packet) will be released 81 * by this implementation. 82 */ 83 void 84 radius_request(RADIUS_REQUEST_CTX ctx, RADIUS_PACKET *pkt) 85 { 86 struct overlapped *lap; 87 88 RADIUS_REQ_ASSERT(pkt != NULL); 89 RADIUS_REQ_ASSERT(ctx != NULL); 90 lap = ctx; 91 lap->pkt = pkt; 92 if (radius_request0(lap) != 0) { 93 if (lap->response_fn != NULL) 94 lap->response_fn(lap->context, NULL, 95 RADIUS_REQUST_ERROR); 96 } 97 } 98 99 /** 100 * Prepare NAS-IP-Address or NAS-IPv6-Address. If 101 * setting->server[setting->curr_server].sock is not initialized, address 102 * will be selected automatically. 103 */ 104 int 105 radius_prepare_nas_address(radius_req_setting *setting, 106 RADIUS_PACKET *pkt) 107 { 108 int af; 109 struct sockaddr_in *sin4; 110 struct sockaddr_in6 *sin6; 111 socklen_t socklen; 112 113 /* See RFC 2765, 3162 */ 114 RADIUS_REQ_ASSERT(setting != NULL); 115 116 af = setting->server[setting->curr_server].peer.sin6.sin6_family; 117 RADIUS_REQ_ASSERT(af == AF_INET6 || af == AF_INET); 118 119 sin4 = &setting->server[setting->curr_server].sock.sin4; 120 sin6 = &setting->server[setting->curr_server].sock.sin6; 121 122 switch (af) { 123 case AF_INET: 124 socklen = sizeof(*sin4); 125 if (sin4->sin_addr.s_addr == INADDR_ANY) { 126 if (select_srcaddr((struct sockaddr const *) 127 &setting->server[setting->curr_server].peer, 128 (struct sockaddr *)sin4, &socklen) != 0) { 129 RADIUS_REQ_ASSERT("NOTREACHED" == NULL); 130 goto fail; 131 } 132 } 133 if (radius_put_ipv4_attr(pkt, RADIUS_TYPE_NAS_IP_ADDRESS, 134 sin4->sin_addr) != 0) 135 goto fail; 136 break; 137 case AF_INET6: 138 socklen = sizeof(*sin6); 139 if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) { 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_raw_attr(pkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, 148 sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr.s6_addr)) 149 != 0) 150 goto fail; 151 break; 152 } 153 154 return 0; 155 fail: 156 return 1; 157 } 158 159 /** 160 * Prepare sending RADIUS request. This implementation will call back to 161 * notice that it receives the response or it fails for timeouts to the 162 * The context that is set as 'pctx' and response packet that is given 163 * by the callback function will be released by this implementation internally. 164 * @param setting Setting for RADIUS server or request. 165 * @param context Context for the caller. 166 * @param pctx Pointer to the space for context of RADIUS request 167 * (RADIUS_REQUEST_CTX). This will be used for canceling. 168 * NULL can be specified when you don't need. 169 * @param response_fn Specify callback function as a pointer. The function 170 * will be called when it receives a response or when 171 * request fails for timeouts. 172 * @param timeout response timeout in second. 173 */ 174 int 175 radius_prepare(radius_req_setting *setting, void *context, 176 RADIUS_REQUEST_CTX *pctx, radius_response response_fn, int timeout) 177 { 178 int sock; 179 struct overlapped *lap; 180 struct sockaddr_in6 *sin6; 181 182 RADIUS_REQ_ASSERT(setting != NULL); 183 lap = NULL; 184 185 if (setting->server[setting->curr_server].enabled == 0) 186 return 1; 187 if ((lap = malloc(sizeof(struct overlapped))) == NULL) { 188 log_printf(LOG_ERR, "malloc() failed in %s: %m", __func__); 189 goto fail; 190 } 191 sin6 = &setting->server[setting->curr_server].peer.sin6; 192 if ((sock = socket(sin6->sin6_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) { 193 log_printf(LOG_ERR, "socket() failed in %s: %m", __func__); 194 goto fail; 195 } 196 memset(lap, 0, sizeof(struct overlapped)); 197 memcpy(&lap->ss, &setting->server[setting->curr_server].peer, 198 setting->server[setting->curr_server].peer.sin6.sin6_len); 199 200 lap->socket = sock; 201 lap->timeout = MIN(setting->timeout, timeout); 202 lap->ntry = timeout / lap->timeout; 203 lap->context = context; 204 lap->response_fn = response_fn; 205 memcpy(lap->secret, setting->server[setting->curr_server].secret, 206 sizeof(lap->secret)); 207 if (pctx != NULL) 208 *pctx = lap; 209 210 return 0; 211 fail: 212 if (lap != NULL) 213 free(lap); 214 215 return 1; 216 } 217 218 /** 219 * Cancel the RADIUS request. 220 * @param The context received by {@link radius_request()} 221 */ 222 void 223 radius_cancel_request(RADIUS_REQUEST_CTX ctx) 224 { 225 struct overlapped *lap; 226 227 lap = ctx; 228 if (lap->socket >= 0) { 229 event_del(&lap->ev_sock); 230 close(lap->socket); 231 lap->socket = -1; 232 } 233 if (lap->pkt != NULL) { 234 radius_delete_packet(lap->pkt); 235 lap->pkt = NULL; 236 } 237 memset(lap->secret, 0x41, sizeof(lap->secret)); 238 239 free(lap); 240 } 241 242 /** Return the shared secret for RADIUS server that is used by this context. */ 243 const char * 244 radius_get_server_secret(RADIUS_REQUEST_CTX ctx) 245 { 246 struct overlapped *lap; 247 248 lap = ctx; 249 RADIUS_REQ_ASSERT(lap != NULL); 250 251 return lap->secret; 252 } 253 254 /** Return the address of RADIUS server that is used by this context. */ 255 struct sockaddr * 256 radius_get_server_address(RADIUS_REQUEST_CTX ctx) 257 { 258 struct overlapped *lap; 259 260 lap = ctx; 261 RADIUS_REQ_ASSERT(lap != NULL); 262 263 return (struct sockaddr *)&lap->ss; 264 } 265 266 static int 267 radius_request0(struct overlapped *lap) 268 { 269 struct timeval tv0; 270 271 RADIUS_REQ_ASSERT(lap->ntry > 0); 272 273 lap->ntry--; 274 if (radius_sendto(lap->socket, lap->pkt, 0, (struct sockaddr *) 275 &lap->ss, lap->ss.ss_len) != 0) 276 return 1; 277 tv0.tv_usec = 0; 278 tv0.tv_sec = lap->timeout; 279 280 event_set(&lap->ev_sock, lap->socket, EV_READ, 281 radius_request_io_event, lap); 282 event_add(&lap->ev_sock, &tv0); 283 284 return 0; 285 } 286 287 static void 288 radius_request_io_event(int fd, short evmask, void *context) 289 { 290 struct overlapped *lap; 291 struct sockaddr_storage ss; 292 int flags; 293 socklen_t len; 294 RADIUS_PACKET *respkt; 295 296 RADIUS_REQ_ASSERT(context != NULL); 297 298 if ((evmask & EV_READ) != 0) { 299 lap = context; 300 flags = 0; 301 302 RADIUS_REQ_ASSERT(lap->socket >= 0); 303 if (lap->socket < 0) 304 return; 305 RADIUS_REQ_ASSERT(lap->pkt != NULL); 306 307 memset(&ss, 0, sizeof(ss)); 308 len = sizeof(ss); 309 if ((respkt = radius_recvfrom(lap->socket, 0, 310 (struct sockaddr *)&ss, &len)) == NULL) { 311 log_printf(LOG_ERR, "recvfrom() failed in %s: %m", 312 __func__); 313 flags |= RADIUS_REQUST_ERROR; 314 } else if (lap->secret[0] == '\0') { 315 flags |= RADIUS_REQUST_CHECK_AUTHENTICTOR_NO_CHECK; 316 } else { 317 radius_set_request_packet(respkt, lap->pkt); 318 if (!radius_check_response_authenticator(respkt, 319 lap->secret)) 320 flags |= RADIUS_REQUST_CHECK_AUTHENTICTOR_OK; 321 } 322 323 if (lap->response_fn != NULL) 324 lap->response_fn(lap->context, respkt, flags); 325 326 if (respkt != NULL) 327 radius_delete_packet(respkt); 328 radius_cancel_request(lap); 329 } else if ((evmask & EV_TIMEOUT) != 0) { 330 lap = context; 331 if (lap->ntry > 0) { 332 if (radius_request0(lap) != 0) { 333 if (lap->response_fn != NULL) 334 lap->response_fn(lap->context, NULL, 335 RADIUS_REQUST_ERROR); 336 radius_cancel_request(lap); 337 } 338 return; 339 } 340 if (lap->response_fn != NULL) 341 lap->response_fn(lap->context, NULL, 342 RADIUS_REQUST_TIMEOUT); 343 radius_cancel_request(lap); 344 } 345 } 346 347 static int 348 select_srcaddr(struct sockaddr const *dst, struct sockaddr *src, 349 socklen_t *srclen) 350 { 351 int sock; 352 353 sock = -1; 354 if ((sock = socket(dst->sa_family, SOCK_DGRAM, IPPROTO_UDP)) < 0) 355 goto fail; 356 if (connect(sock, dst, dst->sa_len) != 0) 357 goto fail; 358 if (getsockname(sock, src, srclen) != 0) 359 goto fail; 360 361 close(sock); 362 363 return 0; 364 fail: 365 if (sock >= 0) 366 close(sock); 367 368 return 1; 369 } 370