aboutsummaryrefslogtreecommitdiffstats
path: root/hostapd/pmksa_cache.c
diff options
context:
space:
mode:
authorJouni Malinen <j@w1.fi>2005-12-10 21:34:08 (GMT)
committerJouni Malinen <j@w1.fi>2005-12-10 21:34:08 (GMT)
commitce08a9dfe5634b244032a279653e2ce702708b3f (patch)
tree73c92cea90f86d8f851b6d4dd47754586043b0ce /hostapd/pmksa_cache.c
parente5015aaca78479cfeeb855f92e7b9f60b4446226 (diff)
downloadhostap-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.c285
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;
+}