From 2ad3e6c858526f5822a47741c57a7fe1423dc724 Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Sun, 25 Nov 2012 17:41:13 +0200 Subject: [PATCH] Cache a list of PSK entries for RADIUS-based PSK delivery Signed-hostap: Michael Braun --- src/ap/ap_config.h | 5 ++ src/ap/ieee802_11.c | 14 +++-- src/ap/ieee802_11_auth.c | 129 +++++++++++++++++++++++++++------------ src/ap/ieee802_11_auth.h | 4 +- 4 files changed, 105 insertions(+), 47 deletions(-) diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h index 010d21645..71313c023 100644 --- a/src/ap/ap_config.h +++ b/src/ap/ap_config.h @@ -96,6 +96,11 @@ struct hostapd_vlan { }; #define PMK_LEN 32 +struct hostapd_sta_wpa_psk_short { + struct hostapd_sta_wpa_psk_short *next; + u8 psk[PMK_LEN]; +}; + struct hostapd_wpa_psk { struct hostapd_wpa_psk *next; int group; diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c index 366cbb664..3bc7059db 100644 --- a/src/ap/ieee802_11.c +++ b/src/ap/ieee802_11.c @@ -461,8 +461,7 @@ static void handle_auth(struct hostapd_data *hapd, const u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; int vlan_id = 0; - u8 psk[PMK_LEN]; - int has_psk = 0; + struct hostapd_sta_wpa_psk_short *psk = NULL; u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; size_t resp_ies_len = 0; char *identity = NULL; @@ -532,7 +531,7 @@ static void handle_auth(struct hostapd_data *hapd, res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, &session_timeout, &acct_interim_interval, &vlan_id, - psk, &has_psk, &identity, &radius_cui); + &psk, &identity, &radius_cui); if (res == HOSTAPD_ACL_REJECT) { printf("Station " MACSTR " not allowed to authenticate.\n", @@ -571,11 +570,11 @@ static void handle_auth(struct hostapd_data *hapd, HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); } - if (has_psk && hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { + if (psk && hapd->conf->wpa_psk_radius != PSK_RADIUS_IGNORED) { os_free(sta->psk); sta->psk = os_malloc(PMK_LEN); if (sta->psk) - os_memcpy(sta->psk, psk, PMK_LEN); + os_memcpy(sta->psk, psk->psk, PMK_LEN); } else { os_free(sta->psk); sta->psk = NULL; @@ -654,6 +653,11 @@ static void handle_auth(struct hostapd_data *hapd, fail: os_free(identity); os_free(radius_cui); + while (psk) { + struct hostapd_sta_wpa_psk_short *prev = psk; + psk = psk->next; + os_free(prev); + } send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, auth_transaction + 1, resp, resp_ies, resp_ies_len); diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c index 12b65b593..0a406c748 100644 --- a/src/ap/ieee802_11_auth.c +++ b/src/ap/ieee802_11_auth.c @@ -36,8 +36,7 @@ struct hostapd_cached_radius_acl { u32 session_timeout; u32 acct_interim_interval; int vlan_id; - int has_psk; - u8 psk[PMK_LEN]; + struct hostapd_sta_wpa_psk_short *psk; char *identity; char *radius_cui; }; @@ -56,8 +55,14 @@ struct hostapd_acl_query_data { #ifndef CONFIG_NO_RADIUS static void hostapd_acl_cache_free_entry(struct hostapd_cached_radius_acl *e) { + struct hostapd_sta_wpa_psk_short *psk = e->psk; os_free(e->identity); os_free(e->radius_cui); + while (psk) { + struct hostapd_sta_wpa_psk_short *prev = psk; + psk = psk->next; + os_free(prev); + } os_free(e); } @@ -74,11 +79,34 @@ static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) } +static void copy_psk_list(struct hostapd_sta_wpa_psk_short **psk, + struct hostapd_sta_wpa_psk_short *src) +{ + struct hostapd_sta_wpa_psk_short **copy_to; + struct hostapd_sta_wpa_psk_short *copy_from; + + /* Copy PSK linked list */ + copy_to = psk; + copy_from = src; + while (copy_from && copy_to) { + *copy_to = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); + if (*copy_to == NULL) + break; + os_memcpy(*copy_to, copy_from, + sizeof(struct hostapd_sta_wpa_psk_short)); + copy_from = copy_from->next; + copy_to = &((*copy_to)->next); + } + if (copy_to) + *copy_to = NULL; +} + + static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, u32 *session_timeout, u32 *acct_interim_interval, int *vlan_id, - u8 *psk, int *has_psk, char **identity, - char **radius_cui) + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) { struct hostapd_cached_radius_acl *entry; struct os_time now; @@ -99,10 +127,7 @@ static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, entry->acct_interim_interval; if (vlan_id) *vlan_id = entry->vlan_id; - if (psk) - os_memcpy(psk, entry->psk, PMK_LEN); - if (has_psk) - *has_psk = entry->has_psk; + copy_psk_list(psk, entry->psk); if (identity) { if (entry->identity) *identity = os_strdup(entry->identity); @@ -200,8 +225,7 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, * @session_timeout: Buffer for returning session timeout (from RADIUS) * @acct_interim_interval: Buffer for returning account interval (from RADIUS) * @vlan_id: Buffer for returning VLAN ID - * @psk: Buffer for returning WPA PSK - * @has_psk: Buffer for indicating whether psk was filled + * @psk: Linked list buffer for returning WPA PSK * @identity: Buffer for returning identity (from RADIUS) * @radius_cui: Buffer for returning CUI (from RADIUS) * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING @@ -212,8 +236,8 @@ static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, u32 *acct_interim_interval, int *vlan_id, - u8 *psk, int *has_psk, char **identity, - char **radius_cui) + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui) { if (session_timeout) *session_timeout = 0; @@ -221,10 +245,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, *acct_interim_interval = 0; if (vlan_id) *vlan_id = 0; - if (has_psk) - *has_psk = 0; if (psk) - os_memset(psk, 0, PMK_LEN); + *psk = NULL; if (identity) *identity = NULL; if (radius_cui) @@ -253,7 +275,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, /* Check whether ACL cache has an entry for this station */ int res = hostapd_acl_cache_get(hapd, addr, session_timeout, acct_interim_interval, - vlan_id, psk, has_psk, + vlan_id, psk, identity, radius_cui); if (res == HOSTAPD_ACL_ACCEPT || res == HOSTAPD_ACL_ACCEPT_TIMEOUT) @@ -396,6 +418,54 @@ static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) } +static void decode_tunnel_passwords(struct hostapd_data *hapd, + struct radius_msg *msg, + struct radius_msg *req, + struct hostapd_cached_radius_acl *cache) +{ + int passphraselen; + char *passphrase, *strpassphrase; + size_t i; + struct hostapd_sta_wpa_psk_short *psk; + + /* + * Decode all tunnel passwords as PSK and save them into a linked list. + */ + for (i = 0; ; i++) { + passphrase = radius_msg_get_tunnel_password( + msg, &passphraselen, + hapd->conf->radius->auth_server->shared_secret, + hapd->conf->radius->auth_server->shared_secret_len, + req, i); + /* + * Passphrase is NULL iff there is no i-th Tunnel-Password + * attribute in msg. + */ + if (passphrase == NULL) + break; + /* + * passphrase does not contain the NULL termination. + * Add it here as pbkdf2_sha1() requires it. + */ + strpassphrase = os_zalloc(passphraselen + 1); + psk = os_zalloc(sizeof(struct hostapd_sta_wpa_psk_short)); + if (strpassphrase && psk) { + os_memcpy(strpassphrase, passphrase, passphraselen); + pbkdf2_sha1(strpassphrase, + hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len, 4096, + psk->psk, PMK_LEN); + psk->next = cache->psk; + cache->psk = psk; + psk = NULL; + } + os_free(strpassphrase); + os_free(psk); + os_free(passphrase); + } +} + + /** * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages * @msg: RADIUS response message @@ -454,8 +524,6 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, cache->timestamp = t.sec; os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { - int passphraselen; - char *passphrase; u8 *buf; size_t len; @@ -478,27 +546,8 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, cache->vlan_id = radius_msg_get_vlanid(msg); - passphrase = radius_msg_get_tunnel_password( - msg, &passphraselen, - hapd->conf->radius->auth_server->shared_secret, - hapd->conf->radius->auth_server->shared_secret_len, - req, 0); - cache->has_psk = passphrase != NULL; - if (passphrase != NULL) { - /* passphrase does not contain the NULL termination. - * Add it here as pbkdf2_sha1 requires it. */ - char *strpassphrase = os_zalloc(passphraselen + 1); - if (strpassphrase) { - os_memcpy(strpassphrase, passphrase, - passphraselen); - pbkdf2_sha1(strpassphrase, - hapd->conf->ssid.ssid, - hapd->conf->ssid.ssid_len, 4096, - cache->psk, PMK_LEN); - os_free(strpassphrase); - } - os_free(passphrase); - } + decode_tunnel_passwords(hapd, msg, req, cache); + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len, NULL) == 0) { cache->identity = os_zalloc(len + 1); @@ -514,7 +563,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, } if (hapd->conf->wpa_psk_radius == PSK_RADIUS_REQUIRED && - !cache->has_psk) + !cache->psk) cache->accepted = HOSTAPD_ACL_REJECT; } else cache->accepted = HOSTAPD_ACL_REJECT; diff --git a/src/ap/ieee802_11_auth.h b/src/ap/ieee802_11_auth.h index 0e8d1cb1b..7bbc09ae4 100644 --- a/src/ap/ieee802_11_auth.h +++ b/src/ap/ieee802_11_auth.h @@ -19,8 +19,8 @@ enum { int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, const u8 *msg, size_t len, u32 *session_timeout, u32 *acct_interim_interval, int *vlan_id, - u8 *psk, int *has_psk, char **identity, - char **radius_cui); + struct hostapd_sta_wpa_psk_short **psk, + char **identity, char **radius_cui); int hostapd_acl_init(struct hostapd_data *hapd); void hostapd_acl_deinit(struct hostapd_data *hapd);