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