LCOV - code coverage report
Current view: top level - src/ap - pmksa_cache_auth.c (source / functions) Hit Total Coverage
Test: hostapd hwsim test run 1388613141 Lines: 159 196 81.1 %
Date: 2014-01-02 Functions: 12 13 92.3 %
Branches: 50 98 51.0 %

           Branch data     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                 :         82 : static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
      39                 :            : {
      40         [ -  + ]:         82 :         if (entry == NULL)
      41                 :         82 :                 return;
      42                 :         82 :         os_free(entry->identity);
      43                 :         82 :         wpabuf_free(entry->cui);
      44                 :            : #ifndef CONFIG_NO_RADIUS
      45                 :         82 :         radius_free_class(&entry->radius_class);
      46                 :            : #endif /* CONFIG_NO_RADIUS */
      47                 :         82 :         os_free(entry);
      48                 :            : }
      49                 :            : 
      50                 :            : 
      51                 :         26 : 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                 :         26 :         pmksa->pmksa_count--;
      57                 :         26 :         pmksa->free_cb(entry, pmksa->ctx);
      58                 :         26 :         pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
      59                 :         26 :         prev = NULL;
      60         [ +  - ]:         26 :         while (pos) {
      61         [ +  - ]:         26 :                 if (pos == entry) {
      62         [ -  + ]:         26 :                         if (prev != NULL) {
      63                 :          0 :                                 prev->hnext = pos->hnext;
      64                 :            :                         } else {
      65                 :         26 :                                 pmksa->pmkid[PMKID_HASH(entry->pmkid)] =
      66                 :         26 :                                         pos->hnext;
      67                 :            :                         }
      68                 :         26 :                         break;
      69                 :            :                 }
      70                 :          0 :                 prev = pos;
      71                 :          0 :                 pos = pos->hnext;
      72                 :            :         }
      73                 :            : 
      74                 :         26 :         pos = pmksa->pmksa;
      75                 :         26 :         prev = NULL;
      76         [ +  - ]:         26 :         while (pos) {
      77         [ +  - ]:         26 :                 if (pos == entry) {
      78         [ -  + ]:         26 :                         if (prev != NULL)
      79                 :          0 :                                 prev->next = pos->next;
      80                 :            :                         else
      81                 :         26 :                                 pmksa->pmksa = pos->next;
      82                 :         26 :                         break;
      83                 :            :                 }
      84                 :          0 :                 prev = pos;
      85                 :          0 :                 pos = pos->next;
      86                 :            :         }
      87                 :         26 :         _pmksa_cache_free_entry(entry);
      88                 :         26 : }
      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                 :         82 : static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
     108                 :            : {
     109                 :            :         int sec;
     110                 :            :         struct os_reltime now;
     111                 :            : 
     112                 :         82 :         eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
     113         [ -  + ]:         82 :         if (pmksa->pmksa == NULL)
     114                 :         82 :                 return;
     115                 :         82 :         os_get_reltime(&now);
     116                 :         82 :         sec = pmksa->pmksa->expiration - now.sec;
     117         [ -  + ]:         82 :         if (sec < 0)
     118                 :          0 :                 sec = 0;
     119                 :         82 :         eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
     120                 :            : }
     121                 :            : 
     122                 :            : 
     123                 :         81 : static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
     124                 :            :                                         struct eapol_state_machine *eapol)
     125                 :            : {
     126         [ -  + ]:         81 :         if (eapol == NULL)
     127                 :         81 :                 return;
     128                 :            : 
     129         [ +  - ]:         81 :         if (eapol->identity) {
     130                 :         81 :                 entry->identity = os_malloc(eapol->identity_len);
     131         [ +  - ]:         81 :                 if (entry->identity) {
     132                 :         81 :                         entry->identity_len = eapol->identity_len;
     133                 :         81 :                         os_memcpy(entry->identity, eapol->identity,
     134                 :            :                                   eapol->identity_len);
     135                 :            :                 }
     136                 :            :         }
     137                 :            : 
     138         [ -  + ]:         81 :         if (eapol->radius_cui)
     139                 :          0 :                 entry->cui = wpabuf_dup(eapol->radius_cui);
     140                 :            : 
     141                 :            : #ifndef CONFIG_NO_RADIUS
     142                 :         81 :         radius_copy_class(&entry->radius_class, &eapol->radius_class);
     143                 :            : #endif /* CONFIG_NO_RADIUS */
     144                 :            : 
     145                 :         81 :         entry->eap_type_authsrv = eapol->eap_type_authsrv;
     146                 :         81 :         entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
     147                 :            : }
     148                 :            : 
     149                 :            : 
     150                 :          5 : void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
     151                 :            :                                struct eapol_state_machine *eapol)
     152                 :            : {
     153 [ +  - ][ -  + ]:          5 :         if (entry == NULL || eapol == NULL)
     154                 :          5 :                 return;
     155                 :            : 
     156         [ +  - ]:          5 :         if (entry->identity) {
     157                 :          5 :                 os_free(eapol->identity);
     158                 :          5 :                 eapol->identity = os_malloc(entry->identity_len);
     159         [ +  - ]:          5 :                 if (eapol->identity) {
     160                 :          5 :                         eapol->identity_len = entry->identity_len;
     161                 :          5 :                         os_memcpy(eapol->identity, entry->identity,
     162                 :            :                                   entry->identity_len);
     163                 :            :                 }
     164                 :          5 :                 wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA",
     165                 :          5 :                                   eapol->identity, eapol->identity_len);
     166                 :            :         }
     167                 :            : 
     168         [ -  + ]:          5 :         if (entry->cui) {
     169                 :          0 :                 wpabuf_free(eapol->radius_cui);
     170                 :          0 :                 eapol->radius_cui = wpabuf_dup(entry->cui);
     171                 :            :         }
     172                 :            : 
     173                 :            : #ifndef CONFIG_NO_RADIUS
     174                 :          5 :         radius_free_class(&eapol->radius_class);
     175                 :          5 :         radius_copy_class(&eapol->radius_class, &entry->radius_class);
     176                 :            : #endif /* CONFIG_NO_RADIUS */
     177         [ -  + ]:          5 :         if (eapol->radius_class.attr) {
     178                 :          0 :                 wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from "
     179                 :            :                            "PMKSA", (unsigned long) eapol->radius_class.count);
     180                 :            :         }
     181                 :            : 
     182                 :          5 :         eapol->eap_type_authsrv = entry->eap_type_authsrv;
     183                 :          5 :         ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
     184                 :            : }
     185                 :            : 
     186                 :            : 
     187                 :         82 : 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                 :         82 :         pos = pmksa->pmksa;
     194                 :         82 :         prev = NULL;
     195         [ -  + ]:         82 :         while (pos) {
     196         [ #  # ]:          0 :                 if (pos->expiration > entry->expiration)
     197                 :          0 :                         break;
     198                 :          0 :                 prev = pos;
     199                 :          0 :                 pos = pos->next;
     200                 :            :         }
     201         [ +  - ]:         82 :         if (prev == NULL) {
     202                 :         82 :                 entry->next = pmksa->pmksa;
     203                 :         82 :                 pmksa->pmksa = entry;
     204                 :            :         } else {
     205                 :          0 :                 entry->next = prev->next;
     206                 :          0 :                 prev->next = entry;
     207                 :            :         }
     208                 :         82 :         entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
     209                 :         82 :         pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
     210                 :            : 
     211                 :         82 :         pmksa->pmksa_count++;
     212         [ +  - ]:         82 :         if (prev == NULL)
     213                 :         82 :                 pmksa_cache_set_expiration(pmksa);
     214                 :         82 :         wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
     215                 :        492 :                    MAC2STR(entry->spa));
     216                 :         82 :         wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
     217                 :         82 : }
     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                 :         81 : 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         [ -  + ]:         81 :         if (pmk_len > PMK_LEN)
     247                 :          0 :                 return NULL;
     248                 :            : 
     249                 :         81 :         entry = os_zalloc(sizeof(*entry));
     250         [ -  + ]:         81 :         if (entry == NULL)
     251                 :          0 :                 return NULL;
     252                 :         81 :         os_memcpy(entry->pmk, pmk, pmk_len);
     253                 :         81 :         entry->pmk_len = pmk_len;
     254                 :         81 :         rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
     255                 :            :                   wpa_key_mgmt_sha256(akmp));
     256                 :         81 :         os_get_reltime(&now);
     257                 :         81 :         entry->expiration = now.sec;
     258         [ +  - ]:         81 :         if (session_timeout > 0)
     259                 :         81 :                 entry->expiration += session_timeout;
     260                 :            :         else
     261                 :          0 :                 entry->expiration += dot11RSNAConfigPMKLifetime;
     262                 :         81 :         entry->akmp = akmp;
     263                 :         81 :         os_memcpy(entry->spa, spa, ETH_ALEN);
     264                 :         81 :         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                 :         81 :         pos = pmksa_cache_auth_get(pmksa, spa, NULL);
     269         [ +  + ]:         81 :         if (pos)
     270                 :         26 :                 pmksa_cache_free_entry(pmksa, pos);
     271                 :            : 
     272 [ -  + ][ #  # ]:         81 :         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                 :         81 :         pmksa_cache_link_entry(pmksa, entry);
     281                 :            : 
     282                 :         81 :         return entry;
     283                 :            : }
     284                 :            : 
     285                 :            : 
     286                 :            : struct rsn_pmksa_cache_entry *
     287                 :          1 : 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                 :          1 :         entry = os_zalloc(sizeof(*entry));
     294         [ -  + ]:          1 :         if (entry == NULL)
     295                 :          0 :                 return NULL;
     296                 :          1 :         os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
     297                 :          1 :         os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len);
     298                 :          1 :         entry->pmk_len = old_entry->pmk_len;
     299                 :          1 :         entry->expiration = old_entry->expiration;
     300                 :          1 :         entry->akmp = old_entry->akmp;
     301                 :          1 :         os_memcpy(entry->spa, old_entry->spa, ETH_ALEN);
     302                 :          1 :         entry->opportunistic = 1;
     303         [ +  - ]:          1 :         if (old_entry->identity) {
     304                 :          1 :                 entry->identity = os_malloc(old_entry->identity_len);
     305         [ +  - ]:          1 :                 if (entry->identity) {
     306                 :          1 :                         entry->identity_len = old_entry->identity_len;
     307                 :          1 :                         os_memcpy(entry->identity, old_entry->identity,
     308                 :            :                                   old_entry->identity_len);
     309                 :            :                 }
     310                 :            :         }
     311         [ -  + ]:          1 :         if (old_entry->cui)
     312                 :          0 :                 entry->cui = wpabuf_dup(old_entry->cui);
     313                 :            : #ifndef CONFIG_NO_RADIUS
     314                 :          1 :         radius_copy_class(&entry->radius_class, &old_entry->radius_class);
     315                 :            : #endif /* CONFIG_NO_RADIUS */
     316                 :          1 :         entry->eap_type_authsrv = old_entry->eap_type_authsrv;
     317                 :          1 :         entry->vlan_id = old_entry->vlan_id;
     318                 :          1 :         entry->opportunistic = 1;
     319                 :            : 
     320                 :          1 :         pmksa_cache_link_entry(pmksa, entry);
     321                 :            : 
     322                 :          1 :         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                 :        159 : void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
     331                 :            : {
     332                 :            :         struct rsn_pmksa_cache_entry *entry, *prev;
     333                 :            :         int i;
     334                 :            : 
     335         [ -  + ]:        159 :         if (pmksa == NULL)
     336                 :        159 :                 return;
     337                 :            : 
     338                 :        159 :         entry = pmksa->pmksa;
     339         [ +  + ]:        215 :         while (entry) {
     340                 :         56 :                 prev = entry;
     341                 :         56 :                 entry = entry->next;
     342                 :         56 :                 _pmksa_cache_free_entry(prev);
     343                 :            :         }
     344                 :        159 :         eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
     345         [ +  + ]:      20511 :         for (i = 0; i < PMKID_HASH_SIZE; i++)
     346                 :      20352 :                 pmksa->pmkid[i] = NULL;
     347                 :        159 :         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                 :        104 : 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         [ +  + ]:        104 :         if (pmkid)
     365                 :         22 :                 entry = pmksa->pmkid[PMKID_HASH(pmkid)];
     366                 :            :         else
     367                 :         82 :                 entry = pmksa->pmksa;
     368         [ +  + ]:        104 :         while (entry) {
     369 [ +  - ][ +  - ]:         30 :                 if ((spa == NULL ||
     370         [ +  + ]:         30 :                      os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
     371         [ +  - ]:          4 :                     (pmkid == NULL ||
     372                 :          4 :                      os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
     373                 :         30 :                         return entry;
     374         [ #  # ]:          0 :                 entry = pmkid ? entry->hnext : entry->next;
     375                 :            :         }
     376                 :        104 :         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                 :          1 : 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                 :          1 :         entry = pmksa->pmksa;
     398         [ +  - ]:          1 :         while (entry) {
     399         [ -  + ]:          1 :                 if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
     400                 :          0 :                         continue;
     401                 :          1 :                 rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
     402                 :            :                           wpa_key_mgmt_sha256(entry->akmp));
     403         [ +  - ]:          1 :                 if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
     404                 :          1 :                         return entry;
     405                 :          0 :                 entry = entry->next;
     406                 :            :         }
     407                 :          1 :         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                 :        159 : 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                 :        159 :         pmksa = os_zalloc(sizeof(*pmksa));
     424         [ +  - ]:        159 :         if (pmksa) {
     425                 :        159 :                 pmksa->free_cb = free_cb;
     426                 :        159 :                 pmksa->ctx = ctx;
     427                 :            :         }
     428                 :            : 
     429                 :        159 :         return pmksa;
     430                 :            : }

Generated by: LCOV version 1.9