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