Added support for opportunistic key caching (OKC)

This allows hostapd to share the PMKSA caches internally when multiple
BSSes or radios are being controlled by the same hostapd process.
master
Jouni Malinen 16 years ago
parent 3ff77e070d
commit bf98f7f3bc

@ -12,6 +12,7 @@ ChangeLog for hostapd
* added fragmentation support for EAP-TNC
* added support for fragmenting EAP-TTLS/PEAP/FAST Phase 2 (tunneled)
data
* added support for opportunistic key caching (OKC)
2008-02-22 - v0.6.3
* fixed Reassociation Response callback processing when using internal

@ -1935,6 +1935,8 @@ struct hostapd_config * hostapd_config_read(const char *fname)
#endif /* CONFIG_IEEE80211W */
} else if (os_strcmp(buf, "max_listen_interval") == 0) {
bss->max_listen_interval = atoi(pos);
} else if (os_strcmp(buf, "okc") == 0) {
bss->okc = atoi(pos);
} else {
printf("Line %d: unknown configuration item '%s'\n",
line, buf);

@ -270,6 +270,8 @@ struct hostapd_bss_config {
* denied with status code 51.
*/
u16 max_listen_interval;
int okc; /* Opportunistic Key Caching */
};

@ -298,6 +298,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->eapol_version = conf->eapol_version;
wconf->peerkey = conf->peerkey;
wconf->wme_enabled = conf->wme_enabled;
wconf->okc = conf->okc;
#ifdef CONFIG_IEEE80211W
wconf->ieee80211w = conf->ieee80211w;
#endif /* CONFIG_IEEE80211W */
@ -891,6 +892,26 @@ static int hostapd_wpa_auth_for_each_sta(
}
static int hostapd_wpa_auth_for_each_auth(
void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx),
void *cb_ctx)
{
struct hostapd_data *ohapd;
size_t i, j;
struct hapd_interfaces *interfaces = eloop_get_user_data();
for (i = 0; i < interfaces->count; i++) {
for (j = 0; j < interfaces->iface[i]->num_bss; j++) {
ohapd = interfaces->iface[i]->bss[j];
if (cb(ohapd->wpa_auth, cb_ctx))
return 1;
}
}
return 0;
}
static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
const u8 *data, size_t data_len)
{
@ -1095,6 +1116,7 @@ static int hostapd_setup_wpa(struct hostapd_data *hapd)
cb.get_seqnum_igtk = hostapd_wpa_auth_get_seqnum_igtk;
cb.send_eapol = hostapd_wpa_auth_send_eapol;
cb.for_each_sta = hostapd_wpa_auth_for_each_sta;
cb.for_each_auth = hostapd_wpa_auth_for_each_auth;
cb.send_ether = hostapd_wpa_auth_send_ether;
#ifdef CONFIG_IEEE80211R
cb.send_ft_action = hostapd_wpa_auth_send_ft_action;

@ -691,6 +691,13 @@ own_ip_addr=127.0.0.1
# 2 = required
#ieee80211w=0
# okc: Opportunistic Key Caching (aka Proactive Key Caching)
# Allow PMK cache to be shared opportunistically among configured interfaces
# and BSSes (i.e., all configurations within a single hostapd process).
# 0 = disabled (default)
# 1 = enabled
#okc=1
##### IEEE 802.11r configuration ##############################################

@ -873,7 +873,7 @@ void ieee802_1x_free_radius_class(struct radius_class_data *class)
int ieee802_1x_copy_radius_class(struct radius_class_data *dst,
struct radius_class_data *src)
const struct radius_class_data *src)
{
size_t i;

@ -82,6 +82,6 @@ struct radius_class_data;
void ieee802_1x_free_radius_class(struct radius_class_data *class);
int ieee802_1x_copy_radius_class(struct radius_class_data *dst,
struct radius_class_data *src);
const struct radius_class_data *src);
#endif /* IEEE802_1X_H */

