1e5b75505Sopenharmony_ci/* 2e5b75505Sopenharmony_ci * RADIUS Dynamic Authorization Server (DAS) (RFC 5176) 3e5b75505Sopenharmony_ci * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi> 4e5b75505Sopenharmony_ci * 5e5b75505Sopenharmony_ci * This software may be distributed under the terms of the BSD license. 6e5b75505Sopenharmony_ci * See README for more details. 7e5b75505Sopenharmony_ci */ 8e5b75505Sopenharmony_ci 9e5b75505Sopenharmony_ci#include "includes.h" 10e5b75505Sopenharmony_ci#include <net/if.h> 11e5b75505Sopenharmony_ci 12e5b75505Sopenharmony_ci#include "utils/common.h" 13e5b75505Sopenharmony_ci#include "utils/eloop.h" 14e5b75505Sopenharmony_ci#include "utils/ip_addr.h" 15e5b75505Sopenharmony_ci#include "radius.h" 16e5b75505Sopenharmony_ci#include "radius_das.h" 17e5b75505Sopenharmony_ci 18e5b75505Sopenharmony_ci 19e5b75505Sopenharmony_cistruct radius_das_data { 20e5b75505Sopenharmony_ci int sock; 21e5b75505Sopenharmony_ci u8 *shared_secret; 22e5b75505Sopenharmony_ci size_t shared_secret_len; 23e5b75505Sopenharmony_ci struct hostapd_ip_addr client_addr; 24e5b75505Sopenharmony_ci unsigned int time_window; 25e5b75505Sopenharmony_ci int require_event_timestamp; 26e5b75505Sopenharmony_ci int require_message_authenticator; 27e5b75505Sopenharmony_ci void *ctx; 28e5b75505Sopenharmony_ci enum radius_das_res (*disconnect)(void *ctx, 29e5b75505Sopenharmony_ci struct radius_das_attrs *attr); 30e5b75505Sopenharmony_ci enum radius_das_res (*coa)(void *ctx, struct radius_das_attrs *attr); 31e5b75505Sopenharmony_ci}; 32e5b75505Sopenharmony_ci 33e5b75505Sopenharmony_ci 34e5b75505Sopenharmony_cistatic struct radius_msg * radius_das_disconnect(struct radius_das_data *das, 35e5b75505Sopenharmony_ci struct radius_msg *msg, 36e5b75505Sopenharmony_ci const char *abuf, 37e5b75505Sopenharmony_ci int from_port) 38e5b75505Sopenharmony_ci{ 39e5b75505Sopenharmony_ci struct radius_hdr *hdr; 40e5b75505Sopenharmony_ci struct radius_msg *reply; 41e5b75505Sopenharmony_ci u8 allowed[] = { 42e5b75505Sopenharmony_ci RADIUS_ATTR_USER_NAME, 43e5b75505Sopenharmony_ci RADIUS_ATTR_NAS_IP_ADDRESS, 44e5b75505Sopenharmony_ci RADIUS_ATTR_CALLING_STATION_ID, 45e5b75505Sopenharmony_ci RADIUS_ATTR_NAS_IDENTIFIER, 46e5b75505Sopenharmony_ci RADIUS_ATTR_ACCT_SESSION_ID, 47e5b75505Sopenharmony_ci RADIUS_ATTR_ACCT_MULTI_SESSION_ID, 48e5b75505Sopenharmony_ci RADIUS_ATTR_EVENT_TIMESTAMP, 49e5b75505Sopenharmony_ci RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 50e5b75505Sopenharmony_ci RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 51e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6 52e5b75505Sopenharmony_ci RADIUS_ATTR_NAS_IPV6_ADDRESS, 53e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */ 54e5b75505Sopenharmony_ci 0 55e5b75505Sopenharmony_ci }; 56e5b75505Sopenharmony_ci int error = 405; 57e5b75505Sopenharmony_ci u8 attr; 58e5b75505Sopenharmony_ci enum radius_das_res res; 59e5b75505Sopenharmony_ci struct radius_das_attrs attrs; 60e5b75505Sopenharmony_ci u8 *buf; 61e5b75505Sopenharmony_ci size_t len; 62e5b75505Sopenharmony_ci char tmp[100]; 63e5b75505Sopenharmony_ci u8 sta_addr[ETH_ALEN]; 64e5b75505Sopenharmony_ci 65e5b75505Sopenharmony_ci hdr = radius_msg_get_hdr(msg); 66e5b75505Sopenharmony_ci 67e5b75505Sopenharmony_ci attr = radius_msg_find_unlisted_attr(msg, allowed); 68e5b75505Sopenharmony_ci if (attr) { 69e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: Unsupported attribute %u in " 70e5b75505Sopenharmony_ci "Disconnect-Request from %s:%d", attr, 71e5b75505Sopenharmony_ci abuf, from_port); 72e5b75505Sopenharmony_ci error = 401; 73e5b75505Sopenharmony_ci goto fail; 74e5b75505Sopenharmony_ci } 75e5b75505Sopenharmony_ci 76e5b75505Sopenharmony_ci os_memset(&attrs, 0, sizeof(attrs)); 77e5b75505Sopenharmony_ci 78e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, 79e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 80e5b75505Sopenharmony_ci if (len != 4) { 81e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d", 82e5b75505Sopenharmony_ci abuf, from_port); 83e5b75505Sopenharmony_ci error = 407; 84e5b75505Sopenharmony_ci goto fail; 85e5b75505Sopenharmony_ci } 86e5b75505Sopenharmony_ci attrs.nas_ip_addr = buf; 87e5b75505Sopenharmony_ci } 88e5b75505Sopenharmony_ci 89e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6 90e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, 91e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 92e5b75505Sopenharmony_ci if (len != 16) { 93e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d", 94e5b75505Sopenharmony_ci abuf, from_port); 95e5b75505Sopenharmony_ci error = 407; 96e5b75505Sopenharmony_ci goto fail; 97e5b75505Sopenharmony_ci } 98e5b75505Sopenharmony_ci attrs.nas_ipv6_addr = buf; 99e5b75505Sopenharmony_ci } 100e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */ 101e5b75505Sopenharmony_ci 102e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER, 103e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 104e5b75505Sopenharmony_ci attrs.nas_identifier = buf; 105e5b75505Sopenharmony_ci attrs.nas_identifier_len = len; 106e5b75505Sopenharmony_ci } 107e5b75505Sopenharmony_ci 108e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, 109e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 110e5b75505Sopenharmony_ci if (len >= sizeof(tmp)) 111e5b75505Sopenharmony_ci len = sizeof(tmp) - 1; 112e5b75505Sopenharmony_ci os_memcpy(tmp, buf, len); 113e5b75505Sopenharmony_ci tmp[len] = '\0'; 114e5b75505Sopenharmony_ci if (hwaddr_aton2(tmp, sta_addr) < 0) { 115e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id " 116e5b75505Sopenharmony_ci "'%s' from %s:%d", tmp, abuf, from_port); 117e5b75505Sopenharmony_ci error = 407; 118e5b75505Sopenharmony_ci goto fail; 119e5b75505Sopenharmony_ci } 120e5b75505Sopenharmony_ci attrs.sta_addr = sta_addr; 121e5b75505Sopenharmony_ci } 122e5b75505Sopenharmony_ci 123e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, 124e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 125e5b75505Sopenharmony_ci attrs.user_name = buf; 126e5b75505Sopenharmony_ci attrs.user_name_len = len; 127e5b75505Sopenharmony_ci } 128e5b75505Sopenharmony_ci 129e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID, 130e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 131e5b75505Sopenharmony_ci attrs.acct_session_id = buf; 132e5b75505Sopenharmony_ci attrs.acct_session_id_len = len; 133e5b75505Sopenharmony_ci } 134e5b75505Sopenharmony_ci 135e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, 136e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 137e5b75505Sopenharmony_ci attrs.acct_multi_session_id = buf; 138e5b75505Sopenharmony_ci attrs.acct_multi_session_id_len = len; 139e5b75505Sopenharmony_ci } 140e5b75505Sopenharmony_ci 141e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 142e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 143e5b75505Sopenharmony_ci attrs.cui = buf; 144e5b75505Sopenharmony_ci attrs.cui_len = len; 145e5b75505Sopenharmony_ci } 146e5b75505Sopenharmony_ci 147e5b75505Sopenharmony_ci res = das->disconnect(das->ctx, &attrs); 148e5b75505Sopenharmony_ci switch (res) { 149e5b75505Sopenharmony_ci case RADIUS_DAS_NAS_MISMATCH: 150e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d", 151e5b75505Sopenharmony_ci abuf, from_port); 152e5b75505Sopenharmony_ci error = 403; 153e5b75505Sopenharmony_ci break; 154e5b75505Sopenharmony_ci case RADIUS_DAS_SESSION_NOT_FOUND: 155e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: Session not found for request from " 156e5b75505Sopenharmony_ci "%s:%d", abuf, from_port); 157e5b75505Sopenharmony_ci error = 503; 158e5b75505Sopenharmony_ci break; 159e5b75505Sopenharmony_ci case RADIUS_DAS_MULTI_SESSION_MATCH: 160e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 161e5b75505Sopenharmony_ci "DAS: Multiple sessions match for request from %s:%d", 162e5b75505Sopenharmony_ci abuf, from_port); 163e5b75505Sopenharmony_ci error = 508; 164e5b75505Sopenharmony_ci break; 165e5b75505Sopenharmony_ci case RADIUS_DAS_COA_FAILED: 166e5b75505Sopenharmony_ci /* not used with Disconnect-Request */ 167e5b75505Sopenharmony_ci error = 405; 168e5b75505Sopenharmony_ci break; 169e5b75505Sopenharmony_ci case RADIUS_DAS_SUCCESS: 170e5b75505Sopenharmony_ci error = 0; 171e5b75505Sopenharmony_ci break; 172e5b75505Sopenharmony_ci } 173e5b75505Sopenharmony_ci 174e5b75505Sopenharmony_cifail: 175e5b75505Sopenharmony_ci reply = radius_msg_new(error ? RADIUS_CODE_DISCONNECT_NAK : 176e5b75505Sopenharmony_ci RADIUS_CODE_DISCONNECT_ACK, hdr->identifier); 177e5b75505Sopenharmony_ci if (reply == NULL) 178e5b75505Sopenharmony_ci return NULL; 179e5b75505Sopenharmony_ci 180e5b75505Sopenharmony_ci if (error) { 181e5b75505Sopenharmony_ci if (!radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, 182e5b75505Sopenharmony_ci error)) { 183e5b75505Sopenharmony_ci radius_msg_free(reply); 184e5b75505Sopenharmony_ci return NULL; 185e5b75505Sopenharmony_ci } 186e5b75505Sopenharmony_ci } 187e5b75505Sopenharmony_ci 188e5b75505Sopenharmony_ci return reply; 189e5b75505Sopenharmony_ci} 190e5b75505Sopenharmony_ci 191e5b75505Sopenharmony_ci 192e5b75505Sopenharmony_cistatic struct radius_msg * radius_das_coa(struct radius_das_data *das, 193e5b75505Sopenharmony_ci struct radius_msg *msg, 194e5b75505Sopenharmony_ci const char *abuf, int from_port) 195e5b75505Sopenharmony_ci{ 196e5b75505Sopenharmony_ci struct radius_hdr *hdr; 197e5b75505Sopenharmony_ci struct radius_msg *reply; 198e5b75505Sopenharmony_ci u8 allowed[] = { 199e5b75505Sopenharmony_ci RADIUS_ATTR_USER_NAME, 200e5b75505Sopenharmony_ci RADIUS_ATTR_NAS_IP_ADDRESS, 201e5b75505Sopenharmony_ci RADIUS_ATTR_CALLING_STATION_ID, 202e5b75505Sopenharmony_ci RADIUS_ATTR_NAS_IDENTIFIER, 203e5b75505Sopenharmony_ci RADIUS_ATTR_ACCT_SESSION_ID, 204e5b75505Sopenharmony_ci RADIUS_ATTR_ACCT_MULTI_SESSION_ID, 205e5b75505Sopenharmony_ci RADIUS_ATTR_EVENT_TIMESTAMP, 206e5b75505Sopenharmony_ci RADIUS_ATTR_MESSAGE_AUTHENTICATOR, 207e5b75505Sopenharmony_ci RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 208e5b75505Sopenharmony_ci#ifdef CONFIG_HS20 209e5b75505Sopenharmony_ci RADIUS_ATTR_VENDOR_SPECIFIC, 210e5b75505Sopenharmony_ci#endif /* CONFIG_HS20 */ 211e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6 212e5b75505Sopenharmony_ci RADIUS_ATTR_NAS_IPV6_ADDRESS, 213e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */ 214e5b75505Sopenharmony_ci 0 215e5b75505Sopenharmony_ci }; 216e5b75505Sopenharmony_ci int error = 405; 217e5b75505Sopenharmony_ci u8 attr; 218e5b75505Sopenharmony_ci enum radius_das_res res; 219e5b75505Sopenharmony_ci struct radius_das_attrs attrs; 220e5b75505Sopenharmony_ci u8 *buf; 221e5b75505Sopenharmony_ci size_t len; 222e5b75505Sopenharmony_ci char tmp[100]; 223e5b75505Sopenharmony_ci u8 sta_addr[ETH_ALEN]; 224e5b75505Sopenharmony_ci 225e5b75505Sopenharmony_ci hdr = radius_msg_get_hdr(msg); 226e5b75505Sopenharmony_ci 227e5b75505Sopenharmony_ci if (!das->coa) { 228e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: CoA not supported"); 229e5b75505Sopenharmony_ci goto fail; 230e5b75505Sopenharmony_ci } 231e5b75505Sopenharmony_ci 232e5b75505Sopenharmony_ci attr = radius_msg_find_unlisted_attr(msg, allowed); 233e5b75505Sopenharmony_ci if (attr) { 234e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 235e5b75505Sopenharmony_ci "DAS: Unsupported attribute %u in CoA-Request from %s:%d", 236e5b75505Sopenharmony_ci attr, abuf, from_port); 237e5b75505Sopenharmony_ci error = 401; 238e5b75505Sopenharmony_ci goto fail; 239e5b75505Sopenharmony_ci } 240e5b75505Sopenharmony_ci 241e5b75505Sopenharmony_ci os_memset(&attrs, 0, sizeof(attrs)); 242e5b75505Sopenharmony_ci 243e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, 244e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 245e5b75505Sopenharmony_ci if (len != 4) { 246e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d", 247e5b75505Sopenharmony_ci abuf, from_port); 248e5b75505Sopenharmony_ci error = 407; 249e5b75505Sopenharmony_ci goto fail; 250e5b75505Sopenharmony_ci } 251e5b75505Sopenharmony_ci attrs.nas_ip_addr = buf; 252e5b75505Sopenharmony_ci } 253e5b75505Sopenharmony_ci 254e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6 255e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, 256e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 257e5b75505Sopenharmony_ci if (len != 16) { 258e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d", 259e5b75505Sopenharmony_ci abuf, from_port); 260e5b75505Sopenharmony_ci error = 407; 261e5b75505Sopenharmony_ci goto fail; 262e5b75505Sopenharmony_ci } 263e5b75505Sopenharmony_ci attrs.nas_ipv6_addr = buf; 264e5b75505Sopenharmony_ci } 265e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */ 266e5b75505Sopenharmony_ci 267e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER, 268e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 269e5b75505Sopenharmony_ci attrs.nas_identifier = buf; 270e5b75505Sopenharmony_ci attrs.nas_identifier_len = len; 271e5b75505Sopenharmony_ci } 272e5b75505Sopenharmony_ci 273e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID, 274e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 275e5b75505Sopenharmony_ci if (len >= sizeof(tmp)) 276e5b75505Sopenharmony_ci len = sizeof(tmp) - 1; 277e5b75505Sopenharmony_ci os_memcpy(tmp, buf, len); 278e5b75505Sopenharmony_ci tmp[len] = '\0'; 279e5b75505Sopenharmony_ci if (hwaddr_aton2(tmp, sta_addr) < 0) { 280e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: Invalid Calling-Station-Id " 281e5b75505Sopenharmony_ci "'%s' from %s:%d", tmp, abuf, from_port); 282e5b75505Sopenharmony_ci error = 407; 283e5b75505Sopenharmony_ci goto fail; 284e5b75505Sopenharmony_ci } 285e5b75505Sopenharmony_ci attrs.sta_addr = sta_addr; 286e5b75505Sopenharmony_ci } 287e5b75505Sopenharmony_ci 288e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, 289e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 290e5b75505Sopenharmony_ci attrs.user_name = buf; 291e5b75505Sopenharmony_ci attrs.user_name_len = len; 292e5b75505Sopenharmony_ci } 293e5b75505Sopenharmony_ci 294e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_SESSION_ID, 295e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 296e5b75505Sopenharmony_ci attrs.acct_session_id = buf; 297e5b75505Sopenharmony_ci attrs.acct_session_id_len = len; 298e5b75505Sopenharmony_ci } 299e5b75505Sopenharmony_ci 300e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID, 301e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 302e5b75505Sopenharmony_ci attrs.acct_multi_session_id = buf; 303e5b75505Sopenharmony_ci attrs.acct_multi_session_id_len = len; 304e5b75505Sopenharmony_ci } 305e5b75505Sopenharmony_ci 306e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, 307e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 308e5b75505Sopenharmony_ci attrs.cui = buf; 309e5b75505Sopenharmony_ci attrs.cui_len = len; 310e5b75505Sopenharmony_ci } 311e5b75505Sopenharmony_ci 312e5b75505Sopenharmony_ci#ifdef CONFIG_HS20 313e5b75505Sopenharmony_ci if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, 314e5b75505Sopenharmony_ci &buf, &len, NULL) == 0) { 315e5b75505Sopenharmony_ci if (len < 10 || WPA_GET_BE32(buf) != RADIUS_VENDOR_ID_WFA || 316e5b75505Sopenharmony_ci buf[4] != RADIUS_VENDOR_ATTR_WFA_HS20_T_C_FILTERING || 317e5b75505Sopenharmony_ci buf[5] < 6) { 318e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 319e5b75505Sopenharmony_ci "DAS: Unsupported attribute %u in CoA-Request from %s:%d", 320e5b75505Sopenharmony_ci attr, abuf, from_port); 321e5b75505Sopenharmony_ci error = 401; 322e5b75505Sopenharmony_ci goto fail; 323e5b75505Sopenharmony_ci } 324e5b75505Sopenharmony_ci attrs.hs20_t_c_filtering = &buf[6]; 325e5b75505Sopenharmony_ci } 326e5b75505Sopenharmony_ci 327e5b75505Sopenharmony_ci if (!attrs.hs20_t_c_filtering) { 328e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 329e5b75505Sopenharmony_ci "DAS: No supported authorization change attribute in CoA-Request from %s:%d", 330e5b75505Sopenharmony_ci abuf, from_port); 331e5b75505Sopenharmony_ci error = 402; 332e5b75505Sopenharmony_ci goto fail; 333e5b75505Sopenharmony_ci } 334e5b75505Sopenharmony_ci#endif /* CONFIG_HS20 */ 335e5b75505Sopenharmony_ci 336e5b75505Sopenharmony_ci res = das->coa(das->ctx, &attrs); 337e5b75505Sopenharmony_ci switch (res) { 338e5b75505Sopenharmony_ci case RADIUS_DAS_NAS_MISMATCH: 339e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: NAS mismatch from %s:%d", 340e5b75505Sopenharmony_ci abuf, from_port); 341e5b75505Sopenharmony_ci error = 403; 342e5b75505Sopenharmony_ci break; 343e5b75505Sopenharmony_ci case RADIUS_DAS_SESSION_NOT_FOUND: 344e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 345e5b75505Sopenharmony_ci "DAS: Session not found for request from %s:%d", 346e5b75505Sopenharmony_ci abuf, from_port); 347e5b75505Sopenharmony_ci error = 503; 348e5b75505Sopenharmony_ci break; 349e5b75505Sopenharmony_ci case RADIUS_DAS_MULTI_SESSION_MATCH: 350e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, 351e5b75505Sopenharmony_ci "DAS: Multiple sessions match for request from %s:%d", 352e5b75505Sopenharmony_ci abuf, from_port); 353e5b75505Sopenharmony_ci error = 508; 354e5b75505Sopenharmony_ci break; 355e5b75505Sopenharmony_ci case RADIUS_DAS_COA_FAILED: 356e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "DAS: CoA failed for request from %s:%d", 357e5b75505Sopenharmony_ci abuf, from_port); 358e5b75505Sopenharmony_ci error = 407; 359e5b75505Sopenharmony_ci break; 360e5b75505Sopenharmony_ci case RADIUS_DAS_SUCCESS: 361e5b75505Sopenharmony_ci error = 0; 362e5b75505Sopenharmony_ci break; 363e5b75505Sopenharmony_ci } 364e5b75505Sopenharmony_ci 365e5b75505Sopenharmony_cifail: 366e5b75505Sopenharmony_ci reply = radius_msg_new(error ? RADIUS_CODE_COA_NAK : 367e5b75505Sopenharmony_ci RADIUS_CODE_COA_ACK, hdr->identifier); 368e5b75505Sopenharmony_ci if (!reply) 369e5b75505Sopenharmony_ci return NULL; 370e5b75505Sopenharmony_ci 371e5b75505Sopenharmony_ci if (error && 372e5b75505Sopenharmony_ci !radius_msg_add_attr_int32(reply, RADIUS_ATTR_ERROR_CAUSE, error)) { 373e5b75505Sopenharmony_ci radius_msg_free(reply); 374e5b75505Sopenharmony_ci return NULL; 375e5b75505Sopenharmony_ci } 376e5b75505Sopenharmony_ci 377e5b75505Sopenharmony_ci return reply; 378e5b75505Sopenharmony_ci} 379e5b75505Sopenharmony_ci 380e5b75505Sopenharmony_ci 381e5b75505Sopenharmony_cistatic void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx) 382e5b75505Sopenharmony_ci{ 383e5b75505Sopenharmony_ci struct radius_das_data *das = eloop_ctx; 384e5b75505Sopenharmony_ci u8 buf[1500]; 385e5b75505Sopenharmony_ci union { 386e5b75505Sopenharmony_ci struct sockaddr_storage ss; 387e5b75505Sopenharmony_ci struct sockaddr_in sin; 388e5b75505Sopenharmony_ci#ifdef CONFIG_IPV6 389e5b75505Sopenharmony_ci struct sockaddr_in6 sin6; 390e5b75505Sopenharmony_ci#endif /* CONFIG_IPV6 */ 391e5b75505Sopenharmony_ci } from; 392e5b75505Sopenharmony_ci char abuf[50]; 393e5b75505Sopenharmony_ci int from_port = 0; 394e5b75505Sopenharmony_ci socklen_t fromlen; 395e5b75505Sopenharmony_ci int len; 396e5b75505Sopenharmony_ci struct radius_msg *msg, *reply = NULL; 397e5b75505Sopenharmony_ci struct radius_hdr *hdr; 398e5b75505Sopenharmony_ci struct wpabuf *rbuf; 399e5b75505Sopenharmony_ci u32 val; 400e5b75505Sopenharmony_ci int res; 401e5b75505Sopenharmony_ci struct os_time now; 402e5b75505Sopenharmony_ci 403e5b75505Sopenharmony_ci fromlen = sizeof(from); 404e5b75505Sopenharmony_ci len = recvfrom(sock, buf, sizeof(buf), 0, 405e5b75505Sopenharmony_ci (struct sockaddr *) &from.ss, &fromlen); 406e5b75505Sopenharmony_ci if (len < 0) { 407e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "DAS: recvfrom: %s", strerror(errno)); 408e5b75505Sopenharmony_ci return; 409e5b75505Sopenharmony_ci } 410e5b75505Sopenharmony_ci 411e5b75505Sopenharmony_ci os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf)); 412e5b75505Sopenharmony_ci from_port = ntohs(from.sin.sin_port); 413e5b75505Sopenharmony_ci 414e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DAS: Received %d bytes from %s:%d", 415e5b75505Sopenharmony_ci len, abuf, from_port); 416e5b75505Sopenharmony_ci if (das->client_addr.u.v4.s_addr && 417e5b75505Sopenharmony_ci das->client_addr.u.v4.s_addr != from.sin.sin_addr.s_addr) { 418e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DAS: Drop message from unknown client"); 419e5b75505Sopenharmony_ci return; 420e5b75505Sopenharmony_ci } 421e5b75505Sopenharmony_ci 422e5b75505Sopenharmony_ci msg = radius_msg_parse(buf, len); 423e5b75505Sopenharmony_ci if (msg == NULL) { 424e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DAS: Parsing incoming RADIUS packet " 425e5b75505Sopenharmony_ci "from %s:%d failed", abuf, from_port); 426e5b75505Sopenharmony_ci return; 427e5b75505Sopenharmony_ci } 428e5b75505Sopenharmony_ci 429e5b75505Sopenharmony_ci if (wpa_debug_level <= MSG_MSGDUMP) 430e5b75505Sopenharmony_ci radius_msg_dump(msg); 431e5b75505Sopenharmony_ci 432e5b75505Sopenharmony_ci if (radius_msg_verify_das_req(msg, das->shared_secret, 433e5b75505Sopenharmony_ci das->shared_secret_len, 434e5b75505Sopenharmony_ci das->require_message_authenticator)) { 435e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, 436e5b75505Sopenharmony_ci "DAS: Invalid authenticator or Message-Authenticator in packet from %s:%d - drop", 437e5b75505Sopenharmony_ci abuf, from_port); 438e5b75505Sopenharmony_ci goto fail; 439e5b75505Sopenharmony_ci } 440e5b75505Sopenharmony_ci 441e5b75505Sopenharmony_ci os_get_time(&now); 442e5b75505Sopenharmony_ci res = radius_msg_get_attr(msg, RADIUS_ATTR_EVENT_TIMESTAMP, 443e5b75505Sopenharmony_ci (u8 *) &val, 4); 444e5b75505Sopenharmony_ci if (res == 4) { 445e5b75505Sopenharmony_ci u32 timestamp = ntohl(val); 446e5b75505Sopenharmony_ci if ((unsigned int) abs((int) (now.sec - timestamp)) > 447e5b75505Sopenharmony_ci das->time_window) { 448e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DAS: Unacceptable " 449e5b75505Sopenharmony_ci "Event-Timestamp (%u; local time %u) in " 450e5b75505Sopenharmony_ci "packet from %s:%d - drop", 451e5b75505Sopenharmony_ci timestamp, (unsigned int) now.sec, 452e5b75505Sopenharmony_ci abuf, from_port); 453e5b75505Sopenharmony_ci goto fail; 454e5b75505Sopenharmony_ci } 455e5b75505Sopenharmony_ci } else if (das->require_event_timestamp) { 456e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DAS: Missing Event-Timestamp in packet " 457e5b75505Sopenharmony_ci "from %s:%d - drop", abuf, from_port); 458e5b75505Sopenharmony_ci goto fail; 459e5b75505Sopenharmony_ci } 460e5b75505Sopenharmony_ci 461e5b75505Sopenharmony_ci hdr = radius_msg_get_hdr(msg); 462e5b75505Sopenharmony_ci 463e5b75505Sopenharmony_ci switch (hdr->code) { 464e5b75505Sopenharmony_ci case RADIUS_CODE_DISCONNECT_REQUEST: 465e5b75505Sopenharmony_ci reply = radius_das_disconnect(das, msg, abuf, from_port); 466e5b75505Sopenharmony_ci break; 467e5b75505Sopenharmony_ci case RADIUS_CODE_COA_REQUEST: 468e5b75505Sopenharmony_ci reply = radius_das_coa(das, msg, abuf, from_port); 469e5b75505Sopenharmony_ci break; 470e5b75505Sopenharmony_ci default: 471e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DAS: Unexpected RADIUS code %u in " 472e5b75505Sopenharmony_ci "packet from %s:%d", 473e5b75505Sopenharmony_ci hdr->code, abuf, from_port); 474e5b75505Sopenharmony_ci } 475e5b75505Sopenharmony_ci 476e5b75505Sopenharmony_ci if (reply) { 477e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DAS: Reply to %s:%d", abuf, from_port); 478e5b75505Sopenharmony_ci 479e5b75505Sopenharmony_ci if (!radius_msg_add_attr_int32(reply, 480e5b75505Sopenharmony_ci RADIUS_ATTR_EVENT_TIMESTAMP, 481e5b75505Sopenharmony_ci now.sec)) { 482e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DAS: Failed to add " 483e5b75505Sopenharmony_ci "Event-Timestamp attribute"); 484e5b75505Sopenharmony_ci } 485e5b75505Sopenharmony_ci 486e5b75505Sopenharmony_ci if (radius_msg_finish_das_resp(reply, das->shared_secret, 487e5b75505Sopenharmony_ci das->shared_secret_len, hdr) < 488e5b75505Sopenharmony_ci 0) { 489e5b75505Sopenharmony_ci wpa_printf(MSG_DEBUG, "DAS: Failed to add " 490e5b75505Sopenharmony_ci "Message-Authenticator attribute"); 491e5b75505Sopenharmony_ci } 492e5b75505Sopenharmony_ci 493e5b75505Sopenharmony_ci if (wpa_debug_level <= MSG_MSGDUMP) 494e5b75505Sopenharmony_ci radius_msg_dump(reply); 495e5b75505Sopenharmony_ci 496e5b75505Sopenharmony_ci rbuf = radius_msg_get_buf(reply); 497e5b75505Sopenharmony_ci res = sendto(das->sock, wpabuf_head(rbuf), 498e5b75505Sopenharmony_ci wpabuf_len(rbuf), 0, 499e5b75505Sopenharmony_ci (struct sockaddr *) &from.ss, fromlen); 500e5b75505Sopenharmony_ci if (res < 0) { 501e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "DAS: sendto(to %s:%d): %s", 502e5b75505Sopenharmony_ci abuf, from_port, strerror(errno)); 503e5b75505Sopenharmony_ci } 504e5b75505Sopenharmony_ci } 505e5b75505Sopenharmony_ci 506e5b75505Sopenharmony_cifail: 507e5b75505Sopenharmony_ci radius_msg_free(msg); 508e5b75505Sopenharmony_ci radius_msg_free(reply); 509e5b75505Sopenharmony_ci} 510e5b75505Sopenharmony_ci 511e5b75505Sopenharmony_ci 512e5b75505Sopenharmony_cistatic int radius_das_open_socket(int port) 513e5b75505Sopenharmony_ci{ 514e5b75505Sopenharmony_ci int s; 515e5b75505Sopenharmony_ci struct sockaddr_in addr; 516e5b75505Sopenharmony_ci 517e5b75505Sopenharmony_ci s = socket(PF_INET, SOCK_DGRAM, 0); 518e5b75505Sopenharmony_ci if (s < 0) { 519e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno)); 520e5b75505Sopenharmony_ci return -1; 521e5b75505Sopenharmony_ci } 522e5b75505Sopenharmony_ci 523e5b75505Sopenharmony_ci os_memset(&addr, 0, sizeof(addr)); 524e5b75505Sopenharmony_ci addr.sin_family = AF_INET; 525e5b75505Sopenharmony_ci addr.sin_port = htons(port); 526e5b75505Sopenharmony_ci if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 527e5b75505Sopenharmony_ci wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno)); 528e5b75505Sopenharmony_ci close(s); 529e5b75505Sopenharmony_ci return -1; 530e5b75505Sopenharmony_ci } 531e5b75505Sopenharmony_ci 532e5b75505Sopenharmony_ci return s; 533e5b75505Sopenharmony_ci} 534e5b75505Sopenharmony_ci 535e5b75505Sopenharmony_ci 536e5b75505Sopenharmony_cistruct radius_das_data * 537e5b75505Sopenharmony_ciradius_das_init(struct radius_das_conf *conf) 538e5b75505Sopenharmony_ci{ 539e5b75505Sopenharmony_ci struct radius_das_data *das; 540e5b75505Sopenharmony_ci 541e5b75505Sopenharmony_ci if (conf->port == 0 || conf->shared_secret == NULL || 542e5b75505Sopenharmony_ci conf->client_addr == NULL) 543e5b75505Sopenharmony_ci return NULL; 544e5b75505Sopenharmony_ci 545e5b75505Sopenharmony_ci das = os_zalloc(sizeof(*das)); 546e5b75505Sopenharmony_ci if (das == NULL) 547e5b75505Sopenharmony_ci return NULL; 548e5b75505Sopenharmony_ci 549e5b75505Sopenharmony_ci das->time_window = conf->time_window; 550e5b75505Sopenharmony_ci das->require_event_timestamp = conf->require_event_timestamp; 551e5b75505Sopenharmony_ci das->require_message_authenticator = 552e5b75505Sopenharmony_ci conf->require_message_authenticator; 553e5b75505Sopenharmony_ci das->ctx = conf->ctx; 554e5b75505Sopenharmony_ci das->disconnect = conf->disconnect; 555e5b75505Sopenharmony_ci das->coa = conf->coa; 556e5b75505Sopenharmony_ci 557e5b75505Sopenharmony_ci os_memcpy(&das->client_addr, conf->client_addr, 558e5b75505Sopenharmony_ci sizeof(das->client_addr)); 559e5b75505Sopenharmony_ci 560e5b75505Sopenharmony_ci das->shared_secret = os_memdup(conf->shared_secret, 561e5b75505Sopenharmony_ci conf->shared_secret_len); 562e5b75505Sopenharmony_ci if (das->shared_secret == NULL) { 563e5b75505Sopenharmony_ci radius_das_deinit(das); 564e5b75505Sopenharmony_ci return NULL; 565e5b75505Sopenharmony_ci } 566e5b75505Sopenharmony_ci das->shared_secret_len = conf->shared_secret_len; 567e5b75505Sopenharmony_ci 568e5b75505Sopenharmony_ci das->sock = radius_das_open_socket(conf->port); 569e5b75505Sopenharmony_ci if (das->sock < 0) { 570e5b75505Sopenharmony_ci wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS " 571e5b75505Sopenharmony_ci "DAS"); 572e5b75505Sopenharmony_ci radius_das_deinit(das); 573e5b75505Sopenharmony_ci return NULL; 574e5b75505Sopenharmony_ci } 575e5b75505Sopenharmony_ci 576e5b75505Sopenharmony_ci if (eloop_register_read_sock(das->sock, radius_das_receive, das, NULL)) 577e5b75505Sopenharmony_ci { 578e5b75505Sopenharmony_ci radius_das_deinit(das); 579e5b75505Sopenharmony_ci return NULL; 580e5b75505Sopenharmony_ci } 581e5b75505Sopenharmony_ci 582e5b75505Sopenharmony_ci return das; 583e5b75505Sopenharmony_ci} 584e5b75505Sopenharmony_ci 585e5b75505Sopenharmony_ci 586e5b75505Sopenharmony_civoid radius_das_deinit(struct radius_das_data *das) 587e5b75505Sopenharmony_ci{ 588e5b75505Sopenharmony_ci if (das == NULL) 589e5b75505Sopenharmony_ci return; 590e5b75505Sopenharmony_ci 591e5b75505Sopenharmony_ci if (das->sock >= 0) { 592e5b75505Sopenharmony_ci eloop_unregister_read_sock(das->sock); 593e5b75505Sopenharmony_ci close(das->sock); 594e5b75505Sopenharmony_ci } 595e5b75505Sopenharmony_ci 596e5b75505Sopenharmony_ci os_free(das->shared_secret); 597e5b75505Sopenharmony_ci os_free(das); 598e5b75505Sopenharmony_ci} 599