pmksa_cache.c

Go to the documentation of this file.
00001 
00016 #include "includes.h"
00017 
00018 #include "common.h"
00019 #include "wpa.h"
00020 #include "eloop.h"
00021 #include "sha1.h"
00022 #include "sha256.h"
00023 #include "wpa_i.h"
00024 #include "eapol_supp/eapol_supp_sm.h"
00025 #include "pmksa_cache.h"
00026 
00027 #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
00028 
00029 static const int pmksa_cache_max_entries = 32;
00030 
00031 struct rsn_pmksa_cache {
00032         struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
00033         int pmksa_count; /* number of entries in PMKSA cache */
00034         struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
00035 
00036         void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
00037                         int replace);
00038         void *ctx;
00039 };
00040 
00041 
00042 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
00043 
00044 
00045 static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
00046 {
00047         os_free(entry);
00048 }
00049 
00050 
00051 static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
00052                                    struct rsn_pmksa_cache_entry *entry,
00053                                    int replace)
00054 {
00055         pmksa->pmksa_count--;
00056         pmksa->free_cb(entry, pmksa->ctx, replace);
00057         _pmksa_cache_free_entry(entry);
00058 }
00059 
00060 
00061 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
00062 {
00063         struct rsn_pmksa_cache *pmksa = eloop_ctx;
00064         struct os_time now;
00065 
00066         os_get_time(&now);
00067         while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
00068                 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
00069                 pmksa->pmksa = entry->next;
00070                 wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
00071                            MACSTR, MAC2STR(entry->aa));
00072                 pmksa_cache_free_entry(pmksa, entry, 0);
00073         }
00074 
00075         pmksa_cache_set_expiration(pmksa);
00076 }
00077 
00078 
00079 static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
00080 {
00081         struct rsn_pmksa_cache *pmksa = eloop_ctx;
00082         pmksa->sm->cur_pmksa = NULL;
00083         eapol_sm_request_reauth(pmksa->sm->eapol);
00084 }
00085 
00086 
00087 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
00088 {
00089         int sec;
00090         struct rsn_pmksa_cache_entry *entry;
00091         struct os_time now;
00092 
00093         eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
00094         eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
00095         if (pmksa->pmksa == NULL)
00096                 return;
00097         os_get_time(&now);
00098         sec = pmksa->pmksa->expiration - now.sec;
00099         if (sec < 0)
00100                 sec = 0;
00101         eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
00102 
00103         entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
00104                 pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL);
00105         if (entry) {
00106                 sec = pmksa->pmksa->reauth_time - now.sec;
00107                 if (sec < 0)
00108                         sec = 0;
00109                 eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
00110                                        NULL);
00111         }
00112 }
00113 
00114 
00132 struct rsn_pmksa_cache_entry *
00133 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
00134                 const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
00135 {
00136         struct rsn_pmksa_cache_entry *entry, *pos, *prev;
00137         struct os_time now;
00138 
00139         if (pmk_len > PMK_LEN)
00140                 return NULL;
00141 
00142         entry = os_zalloc(sizeof(*entry));
00143         if (entry == NULL)
00144                 return NULL;
00145         os_memcpy(entry->pmk, pmk, pmk_len);
00146         entry->pmk_len = pmk_len;
00147         rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
00148                   wpa_key_mgmt_sha256(akmp));
00149         os_get_time(&now);
00150         entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
00151         entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
00152                 pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
00153         entry->akmp = akmp;
00154         os_memcpy(entry->aa, aa, ETH_ALEN);
00155         entry->network_ctx = network_ctx;
00156 
00157         /* Replace an old entry for the same Authenticator (if found) with the
00158          * new entry */
00159         pos = pmksa->pmksa;
00160         prev = NULL;
00161         while (pos) {
00162                 if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
00163                         if (pos->pmk_len == pmk_len &&
00164                             os_memcmp(pos->pmk, pmk, pmk_len) == 0 &&
00165                             os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) ==
00166                             0) {
00167                                 wpa_printf(MSG_DEBUG, "WPA: reusing previous "
00168                                            "PMKSA entry");
00169                                 os_free(entry);
00170                                 return pos;
00171                         }
00172                         if (prev == NULL)
00173                                 pmksa->pmksa = pos->next;
00174                         else
00175                                 prev->next = pos->next;
00176                         if (pos == pmksa->sm->cur_pmksa) {
00177                                 /* We are about to replace the current PMKSA
00178                                  * cache entry. This happens when the PMKSA
00179                                  * caching attempt fails, so we don't want to
00180                                  * force pmksa_cache_free_entry() to disconnect
00181                                  * at this point. Let's just make sure the old
00182                                  * PMKSA cache entry will not be used in the
00183                                  * future.
00184                                  */
00185                                 wpa_printf(MSG_DEBUG, "RSN: replacing current "
00186                                            "PMKSA entry");
00187                                 pmksa->sm->cur_pmksa = NULL;
00188                         }
00189                         wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
00190                                    "the current AP");
00191                         pmksa_cache_free_entry(pmksa, pos, 1);
00192                         break;
00193                 }
00194                 prev = pos;
00195                 pos = pos->next;
00196         }
00197 
00198         if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
00199                 /* Remove the oldest entry to make room for the new entry */
00200                 pos = pmksa->pmksa;
00201                 pmksa->pmksa = pos->next;
00202                 wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
00203                            "entry (for " MACSTR ") to make room for new one",
00204                            MAC2STR(pos->aa));
00205                 wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
00206                 pmksa_cache_free_entry(pmksa, pos, 0);
00207         }
00208 
00209         /* Add the new entry; order by expiration time */
00210         pos = pmksa->pmksa;
00211         prev = NULL;
00212         while (pos) {
00213                 if (pos->expiration > entry->expiration)
00214                         break;
00215                 prev = pos;
00216                 pos = pos->next;
00217         }
00218         if (prev == NULL) {
00219                 entry->next = pmksa->pmksa;
00220                 pmksa->pmksa = entry;
00221                 pmksa_cache_set_expiration(pmksa);
00222         } else {
00223                 entry->next = prev->next;
00224                 prev->next = entry;
00225         }
00226         pmksa->pmksa_count++;
00227         wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
00228                    MAC2STR(entry->aa));
00229         wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
00230 
00231         return entry;
00232 }
00233 
00234 
00240 void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
00241 {
00242         struct rsn_pmksa_cache_entry *entry, *prev;
00243 
00244         if (pmksa == NULL)
00245                 return;
00246 
00247         entry = pmksa->pmksa;
00248         pmksa->pmksa = NULL;
00249         while (entry) {
00250                 prev = entry;
00251                 entry = entry->next;
00252                 os_free(prev);
00253         }
00254         pmksa_cache_set_expiration(pmksa);
00255         os_free(pmksa);
00256 }
00257 
00258 
00267 struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
00268                                                const u8 *aa, const u8 *pmkid)
00269 {
00270         struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
00271         while (entry) {
00272                 if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
00273                     (pmkid == NULL ||
00274                      os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
00275                         return entry;
00276                 entry = entry->next;
00277         }
00278         return NULL;
00279 }
00280 
00281 
00289 void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
00290 {
00291         struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
00292         while (entry) {
00293                 entry->network_ctx = NULL;
00294                 entry = entry->next;
00295         }
00296 }
00297 
00298 
00299 static struct rsn_pmksa_cache_entry *
00300 pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
00301                         const struct rsn_pmksa_cache_entry *old_entry,
00302                         const u8 *aa)
00303 {
00304         struct rsn_pmksa_cache_entry *new_entry;
00305 
00306         new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
00307                                     aa, pmksa->sm->own_addr,
00308                                     old_entry->network_ctx, old_entry->akmp);
00309         if (new_entry == NULL)
00310                 return NULL;
00311 
00312         /* TODO: reorder entries based on expiration time? */
00313         new_entry->expiration = old_entry->expiration;
00314         new_entry->opportunistic = 1;
00315 
00316         return new_entry;
00317 }
00318 
00319 
00332 struct rsn_pmksa_cache_entry *
00333 pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
00334                               const u8 *aa)
00335 {
00336         struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
00337 
00338         if (network_ctx == NULL)
00339                 return NULL;
00340         while (entry) {
00341                 if (entry->network_ctx == network_ctx) {
00342                         entry = pmksa_cache_clone_entry(pmksa, entry, aa);
00343                         if (entry) {
00344                                 wpa_printf(MSG_DEBUG, "RSN: added "
00345                                            "opportunistic PMKSA cache entry "
00346                                            "for " MACSTR, MAC2STR(aa));
00347                         }
00348                         return entry;
00349                 }
00350                 entry = entry->next;
00351         }
00352         return NULL;
00353 }
00354 
00355 
00362 struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
00363 {
00364         if (sm == NULL)
00365                 return NULL;
00366         return sm->cur_pmksa;
00367 }
00368 
00369 
00375 void pmksa_cache_clear_current(struct wpa_sm *sm)
00376 {
00377         if (sm == NULL)
00378                 return;
00379         sm->cur_pmksa = NULL;
00380 }
00381 
00382 
00393 int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
00394                             const u8 *bssid, void *network_ctx,
00395                             int try_opportunistic)
00396 {
00397         struct rsn_pmksa_cache *pmksa = sm->pmksa;
00398         sm->cur_pmksa = NULL;
00399         if (pmkid)
00400                 sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid);
00401         if (sm->cur_pmksa == NULL && bssid)
00402                 sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL);
00403         if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
00404                 sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
00405                                                               network_ctx,
00406                                                               bssid);
00407         if (sm->cur_pmksa) {
00408                 wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
00409                             sm->cur_pmksa->pmkid, PMKID_LEN);
00410                 return 0;
00411         }
00412         return -1;
00413 }
00414 
00415 
00427 int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
00428 {
00429         int i, ret;
00430         char *pos = buf;
00431         struct rsn_pmksa_cache_entry *entry;
00432         struct os_time now;
00433 
00434         os_get_time(&now);
00435         ret = os_snprintf(pos, buf + len - pos,
00436                           "Index / AA / PMKID / expiration (in seconds) / "
00437                           "opportunistic\n");
00438         if (ret < 0 || ret >= buf + len - pos)
00439                 return pos - buf;
00440         pos += ret;
00441         i = 0;
00442         entry = pmksa->pmksa;
00443         while (entry) {
00444                 i++;
00445                 ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
00446                                   i, MAC2STR(entry->aa));
00447                 if (ret < 0 || ret >= buf + len - pos)
00448                         return pos - buf;
00449                 pos += ret;
00450                 pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
00451                                         PMKID_LEN);
00452                 ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
00453                                   (int) (entry->expiration - now.sec),
00454                                   entry->opportunistic);
00455                 if (ret < 0 || ret >= buf + len - pos)
00456                         return pos - buf;
00457                 pos += ret;
00458                 entry = entry->next;
00459         }
00460         return pos - buf;
00461 }
00462 
00463 
00472 struct rsn_pmksa_cache *
00473 pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
00474                                  void *ctx, int replace),
00475                  void *ctx, struct wpa_sm *sm)
00476 {
00477         struct rsn_pmksa_cache *pmksa;
00478 
00479         pmksa = os_zalloc(sizeof(*pmksa));
00480         if (pmksa) {
00481                 pmksa->free_cb = free_cb;
00482                 pmksa->ctx = ctx;
00483                 pmksa->sm = sm;
00484         }
00485 
00486         return pmksa;
00487 }
00488 
00489 #endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */
00490 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines

Generated on Sat Nov 21 23:16:48 2009 for hostapd by  doxygen 1.6.1