1/* 2 * RADIUS Dynamic Authorization Server (DAS) (RFC 5176) 3 * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> 4 * 5 * This software may be distributed under the terms of the BSD license. 6 * See README for more details. 7 */ 8 9#include "includes.h" 10#include <net/if.h> 11 12#include "utils/common.h" 13#include "utils/eloop.h" 14#include "utils/ip_addr.h" 15#include "radius.h" 16#include "radius_das.h" 17 18 19struct radius_das_data { 20 int sock; 21 u8 *shared_secret; 22 size_t shared_secret_len; 23 struct hostapd_ip_addr client_addr; 24 unsigned int time_window; 25 int require_event_timestamp; 26 void *ctx; 27 enum radius_das_res (*disconnect)(void *ctx, 28 struct radius_das_attrs *attr); 29}; 30 31 32static struct radius_msg * radius_das_disconnect(struct radius_das_data *das, 33 struct radius_msg *msg, 34 const char *abuf, 35 int from_port) 36{ 37 struct radius_hdr *hdr; 38 struct radius_msg *reply; 39 u8 allowed[] = { 40 RADIUS_ATTR_USER_NAME, 41 RADIUS_ATTR_NAS_IP_ADDRESS, 42 RADIUS_ATTR_CALLING_STATION_ID, 43 RADIUS_ATTR_NAS_IDENTIFIER, 44 RADIUS_ATTR_ACCT_SESSION_ID, 45 RADIUS_ATTR_ACCT_MULTI_SESSION_ID, 46 RADIUS_ATTR_EVENT_TIMESTAMP, 47 RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 48 RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 49#ifdef CONFIG_IPV6 50 RADIUS_ATTR_NAS_IPV6_ADDRESS, 51#endif /* CONFIG_IPV6 */ 52 0 53 }; 54 int error = 405; 55 u8 attr; 56 enum radius_das_res res; 57 struct radius_das_attrs attrs; 58 u8 *buf; 59 size_t len; 60 char tmp[100]; 61 u8 sta_addr[ETH_ALEN]; 62 63 hdr = radius_msg_get_hdr(msg); 64 65 attr = radius_msg_find_unlisted_attr(msg, allowed); 66 if (attr) { 67 wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in " 68 "Disconnect-Request from %s:%d", attr, 69 abuf, from_port); 70 error = 401; 71 goto fail; 72 } 73 74 os_memset(&attrs, 0, sizeof(attrs)); 75 76 if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, 77 &buf, &len, NULL) == 0) { 78 if (len != 4) { 79 wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d", 80 abuf, from_port); 81 error = 407; 82 goto fail; 83 } 84 attrs.nas_ip_addr = buf; 85 } 86 87#ifdef CONFIG_IPV6 88 if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, 89 &buf, &len, NULL) == 0) { 90 if (len != 16) { 91 wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d", 92 abuf, from_port); 93 error = 407; 94 goto fail; 95 } 96 attrs.nas_ipv6_addr = buf; 97 } 98#endif /* CONFIG_IPV6 */ 99 100 if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER, 101 &buf, &len, NULL) == 0) { 102 attrs.nas_identifier = buf; 103 attrs.nas_identifier_len = len; 104 } 105 106 if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, 107 &buf, &len, NULL) == 0) { 108 if (len >= sizeof(tmp)) 109 len = sizeof(tmp) - 1; 110 os_memcpy(tmp, buf, len); 111 tmp[len] = '\0'; 112 if (hwaddr_aton2(tmp, sta_addr) < 0) { 113 wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id " 114 "'%s' from %s:%d", tmp, abuf, from_port); 115 error = 407; 116 goto fail; 117 } 118 attrs.sta_addr = sta_addr; 119 } 120 121 if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, 122 &buf, &len, NULL) == 0) { 123 attrs.user_name = buf; 124 attrs.user_name_len = len; 125 } 126 127 if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID, 128 &buf, &len, NULL) == 0) { 129 attrs.acct_session_id = buf; 130 attrs.acct_session_id_len = len; 131 } 132 133 if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, 134 &buf, &len, NULL) == 0) { 135 attrs.acct_multi_session_id = buf; 136 attrs.acct_multi_session_id_len = len; 137 } 138 139 if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 140 &buf, &len, NULL) == 0) { 141 attrs.cui = buf; 142 attrs.cui_len = len; 143 } 144 145 res = das->disconnect(das->ctx, &attrs); 146 switch (res) { 147 case RADIUS_DAS_NAS_MISMATCH: 148 wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d", 149 abuf, from_port); 150 error = 403; 151 break; 152 case RADIUS_DAS_SESSION_NOT_FOUND: 153 wpa_printf(MSG_INFO, "DAS: Session not found for request from " 154 "%s:%d", abuf, from_port); 155 error = 503; 156 break; 157 case RADIUS_DAS_MULTI_SESSION_MATCH: 158 wpa_printf(MSG_INFO, 159 "DAS: Multiple sessions match for request from %s:%d", 160 abuf, from_port); 161 error = 508; 162 break; 163 case RADIUS_DAS_SUCCESS: 164 error = 0; 165 break; 166 } 167 168fail: 169 reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK : 170 RADIUS_CODE_DISCONNECT_ACK, hdr->identifier); 171 if (reply == NULL) 172 return NULL; 173 174 if (error) { 175 if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 176 error)) { 177 radius_msg_free(reply); 178 return NULL; 179 } 180 } 181 182 return reply; 183} 184 185 186static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) 187{ 188 struct radius_das_data *das = eloop_ctx; 189 u8 buf[1500]; 190 union { 191 struct sockaddr_storage ss; 192 struct sockaddr_in sin; 193#ifdef CONFIG_IPV6 194 struct sockaddr_in6 sin6; 195#endif /* CONFIG_IPV6 */ 196 } from; 197 char abuf[50]; 198 int from_port = 0; 199 socklen_t fromlen; 200 int len; 201 struct radius_msg *msg, *reply = NULL; 202 struct radius_hdr *hdr; 203 struct wpabuf *rbuf; 204 u32 val; 205 int res; 206 struct os_time now; 207 208 fromlen = sizeof(from); 209 len = recvfrom(sock, buf, sizeof(buf), 0, 210 (struct sockaddr *) &from.ss, &fromlen); 211 if (len < 0) { 212 wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno)); 213 return; 214 } 215 216 os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); 217 from_port = ntohs(from.sin.sin_port); 218 219 wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d", 220 len, abuf, from_port); 221 if (das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) { 222 wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client"); 223 return; 224 } 225 226 msg = radius_msg_parse(buf, len); 227 if (msg == NULL) { 228 wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet " 229 "from %s:%d failed", abuf, from_port); 230 return; 231 } 232 233 if (wpa_debug_level <= MSG_MSGDUMP) 234 radius_msg_dump(msg); 235 236 if (radius_msg_verify_das_req(msg, das->shared_secret, 237 das->shared_secret_len)) { 238 wpa_printf(MSG_DEBUG, "DAS: Invalid authenticator in packet " 239 "from %s:%d - drop", abuf, from_port); 240 goto fail; 241 } 242 243 os_get_time(&now); 244 res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP, 245 (u8 *) &val, 4); 246 if (res == 4) { 247 u32 timestamp = ntohl(val); 248 if ((unsigned int) abs((int) (now.sec - timestamp)) > 249 das->time_window) { 250 wpa_printf(MSG_DEBUG, "DAS: Unacceptable " 251 "Event-Timestamp (%u; local time %u) in " 252 "packet from %s:%d - drop", 253 timestamp, (unsigned int) now.sec, 254 abuf, from_port); 255 goto fail; 256 } 257 } else if (das->require_event_timestamp) { 258 wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet " 259 "from %s:%d - drop", abuf, from_port); 260 goto fail; 261 } 262 263 hdr = radius_msg_get_hdr(msg); 264 265 switch (hdr->code) { 266 case RADIUS_CODE_DISCONNECT_REQUEST: 267 reply = radius_das_disconnect(das, msg, abuf, from_port); 268 break; 269 case RADIUS_CODE_COA_REQUEST: 270 /* TODO */ 271 reply = radius_msg_new(RADIUS_CODE_COA_NAK, 272 hdr->identifier); 273 if (reply == NULL) 274 break; 275 276 /* Unsupported Service */ 277 if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 278 405)) { 279 radius_msg_free(reply); 280 reply = NULL; 281 break; 282 } 283 break; 284 default: 285 wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in " 286 "packet from %s:%d", 287 hdr->code, abuf, from_port); 288 } 289 290 if (reply) { 291 wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port); 292 293 if (!radius_msg_add_attr_int32(reply, 294 RADIUS_ATTR_EVENT_TIMESTAMP, 295 now.sec)) { 296 wpa_printf(MSG_DEBUG, "DAS: Failed to add " 297 "Event-Timestamp attribute"); 298 } 299 300 if (radius_msg_finish_das_resp(reply, das->shared_secret, 301 das->shared_secret_len, hdr) < 302 0) { 303 wpa_printf(MSG_DEBUG, "DAS: Failed to add " 304 "Message-Authenticator attribute"); 305 } 306 307 if (wpa_debug_level <= MSG_MSGDUMP) 308 radius_msg_dump(reply); 309 310 rbuf = radius_msg_get_buf(reply); 311 res = sendto(das->sock, wpabuf_head(rbuf), 312 wpabuf_len(rbuf), 0, 313 (struct sockaddr *) &from.ss, fromlen); 314 if (res < 0) { 315 wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s", 316 abuf, from_port, strerror(errno)); 317 } 318 } 319 320fail: 321 radius_msg_free(msg); 322 radius_msg_free(reply); 323} 324 325 326static int radius_das_open_socket(int port) 327{ 328 int s; 329 struct sockaddr_in addr; 330 331 s = socket(PF_INET, SOCK_DGRAM, 0); 332 if (s < 0) { 333 wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno)); 334 return -1; 335 } 336 337 os_memset(&addr, 0, sizeof(addr)); 338 addr.sin_family = AF_INET; 339 addr.sin_port = htons(port); 340 if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 341 wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno)); 342 close(s); 343 return -1; 344 } 345 346 return s; 347} 348 349 350struct radius_das_data * 351radius_das_init(struct radius_das_conf *conf) 352{ 353 struct radius_das_data *das; 354 355 if (conf->port == 0 || conf->shared_secret == NULL || 356 conf->client_addr == NULL) 357 return NULL; 358 359 das = os_zalloc(sizeof(*das)); 360 if (das == NULL) 361 return NULL; 362 363 das->time_window = conf->time_window; 364 das->require_event_timestamp = conf->require_event_timestamp; 365 das->ctx = conf->ctx; 366 das->disconnect = conf->disconnect; 367 368 os_memcpy(&das->client_addr, conf->client_addr, 369 sizeof(das->client_addr)); 370 371 das->shared_secret = os_malloc(conf->shared_secret_len); 372 if (das->shared_secret == NULL) { 373 radius_das_deinit(das); 374 return NULL; 375 } 376 os_memcpy(das->shared_secret, conf->shared_secret, 377 conf->shared_secret_len); 378 das->shared_secret_len = conf->shared_secret_len; 379 380 das->sock = radius_das_open_socket(conf->port); 381 if (das->sock < 0) { 382 wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS " 383 "DAS"); 384 radius_das_deinit(das); 385 return NULL; 386 } 387 388 if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL)) 389 { 390 radius_das_deinit(das); 391 return NULL; 392 } 393 394 return das; 395} 396 397 398void radius_das_deinit(struct radius_das_data *das) 399{ 400 if (das == NULL) 401 return; 402 403 if (das->sock >= 0) { 404 eloop_unregister_read_sock(das->sock); 405 close(das->sock); 406 } 407 408 os_free(das->shared_secret); 409 os_free(das); 410} 411