@ -208,6 +208,37 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
}
static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry)
{
struct rsn_pmksa_cache_entry *pos, *prev;
/* Add the new entry; order by expiration time */
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
if (pos->expiration > entry->expiration)
break;
prev = pos;
pos = pos->next;
}
if (prev == NULL) {
entry->next = pmksa->pmksa;
pmksa->pmksa = entry;
} else {
entry->next = prev->next;
prev->next = entry;
}
entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
pmksa->pmksa_count++;
wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
MAC2STR(entry->spa));
wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
}
/**
* pmksa_cache_add - Add a PMKSA cache entry
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
@ -229,7 +260,7 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
const u8 *aa, const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol)
{
struct rsn_pmksa_cache_entry *entry, *pos, *prev;
struct rsn_pmksa_cache_entry *entry, *pos;
struct os_time now;
if (pmk_len > PMK_LEN)
@ -265,29 +296,44 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
pmksa_cache_free_entry(pmksa, pmksa->pmksa);
}
/* Add the new entry; order by expiration time */
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
if (pos->expiration > entry->expiration)
break;
prev = pos;
pos = pos->next;
}
if (prev == NULL) {
entry->next = pmksa->pmksa;
pmksa->pmksa = entry;
} else {
entry->next = prev->next;
prev->next = entry;
pmksa_cache_link_entry(pmksa, entry);
return entry;
}
struct rsn_pmksa_cache_entry *
pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
const struct rsn_pmksa_cache_entry *old_entry,
const u8 *aa, const u8 *pmkid)
{
struct rsn_pmksa_cache_entry *entry;
entry = os_zalloc(sizeof(*entry));
if (entry == NULL)
return NULL;
os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
entry->pmk_len = old_entry->pmk_len;
entry->expiration = old_entry->expiration;
entry->akmp = old_entry->akmp;
os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
entry->opportunistic = 1;
if (old_entry->identity) {
entry->identity = os_malloc(old_entry->identity_len);
if (entry->identity) {
entry->identity_len = old_entry->identity_len;
os_memcpy(entry->identity, old_entry->identity,
old_entry->identity_len);
}
}
entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
ieee802_1x_copy_radius_class(&entry->radius_class,
&old_entry->radius_class);
entry->eap_type_authsrv = old_entry->eap_type_authsrv;
entry->vlan_id = old_entry->vlan_id;
entry->opportunistic = 1;
pmksa->pmksa_count++;
wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
MAC2STR(entry->spa));
wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
pmksa_cache_link_entry(pmksa, entry);
return entry;
}
@ -346,6 +392,35 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
}
/**
* pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
* @spa: Supplicant address
* @pmkid: PMKID
* Returns: Pointer to PMKSA cache entry or %NULL if no match was found
*
* Use opportunistic key caching (OKC) to find a PMK for a supplicant.
*/
struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
const u8 *pmkid)
{
struct rsn_pmksa_cache_entry *entry;
u8 new_pmkid[PMKID_LEN];
entry = pmksa->pmksa;
while (entry) {
if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
continue;
rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid);
if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
return entry;
entry = entry->next;
}
return NULL;
}
/**
* pmksa_cache_init - Initialize PMKSA cache
* @free_cb: Callback function to be called when a PMKSA cache entry is freed

@ -32,6 +32,7 @@ struct rsn_pmksa_cache_entry {
struct radius_class_data radius_class;
u8 eap_type_authsrv;
int vlan_id;
int opportunistic;
};
struct rsn_pmksa_cache;
@ -42,10 +43,17 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
const u8 *spa, const u8 *pmkid);
struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa,
const u8 *pmkid);
struct rsn_pmksa_cache_entry *
pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
const u8 *aa, const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol);
struct rsn_pmksa_cache_entry *
pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
const struct rsn_pmksa_cache_entry *old_entry,
const u8 *aa, const u8 *pmkid);
void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
struct eapol_state_machine *eapol);
void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,

@ -151,6 +151,16 @@ int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
}
int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_authenticator *a, void *ctx),
void *cb_ctx)
{
if (wpa_auth->cb.for_each_auth == NULL)
return 0;
return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx);
}
void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
logger_level level, const char *txt)
{

@ -140,6 +140,7 @@ struct wpa_auth_config {
int eapol_version;
int peerkey;
int wme_enabled;
int okc;
#ifdef CONFIG_IEEE80211W
enum {
WPA_NO_IEEE80211W = 0,
@ -192,6 +193,8 @@ struct wpa_auth_callbacks {
size_t data_len, int encrypt);
int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm,
void *ctx), void *cb_ctx);
int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a,
void *ctx), void *cb_ctx);
int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data,
size_t data_len);
#ifdef CONFIG_IEEE80211R

@ -189,6 +189,9 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_state_machine *sm, void *ctx),
void *cb_ctx);
int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
int (*cb)(struct wpa_authenticator *a, void *ctx),
void *cb_ctx);
#ifdef CONFIG_PEERKEY
int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,

@ -414,6 +414,25 @@ static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
}
struct wpa_auth_okc_iter_data {
struct rsn_pmksa_cache_entry *pmksa;
const u8 *aa;
const u8 *spa;
const u8 *pmkid;
};
static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
{
struct wpa_auth_okc_iter_data *data = ctx;
data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa,
data->pmkid);
if (data->pmksa)
return 1;
return 0;
}
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len,
@ -423,6 +442,7 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
int ciphers, key_mgmt, res, version;
u32 selector;
size_t i;
const u8 *pmkid = NULL;
if (wpa_auth == NULL || sm == NULL)
return WPA_NOT_ENABLED;
@ -615,22 +635,44 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
else
sm->wpa = WPA_VERSION_WPA;
sm->pmksa = NULL;
for (i = 0; i < data.num_pmkid; i++) {
wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
&data.pmkid[i * PMKID_LEN], PMKID_LEN);
sm->pmksa = pmksa_cache_get(wpa_auth->pmksa, sm->addr,
&data.pmkid[i * PMKID_LEN]);
if (sm->pmksa) {
pmkid = sm->pmksa->pmkid;
break;
}
}
for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc &&
i < data.num_pmkid; i++) {
struct wpa_auth_okc_iter_data idata;
idata.pmksa = NULL;
idata.aa = wpa_auth->addr;
idata.spa = sm->addr;
idata.pmkid = &data.pmkid[i * PMKID_LEN];
wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata);
if (idata.pmksa) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"PMKID found from PMKSA cache "
"eap_type=%d vlan_id=%d",
sm->pmksa->eap_type_authsrv,
sm->pmksa->vlan_id);
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed,
sm->pmksa->pmkid, PMKID_LEN);
"OKC match for PMKID");
sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa,
idata.pmksa,
wpa_auth->addr,
idata.pmkid);
pmkid = idata.pmkid;
break;
}
}
if (sm->pmksa) {
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
"PMKID found from PMKSA cache "
"eap_type=%d vlan_id=%d",
sm->pmksa->eap_type_authsrv,
sm->pmksa->vlan_id);
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
}
if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
os_free(sm->wpa_ie);

Loading…
Cancel
Save