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