LCOV - code coverage report
Current view: top level - ap - pmksa_cache_auth.c (source / functions) Hit Total Coverage
Test: hostapd hwsim test run 1412854115 Lines: 187 197 94.9 %
Date: 2014-10-09 Functions: 13 13 100.0 %

          Line data    Source code
       1             : /*
       2             :  * hostapd - PMKSA cache for IEEE 802.11i RSN
       3             :  * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
       4             :  *
       5             :  * This software may be distributed under the terms of the BSD license.
       6             :  * See README for more details.
       7             :  */
       8             : 
       9             : #include "utils/includes.h"
      10             : 
      11             : #include "utils/common.h"
      12             : #include "utils/eloop.h"
      13             : #include "eapol_auth/eapol_auth_sm.h"
      14             : #include "eapol_auth/eapol_auth_sm_i.h"
      15             : #include "sta_info.h"
      16             : #include "ap_config.h"
      17             : #include "pmksa_cache_auth.h"
      18             : 
      19             : 
      20             : static const int pmksa_cache_max_entries = 1024;
      21             : static const int dot11RSNAConfigPMKLifetime = 43200;
      22             : 
      23             : struct rsn_pmksa_cache {
      24             : #define PMKID_HASH_SIZE 128
      25             : #define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
      26             :         struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
      27             :         struct rsn_pmksa_cache_entry *pmksa;
      28             :         int pmksa_count;
      29             : 
      30             :         void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
      31             :         void *ctx;
      32             : };
      33             : 
      34             : 
      35             : static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
      36             : 
      37             : 
      38         302 : static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
      39             : {
      40         302 :         os_free(entry->identity);
      41         302 :         wpabuf_free(entry->cui);
      42             : #ifndef CONFIG_NO_RADIUS
      43         302 :         radius_free_class(&entry->radius_class);
      44             : #endif /* CONFIG_NO_RADIUS */
      45         302 :         bin_clear_free(entry, sizeof(*entry));
      46         302 : }
      47             : 
      48             : 
      49         107 : void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
      50             :                             struct rsn_pmksa_cache_entry *entry)
      51             : {
      52             :         struct rsn_pmksa_cache_entry *pos, *prev;
      53             :         unsigned int hash;
      54             : 
      55         107 :         pmksa->pmksa_count--;
      56         107 :         pmksa->free_cb(entry, pmksa->ctx);
      57             : 
      58             :         /* unlink from hash list */
      59         107 :         hash = PMKID_HASH(entry->pmkid);
      60         107 :         pos = pmksa->pmkid[hash];
      61         107 :         prev = NULL;
      62         215 :         while (pos) {
      63         108 :                 if (pos == entry) {
      64         107 :                         if (prev != NULL)
      65           1 :                                 prev->hnext = entry->hnext;
      66             :                         else
      67         106 :                                 pmksa->pmkid[hash] = entry->hnext;
      68         107 :                         break;
      69             :                 }
      70           1 :                 prev = pos;
      71           1 :                 pos = pos->hnext;
      72             :         }
      73             : 
      74             :         /* unlink from entry list */
      75         107 :         pos = pmksa->pmksa;
      76         107 :         prev = NULL;
      77         218 :         while (pos) {
      78         111 :                 if (pos == entry) {
      79         107 :                         if (prev != NULL)
      80           4 :                                 prev->next = entry->next;
      81             :                         else
      82         103 :                                 pmksa->pmksa = entry->next;
      83         107 :                         break;
      84             :                 }
      85           4 :                 prev = pos;
      86           4 :                 pos = pos->next;
      87             :         }
      88             : 
      89         107 :         _pmksa_cache_free_entry(entry);
      90         107 : }
      91             : 
      92             : 
      93           3 : static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
      94             : {
      95           3 :         struct rsn_pmksa_cache *pmksa = eloop_ctx;
      96             :         struct os_reltime now;
      97             : 
      98           3 :         os_get_reltime(&now);
      99          11 :         while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
     100          30 :                 wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
     101          30 :                            MACSTR, MAC2STR(pmksa->pmksa->spa));
     102           5 :                 pmksa_cache_free_entry(pmksa, pmksa->pmksa);
     103             :         }
     104             : 
     105           3 :         pmksa_cache_set_expiration(pmksa);
     106           3 : }
     107             : 
     108             : 
     109         262 : static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
     110             : {
     111             :         int sec;
     112             :         struct os_reltime now;
     113             : 
     114         262 :         eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
     115         262 :         if (pmksa->pmksa == NULL)
     116         263 :                 return;
     117         261 :         os_get_reltime(&now);
     118         261 :         sec = pmksa->pmksa->expiration - now.sec;
     119         261 :         if (sec < 0)
     120           0 :                 sec = 0;
     121         261 :         eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
     122             : }
     123             : 
     124             : 
     125         296 : static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
     126             :                                         struct eapol_state_machine *eapol)
     127             : {
     128         296 :         if (eapol == NULL)
     129         296 :                 return;
     130             : 
     131         296 :         if (eapol->identity) {
     132         265 :                 entry->identity = os_malloc(eapol->identity_len);
     133         265 :                 if (entry->identity) {
     134         265 :                         entry->identity_len = eapol->identity_len;
     135         265 :                         os_memcpy(entry->identity, eapol->identity,
     136             :                                   eapol->identity_len);
     137             :                 }
     138             :         }
     139             : 
     140         296 :         if (eapol->radius_cui)
     141           5 :                 entry->cui = wpabuf_dup(eapol->radius_cui);
     142             : 
     143             : #ifndef CONFIG_NO_RADIUS
     144         296 :         radius_copy_class(&entry->radius_class, &eapol->radius_class);
     145             : #endif /* CONFIG_NO_RADIUS */
     146             : 
     147         296 :         entry->eap_type_authsrv = eapol->eap_type_authsrv;
     148         296 :         entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
     149             : }
     150             : 
     151             : 
     152          24 : void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
     153             :                                struct eapol_state_machine *eapol)
     154             : {
     155          24 :         if (entry == NULL || eapol == NULL)
     156          24 :                 return;
     157             : 
     158          24 :         if (entry->identity) {
     159          24 :                 os_free(eapol->identity);
     160          24 :                 eapol->identity = os_malloc(entry->identity_len);
     161          24 :                 if (eapol->identity) {
     162          24 :                         eapol->identity_len = entry->identity_len;
     163          24 :                         os_memcpy(eapol->identity, entry->identity,
     164             :                                   entry->identity_len);
     165             :                 }
     166          48 :                 wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
     167          24 :                                   eapol->identity, eapol->identity_len);
     168             :         }
     169             : 
     170          24 :         if (entry->cui) {
     171           1 :                 wpabuf_free(eapol->radius_cui);
     172           1 :                 eapol->radius_cui = wpabuf_dup(entry->cui);
     173             :         }
     174             : 
     175             : #ifndef CONFIG_NO_RADIUS
     176          24 :         radius_free_class(&eapol->radius_class);
     177          24 :         radius_copy_class(&eapol->radius_class, &entry->radius_class);
     178             : #endif /* CONFIG_NO_RADIUS */
     179          24 :         if (eapol->radius_class.attr) {
     180           1 :                 wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
     181             :                            "PMKSA", (unsigned long) eapol->radius_class.count);
     182             :         }
     183             : 
     184          24 :         eapol->eap_type_authsrv = entry->eap_type_authsrv;
     185          24 :         ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
     186             : }
     187             : 
     188             : 
     189         302 : static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
     190             :                                    struct rsn_pmksa_cache_entry *entry)
     191             : {
     192             :         struct rsn_pmksa_cache_entry *pos, *prev;
     193             :         int hash;
     194             : 
     195             :         /* Add the new entry; order by expiration time */
     196         302 :         pos = pmksa->pmksa;
     197         302 :         prev = NULL;
     198         661 :         while (pos) {
     199          62 :                 if (pos->expiration > entry->expiration)
     200           5 :                         break;
     201          57 :                 prev = pos;
     202          57 :                 pos = pos->next;
     203             :         }
     204         302 :         if (prev == NULL) {
     205         259 :                 entry->next = pmksa->pmksa;
     206         259 :                 pmksa->pmksa = entry;
     207             :         } else {
     208          43 :                 entry->next = prev->next;
     209          43 :                 prev->next = entry;
     210             :         }
     211             : 
     212         302 :         hash = PMKID_HASH(entry->pmkid);
     213         302 :         entry->hnext = pmksa->pmkid[hash];
     214         302 :         pmksa->pmkid[hash] = entry;
     215             : 
     216         302 :         pmksa->pmksa_count++;
     217         302 :         if (prev == NULL)
     218         259 :                 pmksa_cache_set_expiration(pmksa);
     219        1812 :         wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
     220        1812 :                    MAC2STR(entry->spa));
     221         302 :         wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
     222         302 : }
     223             : 
     224             : 
     225             : /**
     226             :  * pmksa_cache_auth_add - Add a PMKSA cache entry
     227             :  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
     228             :  * @pmk: The new pairwise master key
     229             :  * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
     230             :  * @aa: Authenticator address
     231             :  * @spa: Supplicant address
     232             :  * @session_timeout: Session timeout
     233             :  * @eapol: Pointer to EAPOL state machine data
     234             :  * @akmp: WPA_KEY_MGMT_* used in key derivation
     235             :  * Returns: Pointer to the added PMKSA cache entry or %NULL on error
     236             :  *
     237             :  * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
     238             :  * cache. If an old entry is already in the cache for the same Supplicant,
     239             :  * this entry will be replaced with the new entry. PMKID will be calculated
     240             :  * based on the PMK.
     241             :  */
     242             : struct rsn_pmksa_cache_entry *
     243         296 : pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
     244             :                      const u8 *pmk, size_t pmk_len,
     245             :                 const u8 *aa, const u8 *spa, int session_timeout,
     246             :                 struct eapol_state_machine *eapol, int akmp)
     247             : {
     248             :         struct rsn_pmksa_cache_entry *entry, *pos;
     249             :         struct os_reltime now;
     250             : 
     251         296 :         if (pmk_len > PMK_LEN)
     252           0 :                 return NULL;
     253             : 
     254         296 :         entry = os_zalloc(sizeof(*entry));
     255         296 :         if (entry == NULL)
     256           0 :                 return NULL;
     257         296 :         os_memcpy(entry->pmk, pmk, pmk_len);
     258         296 :         entry->pmk_len = pmk_len;
     259         296 :         rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
     260             :                   wpa_key_mgmt_sha256(akmp));
     261         296 :         os_get_reltime(&now);
     262         296 :         entry->expiration = now.sec;
     263         296 :         if (session_timeout > 0)
     264         296 :                 entry->expiration += session_timeout;
     265             :         else
     266           0 :                 entry->expiration += dot11RSNAConfigPMKLifetime;
     267         296 :         entry->akmp = akmp;
     268         296 :         os_memcpy(entry->spa, spa, ETH_ALEN);
     269         296 :         pmksa_cache_from_eapol_data(entry, eapol);
     270             : 
     271             :         /* Replace an old entry for the same STA (if found) with the new entry
     272             :          */
     273         296 :         pos = pmksa_cache_auth_get(pmksa, spa, NULL);
     274         296 :         if (pos)
     275          96 :                 pmksa_cache_free_entry(pmksa, pos);
     276             : 
     277         296 :         if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
     278             :                 /* Remove the oldest entry to make room for the new entry */
     279           0 :                 wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
     280             :                            "entry (for " MACSTR ") to make room for new one",
     281           0 :                            MAC2STR(pmksa->pmksa->spa));
     282           0 :                 pmksa_cache_free_entry(pmksa, pmksa->pmksa);
     283             :         }
     284             : 
     285         296 :         pmksa_cache_link_entry(pmksa, entry);
     286             : 
     287         296 :         return entry;
     288             : }
     289             : 
     290             : 
     291             : struct rsn_pmksa_cache_entry *
     292           6 : pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
     293             :                     const struct rsn_pmksa_cache_entry *old_entry,
     294             :                     const u8 *aa, const u8 *pmkid)
     295             : {
     296             :         struct rsn_pmksa_cache_entry *entry;
     297             : 
     298           6 :         entry = os_zalloc(sizeof(*entry));
     299           6 :         if (entry == NULL)
     300           0 :                 return NULL;
     301           6 :         os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
     302           6 :         os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
     303           6 :         entry->pmk_len = old_entry->pmk_len;
     304           6 :         entry->expiration = old_entry->expiration;
     305           6 :         entry->akmp = old_entry->akmp;
     306           6 :         os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
     307           6 :         entry->opportunistic = 1;
     308           6 :         if (old_entry->identity) {
     309           6 :                 entry->identity = os_malloc(old_entry->identity_len);
     310           6 :                 if (entry->identity) {
     311           6 :                         entry->identity_len = old_entry->identity_len;
     312           6 :                         os_memcpy(entry->identity, old_entry->identity,
     313             :                                   old_entry->identity_len);
     314             :                 }
     315             :         }
     316           6 :         if (old_entry->cui)
     317           0 :                 entry->cui = wpabuf_dup(old_entry->cui);
     318             : #ifndef CONFIG_NO_RADIUS
     319           6 :         radius_copy_class(&entry->radius_class, &old_entry->radius_class);
     320             : #endif /* CONFIG_NO_RADIUS */
     321           6 :         entry->eap_type_authsrv = old_entry->eap_type_authsrv;
     322           6 :         entry->vlan_id = old_entry->vlan_id;
     323           6 :         entry->opportunistic = 1;
     324             : 
     325           6 :         pmksa_cache_link_entry(pmksa, entry);
     326             : 
     327           6 :         return entry;
     328             : }
     329             : 
     330             : 
     331             : /**
     332             :  * pmksa_cache_auth_deinit - Free all entries in PMKSA cache
     333             :  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
     334             :  */
     335         437 : void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
     336             : {
     337             :         struct rsn_pmksa_cache_entry *entry, *prev;
     338             :         int i;
     339             : 
     340         437 :         if (pmksa == NULL)
     341         437 :                 return;
     342             : 
     343         437 :         entry = pmksa->pmksa;
     344        1069 :         while (entry) {
     345         195 :                 prev = entry;
     346         195 :                 entry = entry->next;
     347         195 :                 _pmksa_cache_free_entry(prev);
     348             :         }
     349         437 :         eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
     350         437 :         pmksa->pmksa_count = 0;
     351         437 :         pmksa->pmksa = NULL;
     352       56373 :         for (i = 0; i < PMKID_HASH_SIZE; i++)
     353       55936 :                 pmksa->pmkid[i] = NULL;
     354         437 :         os_free(pmksa);
     355             : }
     356             : 
     357             : 
     358             : /**
     359             :  * pmksa_cache_auth_get - Fetch a PMKSA cache entry
     360             :  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
     361             :  * @spa: Supplicant address or %NULL to match any
     362             :  * @pmkid: PMKID or %NULL to match any
     363             :  * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
     364             :  */
     365             : struct rsn_pmksa_cache_entry *
     366         558 : pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
     367             :                      const u8 *spa, const u8 *pmkid)
     368             : {
     369             :         struct rsn_pmksa_cache_entry *entry;
     370             : 
     371         558 :         if (pmkid) {
     372         511 :                 for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
     373           1 :                      entry = entry->hnext) {
     374          38 :                         if ((spa == NULL ||
     375          37 :                              os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
     376          18 :                             os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
     377          18 :                                 return entry;
     378             :                 }
     379             :         } else {
     380         355 :                 for (entry = pmksa->pmksa; entry; entry = entry->next) {
     381         308 :                         if (spa == NULL ||
     382         154 :                             os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
     383         102 :                                 return entry;
     384             :                 }
     385             :         }
     386             : 
     387         438 :         return NULL;
     388             : }
     389             : 
     390             : 
     391             : /**
     392             :  * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC
     393             :  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
     394             :  * @aa: Authenticator address
     395             :  * @spa: Supplicant address
     396             :  * @pmkid: PMKID
     397             :  * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
     398             :  *
     399             :  * Use opportunistic key caching (OKC) to find a PMK for a supplicant.
     400             :  */
     401           6 : struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
     402             :         struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa,
     403             :         const u8 *pmkid)
     404             : {
     405             :         struct rsn_pmksa_cache_entry *entry;
     406             :         u8 new_pmkid[PMKID_LEN];
     407             : 
     408          12 :         for (entry = pmksa->pmksa; entry; entry = entry->next) {
     409          12 :                 if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
     410           6 :                         continue;
     411           6 :                 rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
     412             :                           wpa_key_mgmt_sha256(entry->akmp));
     413           6 :                 if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
     414           6 :                         return entry;
     415             :         }
     416           0 :         return NULL;
     417             : }
     418             : 
     419             : 
     420             : /**
     421             :  * pmksa_cache_auth_init - Initialize PMKSA cache
     422             :  * @free_cb: Callback function to be called when a PMKSA cache entry is freed
     423             :  * @ctx: Context pointer for free_cb function
     424             :  * Returns: Pointer to PMKSA cache data or %NULL on failure
     425             :  */
     426             : struct rsn_pmksa_cache *
     427         437 : pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
     428             :                                       void *ctx), void *ctx)
     429             : {
     430             :         struct rsn_pmksa_cache *pmksa;
     431             : 
     432         437 :         pmksa = os_zalloc(sizeof(*pmksa));
     433         437 :         if (pmksa) {
     434         437 :                 pmksa->free_cb = free_cb;
     435         437 :                 pmksa->ctx = ctx;
     436             :         }
     437             : 
     438         437 :         return pmksa;
     439             : }

Generated by: LCOV version 1.10