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