LCOV - code coverage report
Current view: top level - src/ap - pmksa_cache_auth.c (source / functions) Hit Total Coverage
Test: wpa_supplicant/hostapd combined for hwsim test run 1401264779 Lines: 171 196 87.2 %
Date: 2014-05-28 Functions: 12 13 92.3 %

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

Generated by: LCOV version 1.10