diff options
author | Jouni Malinen <j@w1.fi> | 2005-12-10 21:34:08 (GMT) |
---|---|---|
committer | Jouni Malinen <j@w1.fi> | 2005-12-10 21:34:08 (GMT) |
commit | ce08a9dfe5634b244032a279653e2ce702708b3f (patch) | |
tree | 73c92cea90f86d8f851b6d4dd47754586043b0ce /hostapd/pmksa_cache.c | |
parent | e5015aaca78479cfeeb855f92e7b9f60b4446226 (diff) | |
download | hostap-history-ce08a9dfe5634b244032a279653e2ce702708b3f.zip hostap-history-ce08a9dfe5634b244032a279653e2ce702708b3f.tar.gz hostap-history-ce08a9dfe5634b244032a279653e2ce702708b3f.tar.bz2 |
Moved RSN PMKSA cache into its own file, pmksa_cache.c, so that
wpa.c is dedicated for WPA authenticator state machines.
Diffstat (limited to 'hostapd/pmksa_cache.c')
-rw-r--r-- | hostapd/pmksa_cache.c | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/hostapd/pmksa_cache.c b/hostapd/pmksa_cache.c new file mode 100644 index 0000000..384ebfd --- /dev/null +++ b/hostapd/pmksa_cache.c @@ -0,0 +1,285 @@ +/* + * hostapd - PMKSA cache for IEEE 802.11i RSN + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "hostapd.h" +#include "sha1.h" +#include "eloop.h" +#include "ieee802_1x.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "pmksa_cache.h" + + +static const int pmksa_cache_max_entries = 1024; +static const int dot11RSNAConfigPMKLifetime = 43200; + + +static void rsn_pmkid(const u8 *pmk, const u8 *aa, const u8 *spa, u8 *pmkid) +{ + char *title = "PMK Name"; + const u8 *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA1_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = aa; + addr[2] = spa; + + hmac_sha1_vector(pmk, PMK_LEN, 3, addr, len, hash); + memcpy(pmkid, hash, PMKID_LEN); +} + + +static void pmksa_cache_set_expiration(struct hostapd_data *hapd); + + +void _pmksa_cache_free_entry(struct rsn_pmksa_cache *entry) +{ + if (entry == NULL) + return; + free(entry->identity); + ieee802_1x_free_radius_class(&entry->radius_class); + free(entry); +} + + +static void pmksa_cache_free_entry(struct hostapd_data *hapd, + struct rsn_pmksa_cache *entry) +{ + struct sta_info *sta; + struct rsn_pmksa_cache *pos, *prev; + hapd->pmksa_count--; + for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { + if (sta->pmksa == entry) + sta->pmksa = NULL; + } + pos = hapd->pmkid[PMKID_HASH(entry->pmkid)]; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) { + prev->hnext = pos->hnext; + } else { + hapd->pmkid[PMKID_HASH(entry->pmkid)] = + pos->hnext; + } + break; + } + prev = pos; + pos = pos->hnext; + } + + pos = hapd->pmksa; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) + prev->next = pos->next; + else + hapd->pmksa = pos->next; + break; + } + prev = pos; + pos = pos->next; + } + _pmksa_cache_free_entry(entry); +} + + +static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + time_t now; + + time(&now); + while (hapd->pmksa && hapd->pmksa->expiration <= now) { + struct rsn_pmksa_cache *entry = hapd->pmksa; + hapd->pmksa = entry->next; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "RSN: expired PMKSA cache entry for " + MACSTR, MAC2STR(entry->spa)); + pmksa_cache_free_entry(hapd, entry); + } + + pmksa_cache_set_expiration(hapd); +} + + +static void pmksa_cache_set_expiration(struct hostapd_data *hapd) +{ + int sec; + eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); + if (hapd->pmksa == NULL) + return; + sec = hapd->pmksa->expiration - time(NULL); + if (sec < 0) + sec = 0; + eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, hapd, NULL); +} + + +static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache *entry, + struct eapol_state_machine *eapol) +{ + if (eapol == NULL) + return; + + if (eapol->identity) { + entry->identity = malloc(eapol->identity_len); + if (entry->identity) { + entry->identity_len = eapol->identity_len; + memcpy(entry->identity, eapol->identity, + eapol->identity_len); + } + } + + ieee802_1x_copy_radius_class(&entry->radius_class, + &eapol->radius_class); +} + + +void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache *entry, + struct eapol_state_machine *eapol) +{ + if (entry == NULL || eapol == NULL) + return; + + if (entry->identity) { + free(eapol->identity); + eapol->identity = malloc(entry->identity_len); + if (eapol->identity) { + eapol->identity_len = entry->identity_len; + memcpy(eapol->identity, entry->identity, + entry->identity_len); + } + wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", + eapol->identity, eapol->identity_len); + } + + ieee802_1x_free_radius_class(&eapol->radius_class); + ieee802_1x_copy_radius_class(&eapol->radius_class, + &entry->radius_class); + if (eapol->radius_class.attr) { + wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " + "PMKSA", (unsigned long) eapol->radius_class.count); + } +} + + +void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk, + int session_timeout) +{ + struct rsn_pmksa_cache *entry, *pos, *prev; + + if (sta->wpa != WPA_VERSION_WPA2) + return; + + entry = malloc(sizeof(*entry)); + if (entry == NULL) + return; + memset(entry, 0, sizeof(*entry)); + memcpy(entry->pmk, pmk, PMK_LEN); + rsn_pmkid(pmk, hapd->own_addr, sta->addr, entry->pmkid); + time(&entry->expiration); + if (session_timeout > 0) + entry->expiration += session_timeout; + else + entry->expiration += dot11RSNAConfigPMKLifetime; + entry->akmp = WPA_KEY_MGMT_IEEE8021X; + memcpy(entry->spa, sta->addr, ETH_ALEN); + pmksa_cache_from_eapol_data(entry, sta->eapol_sm); + + /* Replace an old entry for the same STA (if found) with the new entry + */ + pos = pmksa_cache_get(hapd, sta->addr, NULL); + if (pos) + pmksa_cache_free_entry(hapd, pos); + + if (hapd->pmksa_count >= pmksa_cache_max_entries && hapd->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "RSN: removed the oldest PMKSA cache entry (for " + MACSTR ") to make room for new one", + MAC2STR(hapd->pmksa->spa)); + pmksa_cache_free_entry(hapd, hapd->pmksa); + } + + /* Add the new entry; order by expiration time */ + pos = hapd->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = hapd->pmksa; + hapd->pmksa = entry; + } else { + entry->next = prev->next; + prev->next = entry; + } + entry->hnext = hapd->pmkid[PMKID_HASH(entry->pmkid)]; + hapd->pmkid[PMKID_HASH(entry->pmkid)] = entry; + + hapd->pmksa_count++; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "added PMKSA cache entry"); + wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); +} + + +void pmksa_cache_free(struct hostapd_data *hapd) +{ + struct rsn_pmksa_cache *entry, *prev; + int i; + struct sta_info *sta; + + entry = hapd->pmksa; + hapd->pmksa = NULL; + while (entry) { + prev = entry; + entry = entry->next; + _pmksa_cache_free_entry(prev); + } + eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); + for (i = 0; i < PMKID_HASH_SIZE; i++) + hapd->pmkid[i] = NULL; + for (sta = hapd->sta_list; sta; sta = sta->next) + sta->pmksa = NULL; +} + + +struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd, + const u8 *spa, const u8 *pmkid) +{ + struct rsn_pmksa_cache *entry; + + if (pmkid) + entry = hapd->pmkid[PMKID_HASH(pmkid)]; + else + entry = hapd->pmksa; + while (entry) { + if ((spa == NULL || memcmp(entry->spa, spa, ETH_ALEN) == 0) && + (pmkid == NULL || + memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + return entry; + entry = pmkid ? entry->hnext : entry->next; + } + return NULL; +} |