pmksa_cache.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /*
  2. * WPA Supplicant - RSN PMKSA cache
  3. * Copyright (c) 2004-2009, 2011-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. #include "includes.h"
  9. #include "common.h"
  10. #include "eloop.h"
  11. #include "eapol_supp/eapol_supp_sm.h"
  12. #include "wpa.h"
  13. #include "wpa_i.h"
  14. #include "pmksa_cache.h"
  15. #ifdef IEEE8021X_EAPOL
  16. static const int pmksa_cache_max_entries = 32;
  17. struct rsn_pmksa_cache {
  18. struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
  19. int pmksa_count; /* number of entries in PMKSA cache */
  20. struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
  21. void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
  22. enum pmksa_free_reason reason);
  23. void *ctx;
  24. };
  25. static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
  26. static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
  27. {
  28. bin_clear_free(entry, sizeof(*entry));
  29. }
  30. static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
  31. struct rsn_pmksa_cache_entry *entry,
  32. enum pmksa_free_reason reason)
  33. {
  34. wpa_sm_remove_pmkid(pmksa->sm, entry->aa, entry->pmkid);
  35. pmksa->pmksa_count--;
  36. pmksa->free_cb(entry, pmksa->ctx, reason);
  37. _pmksa_cache_free_entry(entry);
  38. }
  39. static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
  40. {
  41. struct rsn_pmksa_cache *pmksa = eloop_ctx;
  42. struct os_reltime now;
  43. os_get_reltime(&now);
  44. while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
  45. struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
  46. pmksa->pmksa = entry->next;
  47. wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
  48. MACSTR, MAC2STR(entry->aa));
  49. pmksa_cache_free_entry(pmksa, entry, PMKSA_EXPIRE);
  50. }
  51. pmksa_cache_set_expiration(pmksa);
  52. }
  53. static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
  54. {
  55. struct rsn_pmksa_cache *pmksa = eloop_ctx;
  56. pmksa->sm->cur_pmksa = NULL;
  57. eapol_sm_request_reauth(pmksa->sm->eapol);
  58. }
  59. static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
  60. {
  61. int sec;
  62. struct rsn_pmksa_cache_entry *entry;
  63. struct os_reltime now;
  64. eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
  65. eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
  66. if (pmksa->pmksa == NULL)
  67. return;
  68. os_get_reltime(&now);
  69. sec = pmksa->pmksa->expiration - now.sec;
  70. if (sec < 0)
  71. sec = 0;
  72. eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
  73. entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
  74. pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL, NULL);
  75. if (entry) {
  76. sec = pmksa->pmksa->reauth_time - now.sec;
  77. if (sec < 0)
  78. sec = 0;
  79. eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
  80. NULL);
  81. }
  82. }
  83. /**
  84. * pmksa_cache_add - Add a PMKSA cache entry
  85. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  86. * @pmk: The new pairwise master key
  87. * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
  88. * @kck: Key confirmation key or %NULL if not yet derived
  89. * @kck_len: KCK length in bytes
  90. * @aa: Authenticator address
  91. * @spa: Supplicant address
  92. * @network_ctx: Network configuration context for this PMK
  93. * @akmp: WPA_KEY_MGMT_* used in key derivation
  94. * Returns: Pointer to the added PMKSA cache entry or %NULL on error
  95. *
  96. * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
  97. * cache. If an old entry is already in the cache for the same Authenticator,
  98. * this entry will be replaced with the new entry. PMKID will be calculated
  99. * based on the PMK and the driver interface is notified of the new PMKID.
  100. */
  101. struct rsn_pmksa_cache_entry *
  102. pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
  103. const u8 *kck, size_t kck_len,
  104. const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
  105. {
  106. struct rsn_pmksa_cache_entry *entry, *pos, *prev;
  107. struct os_reltime now;
  108. if (pmk_len > PMK_LEN)
  109. return NULL;
  110. if (wpa_key_mgmt_suite_b(akmp) && !kck)
  111. return NULL;
  112. entry = os_zalloc(sizeof(*entry));
  113. if (entry == NULL)
  114. return NULL;
  115. os_memcpy(entry->pmk, pmk, pmk_len);
  116. entry->pmk_len = pmk_len;
  117. if (wpa_key_mgmt_suite_b(akmp))
  118. rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
  119. else
  120. rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
  121. wpa_key_mgmt_sha256(akmp));
  122. os_get_reltime(&now);
  123. entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
  124. entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
  125. pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
  126. entry->akmp = akmp;
  127. os_memcpy(entry->aa, aa, ETH_ALEN);
  128. entry->network_ctx = network_ctx;
  129. /* Replace an old entry for the same Authenticator (if found) with the
  130. * new entry */
  131. pos = pmksa->pmksa;
  132. prev = NULL;
  133. while (pos) {
  134. if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
  135. if (pos->pmk_len == pmk_len &&
  136. os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 &&
  137. os_memcmp_const(pos->pmkid, entry->pmkid,
  138. PMKID_LEN) == 0) {
  139. wpa_printf(MSG_DEBUG, "WPA: reusing previous "
  140. "PMKSA entry");
  141. os_free(entry);
  142. return pos;
  143. }
  144. if (prev == NULL)
  145. pmksa->pmksa = pos->next;
  146. else
  147. prev->next = pos->next;
  148. /*
  149. * If OKC is used, there may be other PMKSA cache
  150. * entries based on the same PMK. These needs to be
  151. * flushed so that a new entry can be created based on
  152. * the new PMK. Only clear other entries if they have a
  153. * matching PMK and this PMK has been used successfully
  154. * with the current AP, i.e., if opportunistic flag has
  155. * been cleared in wpa_supplicant_key_neg_complete().
  156. */
  157. wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
  158. "the current AP and any PMKSA cache entry "
  159. "that was based on the old PMK");
  160. if (!pos->opportunistic)
  161. pmksa_cache_flush(pmksa, network_ctx, pos->pmk,
  162. pos->pmk_len);
  163. pmksa_cache_free_entry(pmksa, pos, PMKSA_REPLACE);
  164. break;
  165. }
  166. prev = pos;
  167. pos = pos->next;
  168. }
  169. if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
  170. /* Remove the oldest entry to make room for the new entry */
  171. pos = pmksa->pmksa;
  172. if (pos == pmksa->sm->cur_pmksa) {
  173. /*
  174. * Never remove the current PMKSA cache entry, since
  175. * it's in use, and removing it triggers a needless
  176. * deauthentication.
  177. */
  178. pos = pos->next;
  179. pmksa->pmksa->next = pos ? pos->next : NULL;
  180. } else
  181. pmksa->pmksa = pos->next;
  182. if (pos) {
  183. wpa_printf(MSG_DEBUG, "RSN: removed the oldest idle "
  184. "PMKSA cache entry (for " MACSTR ") to "
  185. "make room for new one",
  186. MAC2STR(pos->aa));
  187. pmksa_cache_free_entry(pmksa, pos, PMKSA_FREE);
  188. }
  189. }
  190. /* Add the new entry; order by expiration time */
  191. pos = pmksa->pmksa;
  192. prev = NULL;
  193. while (pos) {
  194. if (pos->expiration > entry->expiration)
  195. break;
  196. prev = pos;
  197. pos = pos->next;
  198. }
  199. if (prev == NULL) {
  200. entry->next = pmksa->pmksa;
  201. pmksa->pmksa = entry;
  202. pmksa_cache_set_expiration(pmksa);
  203. } else {
  204. entry->next = prev->next;
  205. prev->next = entry;
  206. }
  207. pmksa->pmksa_count++;
  208. wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
  209. " network_ctx=%p", MAC2STR(entry->aa), network_ctx);
  210. wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
  211. return entry;
  212. }
  213. /**
  214. * pmksa_cache_flush - Flush PMKSA cache entries for a specific network
  215. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  216. * @network_ctx: Network configuration context or %NULL to flush all entries
  217. * @pmk: PMK to match for or %NYLL to match all PMKs
  218. * @pmk_len: PMK length
  219. */
  220. void pmksa_cache_flush(struct rsn_pmksa_cache *pmksa, void *network_ctx,
  221. const u8 *pmk, size_t pmk_len)
  222. {
  223. struct rsn_pmksa_cache_entry *entry, *prev = NULL, *tmp;
  224. int removed = 0;
  225. entry = pmksa->pmksa;
  226. while (entry) {
  227. if ((entry->network_ctx == network_ctx ||
  228. network_ctx == NULL) &&
  229. (pmk == NULL ||
  230. (pmk_len == entry->pmk_len &&
  231. os_memcmp(pmk, entry->pmk, pmk_len) == 0))) {
  232. wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry "
  233. "for " MACSTR, MAC2STR(entry->aa));
  234. if (prev)
  235. prev->next = entry->next;
  236. else
  237. pmksa->pmksa = entry->next;
  238. tmp = entry;
  239. entry = entry->next;
  240. pmksa_cache_free_entry(pmksa, tmp, PMKSA_FREE);
  241. removed++;
  242. } else {
  243. prev = entry;
  244. entry = entry->next;
  245. }
  246. }
  247. if (removed)
  248. pmksa_cache_set_expiration(pmksa);
  249. }
  250. /**
  251. * pmksa_cache_deinit - Free all entries in PMKSA cache
  252. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  253. */
  254. void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
  255. {
  256. struct rsn_pmksa_cache_entry *entry, *prev;
  257. if (pmksa == NULL)
  258. return;
  259. entry = pmksa->pmksa;
  260. pmksa->pmksa = NULL;
  261. while (entry) {
  262. prev = entry;
  263. entry = entry->next;
  264. os_free(prev);
  265. }
  266. pmksa_cache_set_expiration(pmksa);
  267. os_free(pmksa);
  268. }
  269. /**
  270. * pmksa_cache_get - Fetch a PMKSA cache entry
  271. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  272. * @aa: Authenticator address or %NULL to match any
  273. * @pmkid: PMKID or %NULL to match any
  274. * @network_ctx: Network context or %NULL to match any
  275. * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
  276. */
  277. struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
  278. const u8 *aa, const u8 *pmkid,
  279. const void *network_ctx)
  280. {
  281. struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
  282. while (entry) {
  283. if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
  284. (pmkid == NULL ||
  285. os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0) &&
  286. (network_ctx == NULL || network_ctx == entry->network_ctx))
  287. return entry;
  288. entry = entry->next;
  289. }
  290. return NULL;
  291. }
  292. static struct rsn_pmksa_cache_entry *
  293. pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
  294. const struct rsn_pmksa_cache_entry *old_entry,
  295. const u8 *aa)
  296. {
  297. struct rsn_pmksa_cache_entry *new_entry;
  298. new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
  299. NULL, 0,
  300. aa, pmksa->sm->own_addr,
  301. old_entry->network_ctx, old_entry->akmp);
  302. if (new_entry == NULL)
  303. return NULL;
  304. /* TODO: reorder entries based on expiration time? */
  305. new_entry->expiration = old_entry->expiration;
  306. new_entry->opportunistic = 1;
  307. return new_entry;
  308. }
  309. /**
  310. * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
  311. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  312. * @network_ctx: Network configuration context
  313. * @aa: Authenticator address for the new AP
  314. * Returns: Pointer to a new PMKSA cache entry or %NULL if not available
  315. *
  316. * Try to create a new PMKSA cache entry opportunistically by guessing that the
  317. * new AP is sharing the same PMK as another AP that has the same SSID and has
  318. * already an entry in PMKSA cache.
  319. */
  320. struct rsn_pmksa_cache_entry *
  321. pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
  322. const u8 *aa)
  323. {
  324. struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
  325. wpa_printf(MSG_DEBUG, "RSN: Consider " MACSTR " for OKC", MAC2STR(aa));
  326. if (network_ctx == NULL)
  327. return NULL;
  328. while (entry) {
  329. if (entry->network_ctx == network_ctx) {
  330. entry = pmksa_cache_clone_entry(pmksa, entry, aa);
  331. if (entry) {
  332. wpa_printf(MSG_DEBUG, "RSN: added "
  333. "opportunistic PMKSA cache entry "
  334. "for " MACSTR, MAC2STR(aa));
  335. }
  336. return entry;
  337. }
  338. entry = entry->next;
  339. }
  340. return NULL;
  341. }
  342. /**
  343. * pmksa_cache_get_current - Get the current used PMKSA entry
  344. * @sm: Pointer to WPA state machine data from wpa_sm_init()
  345. * Returns: Pointer to the current PMKSA cache entry or %NULL if not available
  346. */
  347. struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
  348. {
  349. if (sm == NULL)
  350. return NULL;
  351. return sm->cur_pmksa;
  352. }
  353. /**
  354. * pmksa_cache_clear_current - Clear the current PMKSA entry selection
  355. * @sm: Pointer to WPA state machine data from wpa_sm_init()
  356. */
  357. void pmksa_cache_clear_current(struct wpa_sm *sm)
  358. {
  359. if (sm == NULL)
  360. return;
  361. sm->cur_pmksa = NULL;
  362. }
  363. /**
  364. * pmksa_cache_set_current - Set the current PMKSA entry selection
  365. * @sm: Pointer to WPA state machine data from wpa_sm_init()
  366. * @pmkid: PMKID for selecting PMKSA or %NULL if not used
  367. * @bssid: BSSID for PMKSA or %NULL if not used
  368. * @network_ctx: Network configuration context
  369. * @try_opportunistic: Whether to allow opportunistic PMKSA caching
  370. * Returns: 0 if PMKSA was found or -1 if no matching entry was found
  371. */
  372. int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
  373. const u8 *bssid, void *network_ctx,
  374. int try_opportunistic)
  375. {
  376. struct rsn_pmksa_cache *pmksa = sm->pmksa;
  377. wpa_printf(MSG_DEBUG, "RSN: PMKSA cache search - network_ctx=%p "
  378. "try_opportunistic=%d", network_ctx, try_opportunistic);
  379. if (pmkid)
  380. wpa_hexdump(MSG_DEBUG, "RSN: Search for PMKID",
  381. pmkid, PMKID_LEN);
  382. if (bssid)
  383. wpa_printf(MSG_DEBUG, "RSN: Search for BSSID " MACSTR,
  384. MAC2STR(bssid));
  385. sm->cur_pmksa = NULL;
  386. if (pmkid)
  387. sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid,
  388. network_ctx);
  389. if (sm->cur_pmksa == NULL && bssid)
  390. sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL,
  391. network_ctx);
  392. if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
  393. sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
  394. network_ctx,
  395. bssid);
  396. if (sm->cur_pmksa) {
  397. wpa_hexdump(MSG_DEBUG, "RSN: PMKSA cache entry found - PMKID",
  398. sm->cur_pmksa->pmkid, PMKID_LEN);
  399. return 0;
  400. }
  401. wpa_printf(MSG_DEBUG, "RSN: No PMKSA cache entry found");
  402. return -1;
  403. }
  404. /**
  405. * pmksa_cache_list - Dump text list of entries in PMKSA cache
  406. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  407. * @buf: Buffer for the list
  408. * @len: Length of the buffer
  409. * Returns: number of bytes written to buffer
  410. *
  411. * This function is used to generate a text format representation of the
  412. * current PMKSA cache contents for the ctrl_iface PMKSA command.
  413. */
  414. int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
  415. {
  416. int i, ret;
  417. char *pos = buf;
  418. struct rsn_pmksa_cache_entry *entry;
  419. struct os_reltime now;
  420. os_get_reltime(&now);
  421. ret = os_snprintf(pos, buf + len - pos,
  422. "Index / AA / PMKID / expiration (in seconds) / "
  423. "opportunistic\n");
  424. if (ret < 0 || ret >= buf + len - pos)
  425. return pos - buf;
  426. pos += ret;
  427. i = 0;
  428. entry = pmksa->pmksa;
  429. while (entry) {
  430. i++;
  431. ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
  432. i, MAC2STR(entry->aa));
  433. if (ret < 0 || ret >= buf + len - pos)
  434. return pos - buf;
  435. pos += ret;
  436. pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
  437. PMKID_LEN);
  438. ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
  439. (int) (entry->expiration - now.sec),
  440. entry->opportunistic);
  441. if (ret < 0 || ret >= buf + len - pos)
  442. return pos - buf;
  443. pos += ret;
  444. entry = entry->next;
  445. }
  446. return pos - buf;
  447. }
  448. /**
  449. * pmksa_cache_init - Initialize PMKSA cache
  450. * @free_cb: Callback function to be called when a PMKSA cache entry is freed
  451. * @ctx: Context pointer for free_cb function
  452. * @sm: Pointer to WPA state machine data from wpa_sm_init()
  453. * Returns: Pointer to PMKSA cache data or %NULL on failure
  454. */
  455. struct rsn_pmksa_cache *
  456. pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
  457. void *ctx, enum pmksa_free_reason reason),
  458. void *ctx, struct wpa_sm *sm)
  459. {
  460. struct rsn_pmksa_cache *pmksa;
  461. pmksa = os_zalloc(sizeof(*pmksa));
  462. if (pmksa) {
  463. pmksa->free_cb = free_cb;
  464. pmksa->ctx = ctx;
  465. pmksa->sm = sm;
  466. }
  467. return pmksa;
  468. }
  469. #endif /* IEEE8021X_EAPOL */