00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "includes.h"
00016
00017 #include "common.h"
00018 #include "eloop.h"
00019 #include "eapol_supp/eapol_supp_sm.h"
00020 #include "wpa.h"
00021 #include "wpa_i.h"
00022 #include "pmksa_cache.h"
00023
00024 #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
00025
00026 static const int pmksa_cache_max_entries = 32;
00027
00028 struct rsn_pmksa_cache {
00029 struct rsn_pmksa_cache_entry *pmksa;
00030 int pmksa_count;
00031 struct wpa_sm *sm;
00032
00033 void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
00034 int replace);
00035 void *ctx;
00036 };
00037
00038
00039 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
00040
00041
00042 static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
00043 {
00044 os_free(entry);
00045 }
00046
00047
00048 static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
00049 struct rsn_pmksa_cache_entry *entry,
00050 int replace)
00051 {
00052 pmksa->pmksa_count--;
00053 pmksa->free_cb(entry, pmksa->ctx, replace);
00054 _pmksa_cache_free_entry(entry);
00055 }
00056
00057
00058 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
00059 {
00060 struct rsn_pmksa_cache *pmksa = eloop_ctx;
00061 struct os_time now;
00062
00063 os_get_time(&now);
00064 while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
00065 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
00066 pmksa->pmksa = entry->next;
00067 wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
00068 MACSTR, MAC2STR(entry->aa));
00069 pmksa_cache_free_entry(pmksa, entry, 0);
00070 }
00071
00072 pmksa_cache_set_expiration(pmksa);
00073 }
00074
00075
00076 static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
00077 {
00078 struct rsn_pmksa_cache *pmksa = eloop_ctx;
00079 pmksa->sm->cur_pmksa = NULL;
00080 eapol_sm_request_reauth(pmksa->sm->eapol);
00081 }
00082
00083
00084 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
00085 {
00086 int sec;
00087 struct rsn_pmksa_cache_entry *entry;
00088 struct os_time now;
00089
00090 eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
00091 eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
00092 if (pmksa->pmksa == NULL)
00093 return;
00094 os_get_time(&now);
00095 sec = pmksa->pmksa->expiration - now.sec;
00096 if (sec < 0)
00097 sec = 0;
00098 eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
00099
00100 entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
00101 pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL);
00102 if (entry) {
00103 sec = pmksa->pmksa->reauth_time - now.sec;
00104 if (sec < 0)
00105 sec = 0;
00106 eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
00107 NULL);
00108 }
00109 }
00110
00111
00128 struct rsn_pmksa_cache_entry *
00129 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
00130 const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
00131 {
00132 struct rsn_pmksa_cache_entry *entry, *pos, *prev;
00133 struct os_time now;
00134
00135 if (pmk_len > PMK_LEN)
00136 return NULL;
00137
00138 entry = os_zalloc(sizeof(*entry));
00139 if (entry == NULL)
00140 return NULL;
00141 os_memcpy(entry->pmk, pmk, pmk_len);
00142 entry->pmk_len = pmk_len;
00143 rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
00144 wpa_key_mgmt_sha256(akmp));
00145 os_get_time(&now);
00146 entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
00147 entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
00148 pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
00149 entry->akmp = akmp;
00150 os_memcpy(entry->aa, aa, ETH_ALEN);
00151 entry->network_ctx = network_ctx;
00152
00153
00154
00155 pos = pmksa->pmksa;
00156 prev = NULL;
00157 while (pos) {
00158 if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
00159 if (pos->pmk_len == pmk_len &&
00160 os_memcmp(pos->pmk, pmk, pmk_len) == 0 &&
00161 os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) ==
00162 0) {
00163 wpa_printf(MSG_DEBUG, "WPA: reusing previous "
00164 "PMKSA entry");
00165 os_free(entry);
00166 return pos;
00167 }
00168 if (prev == NULL)
00169 pmksa->pmksa = pos->next;
00170 else
00171 prev->next = pos->next;
00172 if (pos == pmksa->sm->cur_pmksa) {
00173
00174
00175
00176
00177
00178
00179
00180
00181 wpa_printf(MSG_DEBUG, "RSN: replacing current "
00182 "PMKSA entry");
00183 pmksa->sm->cur_pmksa = NULL;
00184 }
00185 wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
00186 "the current AP");
00187 pmksa_cache_free_entry(pmksa, pos, 1);
00188 break;
00189 }
00190 prev = pos;
00191 pos = pos->next;
00192 }
00193
00194 if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
00195
00196 pos = pmksa->pmksa;
00197 pmksa->pmksa = pos->next;
00198 wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
00199 "entry (for " MACSTR ") to make room for new one",
00200 MAC2STR(pos->aa));
00201 wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
00202 pmksa_cache_free_entry(pmksa, pos, 0);
00203 }
00204
00205
00206 pos = pmksa->pmksa;
00207 prev = NULL;
00208 while (pos) {
00209 if (pos->expiration > entry->expiration)
00210 break;
00211 prev = pos;
00212 pos = pos->next;
00213 }
00214 if (prev == NULL) {
00215 entry->next = pmksa->pmksa;
00216 pmksa->pmksa = entry;
00217 pmksa_cache_set_expiration(pmksa);
00218 } else {
00219 entry->next = prev->next;
00220 prev->next = entry;
00221 }
00222 pmksa->pmksa_count++;
00223 wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
00224 MAC2STR(entry->aa));
00225 wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
00226
00227 return entry;
00228 }
00229
00230
00235 void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
00236 {
00237 struct rsn_pmksa_cache_entry *entry, *prev;
00238
00239 if (pmksa == NULL)
00240 return;
00241
00242 entry = pmksa->pmksa;
00243 pmksa->pmksa = NULL;
00244 while (entry) {
00245 prev = entry;
00246 entry = entry->next;
00247 os_free(prev);
00248 }
00249 pmksa_cache_set_expiration(pmksa);
00250 os_free(pmksa);
00251 }
00252
00253
00261 struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
00262 const u8 *aa, const u8 *pmkid)
00263 {
00264 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
00265 while (entry) {
00266 if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
00267 (pmkid == NULL ||
00268 os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
00269 return entry;
00270 entry = entry->next;
00271 }
00272 return NULL;
00273 }
00274
00275
00282 void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
00283 {
00284 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
00285 while (entry) {
00286 entry->network_ctx = NULL;
00287 entry = entry->next;
00288 }
00289 }
00290
00291
00292 static struct rsn_pmksa_cache_entry *
00293 pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
00294 const struct rsn_pmksa_cache_entry *old_entry,
00295 const u8 *aa)
00296 {
00297 struct rsn_pmksa_cache_entry *new_entry;
00298
00299 new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
00300 aa, pmksa->sm->own_addr,
00301 old_entry->network_ctx, old_entry->akmp);
00302 if (new_entry == NULL)
00303 return NULL;
00304
00305
00306 new_entry->expiration = old_entry->expiration;
00307 new_entry->opportunistic = 1;
00308
00309 return new_entry;
00310 }
00311
00312
00324 struct rsn_pmksa_cache_entry *
00325 pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
00326 const u8 *aa)
00327 {
00328 struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
00329
00330 if (network_ctx == NULL)
00331 return NULL;
00332 while (entry) {
00333 if (entry->network_ctx == network_ctx) {
00334 entry = pmksa_cache_clone_entry(pmksa, entry, aa);
00335 if (entry) {
00336 wpa_printf(MSG_DEBUG, "RSN: added "
00337 "opportunistic PMKSA cache entry "
00338 "for " MACSTR, MAC2STR(aa));
00339 }
00340 return entry;
00341 }
00342 entry = entry->next;
00343 }
00344 return NULL;
00345 }
00346
00347
00353 struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
00354 {
00355 if (sm == NULL)
00356 return NULL;
00357 return sm->cur_pmksa;
00358 }
00359
00360
00365 void pmksa_cache_clear_current(struct wpa_sm *sm)
00366 {
00367 if (sm == NULL)
00368 return;
00369 sm->cur_pmksa = NULL;
00370 }
00371
00372
00382 int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
00383 const u8 *bssid, void *network_ctx,
00384 int try_opportunistic)
00385 {
00386 struct rsn_pmksa_cache *pmksa = sm->pmksa;
00387 sm->cur_pmksa = NULL;
00388 if (pmkid)
00389 sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid);
00390 if (sm->cur_pmksa == NULL && bssid)
00391 sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL);
00392 if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
00393 sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
00394 network_ctx,
00395 bssid);
00396 if (sm->cur_pmksa) {
00397 wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
00398 sm->cur_pmksa->pmkid, PMKID_LEN);
00399 return 0;
00400 }
00401 return -1;
00402 }
00403
00404
00415 int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
00416 {
00417 int i, ret;
00418 char *pos = buf;
00419 struct rsn_pmksa_cache_entry *entry;
00420 struct os_time now;
00421
00422 os_get_time(&now);
00423 ret = os_snprintf(pos, buf + len - pos,
00424 "Index / AA / PMKID / expiration (in seconds) / "
00425 "opportunistic\n");
00426 if (ret < 0 || ret >= buf + len - pos)
00427 return pos - buf;
00428 pos += ret;
00429 i = 0;
00430 entry = pmksa->pmksa;
00431 while (entry) {
00432 i++;
00433 ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
00434 i, MAC2STR(entry->aa));
00435 if (ret < 0 || ret >= buf + len - pos)
00436 return pos - buf;
00437 pos += ret;
00438 pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
00439 PMKID_LEN);
00440 ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
00441 (int) (entry->expiration - now.sec),
00442 entry->opportunistic);
00443 if (ret < 0 || ret >= buf + len - pos)
00444 return pos - buf;
00445 pos += ret;
00446 entry = entry->next;
00447 }
00448 return pos - buf;
00449 }
00450
00451
00459 struct rsn_pmksa_cache *
00460 pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
00461 void *ctx, int replace),
00462 void *ctx, struct wpa_sm *sm)
00463 {
00464 struct rsn_pmksa_cache *pmksa;
00465
00466 pmksa = os_zalloc(sizeof(*pmksa));
00467 if (pmksa) {
00468 pmksa->free_cb = free_cb;
00469 pmksa->ctx = ctx;
00470 pmksa->sm = sm;
00471 }
00472
00473 return pmksa;
00474 }
00475
00476 #endif