13ff40c12SJohn Marino /*
23ff40c12SJohn Marino * hostapd / IEEE 802.11 authentication (ACL)
33ff40c12SJohn Marino * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
43ff40c12SJohn Marino *
53ff40c12SJohn Marino * This software may be distributed under the terms of the BSD license.
63ff40c12SJohn Marino * See README for more details.
73ff40c12SJohn Marino *
83ff40c12SJohn Marino * Access control list for IEEE 802.11 authentication can uses statically
93ff40c12SJohn Marino * configured ACL from configuration files or an external RADIUS server.
103ff40c12SJohn Marino * Results from external RADIUS queries are cached to allow faster
113ff40c12SJohn Marino * authentication frame processing.
123ff40c12SJohn Marino */
133ff40c12SJohn Marino
143ff40c12SJohn Marino #include "utils/includes.h"
153ff40c12SJohn Marino
163ff40c12SJohn Marino #include "utils/common.h"
173ff40c12SJohn Marino #include "utils/eloop.h"
183ff40c12SJohn Marino #include "radius/radius.h"
193ff40c12SJohn Marino #include "radius/radius_client.h"
203ff40c12SJohn Marino #include "hostapd.h"
213ff40c12SJohn Marino #include "ap_config.h"
223ff40c12SJohn Marino #include "ap_drv_ops.h"
233ff40c12SJohn Marino #include "ieee802_11.h"
243ff40c12SJohn Marino #include "ieee802_1x.h"
253ff40c12SJohn Marino #include "ieee802_11_auth.h"
263ff40c12SJohn Marino
273ff40c12SJohn Marino #define RADIUS_ACL_TIMEOUT 30
283ff40c12SJohn Marino
293ff40c12SJohn Marino
303ff40c12SJohn Marino struct hostapd_cached_radius_acl {
313ff40c12SJohn Marino struct os_reltime timestamp;
323ff40c12SJohn Marino macaddr addr;
333ff40c12SJohn Marino int accepted; /* HOSTAPD_ACL_* */
343ff40c12SJohn Marino struct hostapd_cached_radius_acl *next;
353ff40c12SJohn Marino u32 session_timeout;
363ff40c12SJohn Marino u32 acct_interim_interval;
37*a1157835SDaniel Fojt struct vlan_description vlan_id;
383ff40c12SJohn Marino struct hostapd_sta_wpa_psk_short *psk;
393ff40c12SJohn Marino char *identity;
403ff40c12SJohn Marino char *radius_cui;
413ff40c12SJohn Marino };
423ff40c12SJohn Marino
433ff40c12SJohn Marino
443ff40c12SJohn Marino struct hostapd_acl_query_data {
453ff40c12SJohn Marino struct os_reltime timestamp;
463ff40c12SJohn Marino u8 radius_id;
473ff40c12SJohn Marino macaddr addr;
483ff40c12SJohn Marino u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
493ff40c12SJohn Marino size_t auth_msg_len;
503ff40c12SJohn Marino struct hostapd_acl_query_data *next;
513ff40c12SJohn Marino };
523ff40c12SJohn Marino
533ff40c12SJohn Marino
543ff40c12SJohn Marino #ifndef CONFIG_NO_RADIUS
hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl * e)553ff40c12SJohn Marino static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e)
563ff40c12SJohn Marino {
573ff40c12SJohn Marino os_free(e->identity);
583ff40c12SJohn Marino os_free(e->radius_cui);
593ff40c12SJohn Marino hostapd_free_psk_list(e->psk);
603ff40c12SJohn Marino os_free(e);
613ff40c12SJohn Marino }
623ff40c12SJohn Marino
633ff40c12SJohn Marino
hostapd_acl_cache_free(struct hostapd_cached_radius_acl * acl_cache)643ff40c12SJohn Marino static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache)
653ff40c12SJohn Marino {
663ff40c12SJohn Marino struct hostapd_cached_radius_acl *prev;
673ff40c12SJohn Marino
683ff40c12SJohn Marino while (acl_cache) {
693ff40c12SJohn Marino prev = acl_cache;
703ff40c12SJohn Marino acl_cache = acl_cache->next;
713ff40c12SJohn Marino hostapd_acl_cache_free_entry(prev);
723ff40c12SJohn Marino }
733ff40c12SJohn Marino }
743ff40c12SJohn Marino
753ff40c12SJohn Marino
copy_psk_list(struct hostapd_sta_wpa_psk_short ** psk,struct hostapd_sta_wpa_psk_short * src)763ff40c12SJohn Marino static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk,
773ff40c12SJohn Marino struct hostapd_sta_wpa_psk_short *src)
783ff40c12SJohn Marino {
79*a1157835SDaniel Fojt if (!psk)
80*a1157835SDaniel Fojt return;
813ff40c12SJohn Marino
82*a1157835SDaniel Fojt if (src)
83*a1157835SDaniel Fojt src->ref++;
84*a1157835SDaniel Fojt
85*a1157835SDaniel Fojt *psk = src;
863ff40c12SJohn Marino }
873ff40c12SJohn Marino
883ff40c12SJohn Marino
hostapd_acl_cache_get(struct hostapd_data * hapd,const u8 * addr,u32 * session_timeout,u32 * acct_interim_interval,struct vlan_description * vlan_id,struct hostapd_sta_wpa_psk_short ** psk,char ** identity,char ** radius_cui)893ff40c12SJohn Marino static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
903ff40c12SJohn Marino u32 *session_timeout,
91*a1157835SDaniel Fojt u32 *acct_interim_interval,
92*a1157835SDaniel Fojt struct vlan_description *vlan_id,
933ff40c12SJohn Marino struct hostapd_sta_wpa_psk_short **psk,
943ff40c12SJohn Marino char **identity, char **radius_cui)
953ff40c12SJohn Marino {
963ff40c12SJohn Marino struct hostapd_cached_radius_acl *entry;
973ff40c12SJohn Marino struct os_reltime now;
983ff40c12SJohn Marino
993ff40c12SJohn Marino os_get_reltime(&now);
1003ff40c12SJohn Marino
1013ff40c12SJohn Marino for (entry = hapd->acl_cache; entry; entry = entry->next) {
1023ff40c12SJohn Marino if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
1033ff40c12SJohn Marino continue;
1043ff40c12SJohn Marino
1053ff40c12SJohn Marino if (os_reltime_expired(&now, &entry->timestamp,
1063ff40c12SJohn Marino RADIUS_ACL_TIMEOUT))
1073ff40c12SJohn Marino return -1; /* entry has expired */
1083ff40c12SJohn Marino if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
1093ff40c12SJohn Marino if (session_timeout)
1103ff40c12SJohn Marino *session_timeout = entry->session_timeout;
1113ff40c12SJohn Marino if (acct_interim_interval)
1123ff40c12SJohn Marino *acct_interim_interval =
1133ff40c12SJohn Marino entry->acct_interim_interval;
1143ff40c12SJohn Marino if (vlan_id)
1153ff40c12SJohn Marino *vlan_id = entry->vlan_id;
1163ff40c12SJohn Marino copy_psk_list(psk, entry->psk);
1173ff40c12SJohn Marino if (identity) {
1183ff40c12SJohn Marino if (entry->identity)
1193ff40c12SJohn Marino *identity = os_strdup(entry->identity);
1203ff40c12SJohn Marino else
1213ff40c12SJohn Marino *identity = NULL;
1223ff40c12SJohn Marino }
1233ff40c12SJohn Marino if (radius_cui) {
1243ff40c12SJohn Marino if (entry->radius_cui)
1253ff40c12SJohn Marino *radius_cui = os_strdup(entry->radius_cui);
1263ff40c12SJohn Marino else
1273ff40c12SJohn Marino *radius_cui = NULL;
1283ff40c12SJohn Marino }
1293ff40c12SJohn Marino return entry->accepted;
1303ff40c12SJohn Marino }
1313ff40c12SJohn Marino
1323ff40c12SJohn Marino return -1;
1333ff40c12SJohn Marino }
1343ff40c12SJohn Marino #endif /* CONFIG_NO_RADIUS */
1353ff40c12SJohn Marino
1363ff40c12SJohn Marino
hostapd_acl_query_free(struct hostapd_acl_query_data * query)1373ff40c12SJohn Marino static void hostapd_acl_query_free(struct hostapd_acl_query_data *query)
1383ff40c12SJohn Marino {
1393ff40c12SJohn Marino if (query == NULL)
1403ff40c12SJohn Marino return;
1413ff40c12SJohn Marino os_free(query->auth_msg);
1423ff40c12SJohn Marino os_free(query);
1433ff40c12SJohn Marino }
1443ff40c12SJohn Marino
1453ff40c12SJohn Marino
1463ff40c12SJohn Marino #ifndef CONFIG_NO_RADIUS
hostapd_radius_acl_query(struct hostapd_data * hapd,const u8 * addr,struct hostapd_acl_query_data * query)1473ff40c12SJohn Marino static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr,
1483ff40c12SJohn Marino struct hostapd_acl_query_data *query)
1493ff40c12SJohn Marino {
1503ff40c12SJohn Marino struct radius_msg *msg;
1513ff40c12SJohn Marino char buf[128];
1523ff40c12SJohn Marino
1533ff40c12SJohn Marino query->radius_id = radius_client_get_id(hapd->radius);
1543ff40c12SJohn Marino msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id);
1553ff40c12SJohn Marino if (msg == NULL)
1563ff40c12SJohn Marino return -1;
1573ff40c12SJohn Marino
158*a1157835SDaniel Fojt if (radius_msg_make_authenticator(msg) < 0) {
159*a1157835SDaniel Fojt wpa_printf(MSG_INFO, "Could not make Request Authenticator");
160*a1157835SDaniel Fojt goto fail;
161*a1157835SDaniel Fojt }
1623ff40c12SJohn Marino
1633ff40c12SJohn Marino os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr));
1643ff40c12SJohn Marino if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf,
1653ff40c12SJohn Marino os_strlen(buf))) {
1663ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Could not add User-Name");
1673ff40c12SJohn Marino goto fail;
1683ff40c12SJohn Marino }
1693ff40c12SJohn Marino
1703ff40c12SJohn Marino if (!radius_msg_add_attr_user_password(
1713ff40c12SJohn Marino msg, (u8 *) buf, os_strlen(buf),
1723ff40c12SJohn Marino hapd->conf->radius->auth_server->shared_secret,
1733ff40c12SJohn Marino hapd->conf->radius->auth_server->shared_secret_len)) {
1743ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Could not add User-Password");
1753ff40c12SJohn Marino goto fail;
1763ff40c12SJohn Marino }
1773ff40c12SJohn Marino
1783ff40c12SJohn Marino if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr,
1793ff40c12SJohn Marino NULL, msg) < 0)
1803ff40c12SJohn Marino goto fail;
1813ff40c12SJohn Marino
1823ff40c12SJohn Marino os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
1833ff40c12SJohn Marino MAC2STR(addr));
1843ff40c12SJohn Marino if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID,
1853ff40c12SJohn Marino (u8 *) buf, os_strlen(buf))) {
1863ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id");
1873ff40c12SJohn Marino goto fail;
1883ff40c12SJohn Marino }
1893ff40c12SJohn Marino
1903ff40c12SJohn Marino os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b");
1913ff40c12SJohn Marino if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO,
1923ff40c12SJohn Marino (u8 *) buf, os_strlen(buf))) {
1933ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Could not add Connect-Info");
1943ff40c12SJohn Marino goto fail;
1953ff40c12SJohn Marino }
1963ff40c12SJohn Marino
1973ff40c12SJohn Marino if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr) < 0)
1983ff40c12SJohn Marino goto fail;
1993ff40c12SJohn Marino return 0;
2003ff40c12SJohn Marino
2013ff40c12SJohn Marino fail:
2023ff40c12SJohn Marino radius_msg_free(msg);
2033ff40c12SJohn Marino return -1;
2043ff40c12SJohn Marino }
2053ff40c12SJohn Marino #endif /* CONFIG_NO_RADIUS */
2063ff40c12SJohn Marino
2073ff40c12SJohn Marino
2083ff40c12SJohn Marino /**
209*a1157835SDaniel Fojt * hostapd_check_acl - Check a specified STA against accept/deny ACLs
2103ff40c12SJohn Marino * @hapd: hostapd BSS data
2113ff40c12SJohn Marino * @addr: MAC address of the STA
2123ff40c12SJohn Marino * @vlan_id: Buffer for returning VLAN ID
2133ff40c12SJohn Marino * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
2143ff40c12SJohn Marino */
hostapd_check_acl(struct hostapd_data * hapd,const u8 * addr,struct vlan_description * vlan_id)215*a1157835SDaniel Fojt int hostapd_check_acl(struct hostapd_data *hapd, const u8 *addr,
216*a1157835SDaniel Fojt struct vlan_description *vlan_id)
2173ff40c12SJohn Marino {
2183ff40c12SJohn Marino if (hostapd_maclist_found(hapd->conf->accept_mac,
2193ff40c12SJohn Marino hapd->conf->num_accept_mac, addr, vlan_id))
2203ff40c12SJohn Marino return HOSTAPD_ACL_ACCEPT;
2213ff40c12SJohn Marino
2223ff40c12SJohn Marino if (hostapd_maclist_found(hapd->conf->deny_mac,
2233ff40c12SJohn Marino hapd->conf->num_deny_mac, addr, vlan_id))
2243ff40c12SJohn Marino return HOSTAPD_ACL_REJECT;
2253ff40c12SJohn Marino
2263ff40c12SJohn Marino if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED)
2273ff40c12SJohn Marino return HOSTAPD_ACL_ACCEPT;
2283ff40c12SJohn Marino if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED)
2293ff40c12SJohn Marino return HOSTAPD_ACL_REJECT;
2303ff40c12SJohn Marino
231*a1157835SDaniel Fojt return HOSTAPD_ACL_PENDING;
232*a1157835SDaniel Fojt }
233*a1157835SDaniel Fojt
234*a1157835SDaniel Fojt
235*a1157835SDaniel Fojt /**
236*a1157835SDaniel Fojt * hostapd_allowed_address - Check whether a specified STA can be authenticated
237*a1157835SDaniel Fojt * @hapd: hostapd BSS data
238*a1157835SDaniel Fojt * @addr: MAC address of the STA
239*a1157835SDaniel Fojt * @msg: Authentication message
240*a1157835SDaniel Fojt * @len: Length of msg in octets
241*a1157835SDaniel Fojt * @session_timeout: Buffer for returning session timeout (from RADIUS)
242*a1157835SDaniel Fojt * @acct_interim_interval: Buffer for returning account interval (from RADIUS)
243*a1157835SDaniel Fojt * @vlan_id: Buffer for returning VLAN ID
244*a1157835SDaniel Fojt * @psk: Linked list buffer for returning WPA PSK
245*a1157835SDaniel Fojt * @identity: Buffer for returning identity (from RADIUS)
246*a1157835SDaniel Fojt * @radius_cui: Buffer for returning CUI (from RADIUS)
247*a1157835SDaniel Fojt * @is_probe_req: Whether this query for a Probe Request frame
248*a1157835SDaniel Fojt * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING
249*a1157835SDaniel Fojt *
250*a1157835SDaniel Fojt * The caller is responsible for freeing the returned *identity and *radius_cui
251*a1157835SDaniel Fojt * values with os_free().
252*a1157835SDaniel Fojt */
hostapd_allowed_address(struct hostapd_data * hapd,const u8 * addr,const u8 * msg,size_t len,u32 * session_timeout,u32 * acct_interim_interval,struct vlan_description * vlan_id,struct hostapd_sta_wpa_psk_short ** psk,char ** identity,char ** radius_cui,int is_probe_req)253*a1157835SDaniel Fojt int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
254*a1157835SDaniel Fojt const u8 *msg, size_t len, u32 *session_timeout,
255*a1157835SDaniel Fojt u32 *acct_interim_interval,
256*a1157835SDaniel Fojt struct vlan_description *vlan_id,
257*a1157835SDaniel Fojt struct hostapd_sta_wpa_psk_short **psk,
258*a1157835SDaniel Fojt char **identity, char **radius_cui,
259*a1157835SDaniel Fojt int is_probe_req)
260*a1157835SDaniel Fojt {
261*a1157835SDaniel Fojt int res;
262*a1157835SDaniel Fojt
263*a1157835SDaniel Fojt if (session_timeout)
264*a1157835SDaniel Fojt *session_timeout = 0;
265*a1157835SDaniel Fojt if (acct_interim_interval)
266*a1157835SDaniel Fojt *acct_interim_interval = 0;
267*a1157835SDaniel Fojt if (vlan_id)
268*a1157835SDaniel Fojt os_memset(vlan_id, 0, sizeof(*vlan_id));
269*a1157835SDaniel Fojt if (psk)
270*a1157835SDaniel Fojt *psk = NULL;
271*a1157835SDaniel Fojt if (identity)
272*a1157835SDaniel Fojt *identity = NULL;
273*a1157835SDaniel Fojt if (radius_cui)
274*a1157835SDaniel Fojt *radius_cui = NULL;
275*a1157835SDaniel Fojt
276*a1157835SDaniel Fojt res = hostapd_check_acl(hapd, addr, vlan_id);
277*a1157835SDaniel Fojt if (res != HOSTAPD_ACL_PENDING)
278*a1157835SDaniel Fojt return res;
279*a1157835SDaniel Fojt
2803ff40c12SJohn Marino if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) {
2813ff40c12SJohn Marino #ifdef CONFIG_NO_RADIUS
2823ff40c12SJohn Marino return HOSTAPD_ACL_REJECT;
2833ff40c12SJohn Marino #else /* CONFIG_NO_RADIUS */
2843ff40c12SJohn Marino struct hostapd_acl_query_data *query;
2853ff40c12SJohn Marino
286*a1157835SDaniel Fojt if (is_probe_req) {
287*a1157835SDaniel Fojt /* Skip RADIUS queries for Probe Request frames to avoid
288*a1157835SDaniel Fojt * excessive load on the authentication server. */
289*a1157835SDaniel Fojt return HOSTAPD_ACL_ACCEPT;
290*a1157835SDaniel Fojt };
291*a1157835SDaniel Fojt
292*a1157835SDaniel Fojt if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
293*a1157835SDaniel Fojt vlan_id = NULL;
294*a1157835SDaniel Fojt
2953ff40c12SJohn Marino /* Check whether ACL cache has an entry for this station */
296*a1157835SDaniel Fojt res = hostapd_acl_cache_get(hapd, addr, session_timeout,
297*a1157835SDaniel Fojt acct_interim_interval, vlan_id, psk,
2983ff40c12SJohn Marino identity, radius_cui);
2993ff40c12SJohn Marino if (res == HOSTAPD_ACL_ACCEPT ||
3003ff40c12SJohn Marino res == HOSTAPD_ACL_ACCEPT_TIMEOUT)
3013ff40c12SJohn Marino return res;
3023ff40c12SJohn Marino if (res == HOSTAPD_ACL_REJECT)
3033ff40c12SJohn Marino return HOSTAPD_ACL_REJECT;
3043ff40c12SJohn Marino
3053ff40c12SJohn Marino query = hapd->acl_queries;
3063ff40c12SJohn Marino while (query) {
3073ff40c12SJohn Marino if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) {
3083ff40c12SJohn Marino /* pending query in RADIUS retransmit queue;
3093ff40c12SJohn Marino * do not generate a new one */
3103ff40c12SJohn Marino if (identity) {
3113ff40c12SJohn Marino os_free(*identity);
3123ff40c12SJohn Marino *identity = NULL;
3133ff40c12SJohn Marino }
3143ff40c12SJohn Marino if (radius_cui) {
3153ff40c12SJohn Marino os_free(*radius_cui);
3163ff40c12SJohn Marino *radius_cui = NULL;
3173ff40c12SJohn Marino }
3183ff40c12SJohn Marino return HOSTAPD_ACL_PENDING;
3193ff40c12SJohn Marino }
3203ff40c12SJohn Marino query = query->next;
3213ff40c12SJohn Marino }
3223ff40c12SJohn Marino
3233ff40c12SJohn Marino if (!hapd->conf->radius->auth_server)
3243ff40c12SJohn Marino return HOSTAPD_ACL_REJECT;
3253ff40c12SJohn Marino
3263ff40c12SJohn Marino /* No entry in the cache - query external RADIUS server */
3273ff40c12SJohn Marino query = os_zalloc(sizeof(*query));
3283ff40c12SJohn Marino if (query == NULL) {
3293ff40c12SJohn Marino wpa_printf(MSG_ERROR, "malloc for query data failed");
3303ff40c12SJohn Marino return HOSTAPD_ACL_REJECT;
3313ff40c12SJohn Marino }
3323ff40c12SJohn Marino os_get_reltime(&query->timestamp);
3333ff40c12SJohn Marino os_memcpy(query->addr, addr, ETH_ALEN);
3343ff40c12SJohn Marino if (hostapd_radius_acl_query(hapd, addr, query)) {
3353ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
3363ff40c12SJohn Marino "for ACL query.");
3373ff40c12SJohn Marino hostapd_acl_query_free(query);
3383ff40c12SJohn Marino return HOSTAPD_ACL_REJECT;
3393ff40c12SJohn Marino }
3403ff40c12SJohn Marino
341*a1157835SDaniel Fojt query->auth_msg = os_memdup(msg, len);
3423ff40c12SJohn Marino if (query->auth_msg == NULL) {
3433ff40c12SJohn Marino wpa_printf(MSG_ERROR, "Failed to allocate memory for "
3443ff40c12SJohn Marino "auth frame.");
3453ff40c12SJohn Marino hostapd_acl_query_free(query);
3463ff40c12SJohn Marino return HOSTAPD_ACL_REJECT;
3473ff40c12SJohn Marino }
3483ff40c12SJohn Marino query->auth_msg_len = len;
3493ff40c12SJohn Marino query->next = hapd->acl_queries;
3503ff40c12SJohn Marino hapd->acl_queries = query;
3513ff40c12SJohn Marino
3523ff40c12SJohn Marino /* Queued data will be processed in hostapd_acl_recv_radius()
3533ff40c12SJohn Marino * when RADIUS server replies to the sent Access-Request. */
3543ff40c12SJohn Marino return HOSTAPD_ACL_PENDING;
3553ff40c12SJohn Marino #endif /* CONFIG_NO_RADIUS */
3563ff40c12SJohn Marino }
3573ff40c12SJohn Marino
3583ff40c12SJohn Marino return HOSTAPD_ACL_REJECT;
3593ff40c12SJohn Marino }
3603ff40c12SJohn Marino
3613ff40c12SJohn Marino
3623ff40c12SJohn Marino #ifndef CONFIG_NO_RADIUS
hostapd_acl_expire_cache(struct hostapd_data * hapd,struct os_reltime * now)3633ff40c12SJohn Marino static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
3643ff40c12SJohn Marino struct os_reltime *now)
3653ff40c12SJohn Marino {
3663ff40c12SJohn Marino struct hostapd_cached_radius_acl *prev, *entry, *tmp;
3673ff40c12SJohn Marino
3683ff40c12SJohn Marino prev = NULL;
3693ff40c12SJohn Marino entry = hapd->acl_cache;
3703ff40c12SJohn Marino
3713ff40c12SJohn Marino while (entry) {
3723ff40c12SJohn Marino if (os_reltime_expired(now, &entry->timestamp,
3733ff40c12SJohn Marino RADIUS_ACL_TIMEOUT)) {
3743ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
3753ff40c12SJohn Marino " has expired.", MAC2STR(entry->addr));
3763ff40c12SJohn Marino if (prev)
3773ff40c12SJohn Marino prev->next = entry->next;
3783ff40c12SJohn Marino else
3793ff40c12SJohn Marino hapd->acl_cache = entry->next;
3803ff40c12SJohn Marino hostapd_drv_set_radius_acl_expire(hapd, entry->addr);
3813ff40c12SJohn Marino tmp = entry;
3823ff40c12SJohn Marino entry = entry->next;
3833ff40c12SJohn Marino hostapd_acl_cache_free_entry(tmp);
3843ff40c12SJohn Marino continue;
3853ff40c12SJohn Marino }
3863ff40c12SJohn Marino
3873ff40c12SJohn Marino prev = entry;
3883ff40c12SJohn Marino entry = entry->next;
3893ff40c12SJohn Marino }
3903ff40c12SJohn Marino }
3913ff40c12SJohn Marino
3923ff40c12SJohn Marino
hostapd_acl_expire_queries(struct hostapd_data * hapd,struct os_reltime * now)3933ff40c12SJohn Marino static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
3943ff40c12SJohn Marino struct os_reltime *now)
3953ff40c12SJohn Marino {
3963ff40c12SJohn Marino struct hostapd_acl_query_data *prev, *entry, *tmp;
3973ff40c12SJohn Marino
3983ff40c12SJohn Marino prev = NULL;
3993ff40c12SJohn Marino entry = hapd->acl_queries;
4003ff40c12SJohn Marino
4013ff40c12SJohn Marino while (entry) {
4023ff40c12SJohn Marino if (os_reltime_expired(now, &entry->timestamp,
4033ff40c12SJohn Marino RADIUS_ACL_TIMEOUT)) {
4043ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
4053ff40c12SJohn Marino " has expired.", MAC2STR(entry->addr));
4063ff40c12SJohn Marino if (prev)
4073ff40c12SJohn Marino prev->next = entry->next;
4083ff40c12SJohn Marino else
4093ff40c12SJohn Marino hapd->acl_queries = entry->next;
4103ff40c12SJohn Marino
4113ff40c12SJohn Marino tmp = entry;
4123ff40c12SJohn Marino entry = entry->next;
4133ff40c12SJohn Marino hostapd_acl_query_free(tmp);
4143ff40c12SJohn Marino continue;
4153ff40c12SJohn Marino }
4163ff40c12SJohn Marino
4173ff40c12SJohn Marino prev = entry;
4183ff40c12SJohn Marino entry = entry->next;
4193ff40c12SJohn Marino }
4203ff40c12SJohn Marino }
4213ff40c12SJohn Marino
4223ff40c12SJohn Marino
4233ff40c12SJohn Marino /**
4243ff40c12SJohn Marino * hostapd_acl_expire - ACL cache expiration callback
425*a1157835SDaniel Fojt * @hapd: struct hostapd_data *
4263ff40c12SJohn Marino */
hostapd_acl_expire(struct hostapd_data * hapd)427*a1157835SDaniel Fojt void hostapd_acl_expire(struct hostapd_data *hapd)
4283ff40c12SJohn Marino {
4293ff40c12SJohn Marino struct os_reltime now;
4303ff40c12SJohn Marino
4313ff40c12SJohn Marino os_get_reltime(&now);
4323ff40c12SJohn Marino hostapd_acl_expire_cache(hapd, &now);
4333ff40c12SJohn Marino hostapd_acl_expire_queries(hapd, &now);
4343ff40c12SJohn Marino }
4353ff40c12SJohn Marino
4363ff40c12SJohn Marino
decode_tunnel_passwords(struct hostapd_data * hapd,const u8 * shared_secret,size_t shared_secret_len,struct radius_msg * msg,struct radius_msg * req,struct hostapd_cached_radius_acl * cache)4373ff40c12SJohn Marino static void decode_tunnel_passwords(struct hostapd_data *hapd,
4383ff40c12SJohn Marino const u8 *shared_secret,
4393ff40c12SJohn Marino size_t shared_secret_len,
4403ff40c12SJohn Marino struct radius_msg *msg,
4413ff40c12SJohn Marino struct radius_msg *req,
4423ff40c12SJohn Marino struct hostapd_cached_radius_acl *cache)
4433ff40c12SJohn Marino {
4443ff40c12SJohn Marino int passphraselen;
445*a1157835SDaniel Fojt char *passphrase;
4463ff40c12SJohn Marino size_t i;
4473ff40c12SJohn Marino struct hostapd_sta_wpa_psk_short *psk;
4483ff40c12SJohn Marino
4493ff40c12SJohn Marino /*
4503ff40c12SJohn Marino * Decode all tunnel passwords as PSK and save them into a linked list.
4513ff40c12SJohn Marino */
4523ff40c12SJohn Marino for (i = 0; ; i++) {
4533ff40c12SJohn Marino passphrase = radius_msg_get_tunnel_password(
4543ff40c12SJohn Marino msg, &passphraselen, shared_secret, shared_secret_len,
4553ff40c12SJohn Marino req, i);
4563ff40c12SJohn Marino /*
4573ff40c12SJohn Marino * Passphrase is NULL iff there is no i-th Tunnel-Password
4583ff40c12SJohn Marino * attribute in msg.
4593ff40c12SJohn Marino */
4603ff40c12SJohn Marino if (passphrase == NULL)
4613ff40c12SJohn Marino break;
462*a1157835SDaniel Fojt
463*a1157835SDaniel Fojt /*
464*a1157835SDaniel Fojt * Passphase should be 8..63 chars (to be hashed with SSID)
465*a1157835SDaniel Fojt * or 64 chars hex string (no separate hashing with SSID).
466*a1157835SDaniel Fojt */
467*a1157835SDaniel Fojt
468*a1157835SDaniel Fojt if (passphraselen < MIN_PASSPHRASE_LEN ||
469*a1157835SDaniel Fojt passphraselen > MAX_PASSPHRASE_LEN + 1)
470*a1157835SDaniel Fojt goto free_pass;
471*a1157835SDaniel Fojt
4723ff40c12SJohn Marino /*
4733ff40c12SJohn Marino * passphrase does not contain the NULL termination.
4743ff40c12SJohn Marino * Add it here as pbkdf2_sha1() requires it.
4753ff40c12SJohn Marino */
4763ff40c12SJohn Marino psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short));
477*a1157835SDaniel Fojt if (psk) {
478*a1157835SDaniel Fojt if ((passphraselen == MAX_PASSPHRASE_LEN + 1) &&
479*a1157835SDaniel Fojt (hexstr2bin(passphrase, psk->psk, PMK_LEN) < 0)) {
480*a1157835SDaniel Fojt hostapd_logger(hapd, cache->addr,
481*a1157835SDaniel Fojt HOSTAPD_MODULE_RADIUS,
482*a1157835SDaniel Fojt HOSTAPD_LEVEL_WARNING,
483*a1157835SDaniel Fojt "invalid hex string (%d chars) in Tunnel-Password",
484*a1157835SDaniel Fojt passphraselen);
485*a1157835SDaniel Fojt goto skip;
486*a1157835SDaniel Fojt } else if (passphraselen <= MAX_PASSPHRASE_LEN) {
487*a1157835SDaniel Fojt os_memcpy(psk->passphrase, passphrase,
488*a1157835SDaniel Fojt passphraselen);
489*a1157835SDaniel Fojt psk->is_passphrase = 1;
490*a1157835SDaniel Fojt }
4913ff40c12SJohn Marino psk->next = cache->psk;
4923ff40c12SJohn Marino cache->psk = psk;
4933ff40c12SJohn Marino psk = NULL;
4943ff40c12SJohn Marino }
495*a1157835SDaniel Fojt skip:
4963ff40c12SJohn Marino os_free(psk);
497*a1157835SDaniel Fojt free_pass:
4983ff40c12SJohn Marino os_free(passphrase);
4993ff40c12SJohn Marino }
5003ff40c12SJohn Marino }
5013ff40c12SJohn Marino
5023ff40c12SJohn Marino
5033ff40c12SJohn Marino /**
5043ff40c12SJohn Marino * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages
5053ff40c12SJohn Marino * @msg: RADIUS response message
5063ff40c12SJohn Marino * @req: RADIUS request message
5073ff40c12SJohn Marino * @shared_secret: RADIUS shared secret
5083ff40c12SJohn Marino * @shared_secret_len: Length of shared_secret in octets
5093ff40c12SJohn Marino * @data: Context data (struct hostapd_data *)
5103ff40c12SJohn Marino * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and
5113ff40c12SJohn Marino * was processed here) or RADIUS_RX_UNKNOWN if not.
5123ff40c12SJohn Marino */
5133ff40c12SJohn Marino static RadiusRxResult
hostapd_acl_recv_radius(struct radius_msg * msg,struct radius_msg * req,const u8 * shared_secret,size_t shared_secret_len,void * data)5143ff40c12SJohn Marino hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
5153ff40c12SJohn Marino const u8 *shared_secret, size_t shared_secret_len,
5163ff40c12SJohn Marino void *data)
5173ff40c12SJohn Marino {
5183ff40c12SJohn Marino struct hostapd_data *hapd = data;
5193ff40c12SJohn Marino struct hostapd_acl_query_data *query, *prev;
5203ff40c12SJohn Marino struct hostapd_cached_radius_acl *cache;
5213ff40c12SJohn Marino struct radius_hdr *hdr = radius_msg_get_hdr(msg);
5223ff40c12SJohn Marino
5233ff40c12SJohn Marino query = hapd->acl_queries;
5243ff40c12SJohn Marino prev = NULL;
5253ff40c12SJohn Marino while (query) {
5263ff40c12SJohn Marino if (query->radius_id == hdr->identifier)
5273ff40c12SJohn Marino break;
5283ff40c12SJohn Marino prev = query;
5293ff40c12SJohn Marino query = query->next;
5303ff40c12SJohn Marino }
5313ff40c12SJohn Marino if (query == NULL)
5323ff40c12SJohn Marino return RADIUS_RX_UNKNOWN;
5333ff40c12SJohn Marino
5343ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS "
5353ff40c12SJohn Marino "message (id=%d)", query->radius_id);
5363ff40c12SJohn Marino
5373ff40c12SJohn Marino if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
5383ff40c12SJohn Marino wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have "
5393ff40c12SJohn Marino "correct authenticator - dropped\n");
5403ff40c12SJohn Marino return RADIUS_RX_INVALID_AUTHENTICATOR;
5413ff40c12SJohn Marino }
5423ff40c12SJohn Marino
5433ff40c12SJohn Marino if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
5443ff40c12SJohn Marino hdr->code != RADIUS_CODE_ACCESS_REJECT) {
5453ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL "
5463ff40c12SJohn Marino "query", hdr->code);
5473ff40c12SJohn Marino return RADIUS_RX_UNKNOWN;
5483ff40c12SJohn Marino }
5493ff40c12SJohn Marino
5503ff40c12SJohn Marino /* Insert Accept/Reject info into ACL cache */
5513ff40c12SJohn Marino cache = os_zalloc(sizeof(*cache));
5523ff40c12SJohn Marino if (cache == NULL) {
5533ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
5543ff40c12SJohn Marino goto done;
5553ff40c12SJohn Marino }
5563ff40c12SJohn Marino os_get_reltime(&cache->timestamp);
5573ff40c12SJohn Marino os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
5583ff40c12SJohn Marino if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
5593ff40c12SJohn Marino u8 *buf;
5603ff40c12SJohn Marino size_t len;
5613ff40c12SJohn Marino
5623ff40c12SJohn Marino if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT,
5633ff40c12SJohn Marino &cache->session_timeout) == 0)
5643ff40c12SJohn Marino cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT;
5653ff40c12SJohn Marino else
5663ff40c12SJohn Marino cache->accepted = HOSTAPD_ACL_ACCEPT;
5673ff40c12SJohn Marino
5683ff40c12SJohn Marino if (radius_msg_get_attr_int32(
5693ff40c12SJohn Marino msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL,
5703ff40c12SJohn Marino &cache->acct_interim_interval) == 0 &&
5713ff40c12SJohn Marino cache->acct_interim_interval < 60) {
5723ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Ignored too small "
5733ff40c12SJohn Marino "Acct-Interim-Interval %d for STA " MACSTR,
5743ff40c12SJohn Marino cache->acct_interim_interval,
5753ff40c12SJohn Marino MAC2STR(query->addr));
5763ff40c12SJohn Marino cache->acct_interim_interval = 0;
5773ff40c12SJohn Marino }
5783ff40c12SJohn Marino
579*a1157835SDaniel Fojt if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED)
580*a1157835SDaniel Fojt cache->vlan_id.notempty = !!radius_msg_get_vlanid(
581*a1157835SDaniel Fojt msg, &cache->vlan_id.untagged,
582*a1157835SDaniel Fojt MAX_NUM_TAGGED_VLAN, cache->vlan_id.tagged);
5833ff40c12SJohn Marino
5843ff40c12SJohn Marino decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
5853ff40c12SJohn Marino msg, req, cache);
5863ff40c12SJohn Marino
5873ff40c12SJohn Marino if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME,
5883ff40c12SJohn Marino &buf, &len, NULL) == 0) {
5893ff40c12SJohn Marino cache->identity = os_zalloc(len + 1);
5903ff40c12SJohn Marino if (cache->identity)
5913ff40c12SJohn Marino os_memcpy(cache->identity, buf, len);
5923ff40c12SJohn Marino }
5933ff40c12SJohn Marino if (radius_msg_get_attr_ptr(
5943ff40c12SJohn Marino msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
5953ff40c12SJohn Marino &buf, &len, NULL) == 0) {
5963ff40c12SJohn Marino cache->radius_cui = os_zalloc(len + 1);
5973ff40c12SJohn Marino if (cache->radius_cui)
5983ff40c12SJohn Marino os_memcpy(cache->radius_cui, buf, len);
5993ff40c12SJohn Marino }
6003ff40c12SJohn Marino
6013ff40c12SJohn Marino if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED &&
6023ff40c12SJohn Marino !cache->psk)
6033ff40c12SJohn Marino cache->accepted = HOSTAPD_ACL_REJECT;
604*a1157835SDaniel Fojt
605*a1157835SDaniel Fojt if (cache->vlan_id.notempty &&
606*a1157835SDaniel Fojt !hostapd_vlan_valid(hapd->conf->vlan, &cache->vlan_id)) {
607*a1157835SDaniel Fojt hostapd_logger(hapd, query->addr,
608*a1157835SDaniel Fojt HOSTAPD_MODULE_RADIUS,
609*a1157835SDaniel Fojt HOSTAPD_LEVEL_INFO,
610*a1157835SDaniel Fojt "Invalid VLAN %d%s received from RADIUS server",
611*a1157835SDaniel Fojt cache->vlan_id.untagged,
612*a1157835SDaniel Fojt cache->vlan_id.tagged[0] ? "+" : "");
613*a1157835SDaniel Fojt os_memset(&cache->vlan_id, 0, sizeof(cache->vlan_id));
614*a1157835SDaniel Fojt }
615*a1157835SDaniel Fojt if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
616*a1157835SDaniel Fojt !cache->vlan_id.notempty)
617*a1157835SDaniel Fojt cache->accepted = HOSTAPD_ACL_REJECT;
6183ff40c12SJohn Marino } else
6193ff40c12SJohn Marino cache->accepted = HOSTAPD_ACL_REJECT;
6203ff40c12SJohn Marino cache->next = hapd->acl_cache;
6213ff40c12SJohn Marino hapd->acl_cache = cache;
6223ff40c12SJohn Marino
6233ff40c12SJohn Marino #ifdef CONFIG_DRIVER_RADIUS_ACL
6243ff40c12SJohn Marino hostapd_drv_set_radius_acl_auth(hapd, query->addr, cache->accepted,
6253ff40c12SJohn Marino cache->session_timeout);
6263ff40c12SJohn Marino #else /* CONFIG_DRIVER_RADIUS_ACL */
6273ff40c12SJohn Marino #ifdef NEED_AP_MLME
6283ff40c12SJohn Marino /* Re-send original authentication frame for 802.11 processing */
6293ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "Re-sending authentication frame after "
6303ff40c12SJohn Marino "successful RADIUS ACL query");
6313ff40c12SJohn Marino ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL);
6323ff40c12SJohn Marino #endif /* NEED_AP_MLME */
6333ff40c12SJohn Marino #endif /* CONFIG_DRIVER_RADIUS_ACL */
6343ff40c12SJohn Marino
6353ff40c12SJohn Marino done:
6363ff40c12SJohn Marino if (prev == NULL)
6373ff40c12SJohn Marino hapd->acl_queries = query->next;
6383ff40c12SJohn Marino else
6393ff40c12SJohn Marino prev->next = query->next;
6403ff40c12SJohn Marino
6413ff40c12SJohn Marino hostapd_acl_query_free(query);
6423ff40c12SJohn Marino
6433ff40c12SJohn Marino return RADIUS_RX_PROCESSED;
6443ff40c12SJohn Marino }
6453ff40c12SJohn Marino #endif /* CONFIG_NO_RADIUS */
6463ff40c12SJohn Marino
6473ff40c12SJohn Marino
6483ff40c12SJohn Marino /**
6493ff40c12SJohn Marino * hostapd_acl_init: Initialize IEEE 802.11 ACL
6503ff40c12SJohn Marino * @hapd: hostapd BSS data
6513ff40c12SJohn Marino * Returns: 0 on success, -1 on failure
6523ff40c12SJohn Marino */
hostapd_acl_init(struct hostapd_data * hapd)6533ff40c12SJohn Marino int hostapd_acl_init(struct hostapd_data *hapd)
6543ff40c12SJohn Marino {
6553ff40c12SJohn Marino #ifndef CONFIG_NO_RADIUS
6563ff40c12SJohn Marino if (radius_client_register(hapd->radius, RADIUS_AUTH,
6573ff40c12SJohn Marino hostapd_acl_recv_radius, hapd))
6583ff40c12SJohn Marino return -1;
6593ff40c12SJohn Marino #endif /* CONFIG_NO_RADIUS */
6603ff40c12SJohn Marino
6613ff40c12SJohn Marino return 0;
6623ff40c12SJohn Marino }
6633ff40c12SJohn Marino
6643ff40c12SJohn Marino
6653ff40c12SJohn Marino /**
6663ff40c12SJohn Marino * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL
6673ff40c12SJohn Marino * @hapd: hostapd BSS data
6683ff40c12SJohn Marino */
hostapd_acl_deinit(struct hostapd_data * hapd)6693ff40c12SJohn Marino void hostapd_acl_deinit(struct hostapd_data *hapd)
6703ff40c12SJohn Marino {
6713ff40c12SJohn Marino struct hostapd_acl_query_data *query, *prev;
6723ff40c12SJohn Marino
6733ff40c12SJohn Marino #ifndef CONFIG_NO_RADIUS
6743ff40c12SJohn Marino hostapd_acl_cache_free(hapd->acl_cache);
675*a1157835SDaniel Fojt hapd->acl_cache = NULL;
6763ff40c12SJohn Marino #endif /* CONFIG_NO_RADIUS */
6773ff40c12SJohn Marino
6783ff40c12SJohn Marino query = hapd->acl_queries;
679*a1157835SDaniel Fojt hapd->acl_queries = NULL;
6803ff40c12SJohn Marino while (query) {
6813ff40c12SJohn Marino prev = query;
6823ff40c12SJohn Marino query = query->next;
6833ff40c12SJohn Marino hostapd_acl_query_free(prev);
6843ff40c12SJohn Marino }
6853ff40c12SJohn Marino }
6863ff40c12SJohn Marino
6873ff40c12SJohn Marino
hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short * psk)6883ff40c12SJohn Marino void hostapd_free_psk_list(struct hostapd_sta_wpa_psk_short *psk)
6893ff40c12SJohn Marino {
690*a1157835SDaniel Fojt if (psk && psk->ref) {
691*a1157835SDaniel Fojt /* This will be freed when the last reference is dropped. */
692*a1157835SDaniel Fojt psk->ref--;
693*a1157835SDaniel Fojt return;
694*a1157835SDaniel Fojt }
695*a1157835SDaniel Fojt
6963ff40c12SJohn Marino while (psk) {
6973ff40c12SJohn Marino struct hostapd_sta_wpa_psk_short *prev = psk;
6983ff40c12SJohn Marino psk = psk->next;
6993ff40c12SJohn Marino os_free(prev);
7003ff40c12SJohn Marino }
7013ff40c12SJohn Marino }
702