PageRenderTime 45ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/contrib/wpa/src/rsn_supp/pmksa_cache.c

https://github.com/blacklion/GEOM-Events
C | 476 lines | 311 code | 60 blank | 105 comment | 68 complexity | 94681da0305ec2b2be74c6cf61429eba MD5 | raw file
  1. /*
  2. * WPA Supplicant - RSN PMKSA cache
  3. * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * Alternatively, this software may be distributed under the terms of BSD
  10. * license.
  11. *
  12. * See README and COPYING for more details.
  13. */
  14. #include "includes.h"
  15. #include "common.h"
  16. #include "eloop.h"
  17. #include "eapol_supp/eapol_supp_sm.h"
  18. #include "wpa.h"
  19. #include "wpa_i.h"
  20. #include "pmksa_cache.h"
  21. #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2)
  22. static const int pmksa_cache_max_entries = 32;
  23. struct rsn_pmksa_cache {
  24. struct rsn_pmksa_cache_entry *pmksa; /* PMKSA cache */
  25. int pmksa_count; /* number of entries in PMKSA cache */
  26. struct wpa_sm *sm; /* TODO: get rid of this reference(?) */
  27. void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx,
  28. int replace);
  29. void *ctx;
  30. };
  31. static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
  32. static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
  33. {
  34. os_free(entry);
  35. }
  36. static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
  37. struct rsn_pmksa_cache_entry *entry,
  38. int replace)
  39. {
  40. pmksa->pmksa_count--;
  41. pmksa->free_cb(entry, pmksa->ctx, replace);
  42. _pmksa_cache_free_entry(entry);
  43. }
  44. static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
  45. {
  46. struct rsn_pmksa_cache *pmksa = eloop_ctx;
  47. struct os_time now;
  48. os_get_time(&now);
  49. while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
  50. struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
  51. pmksa->pmksa = entry->next;
  52. wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
  53. MACSTR, MAC2STR(entry->aa));
  54. pmksa_cache_free_entry(pmksa, entry, 0);
  55. }
  56. pmksa_cache_set_expiration(pmksa);
  57. }
  58. static void pmksa_cache_reauth(void *eloop_ctx, void *timeout_ctx)
  59. {
  60. struct rsn_pmksa_cache *pmksa = eloop_ctx;
  61. pmksa->sm->cur_pmksa = NULL;
  62. eapol_sm_request_reauth(pmksa->sm->eapol);
  63. }
  64. static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
  65. {
  66. int sec;
  67. struct rsn_pmksa_cache_entry *entry;
  68. struct os_time now;
  69. eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
  70. eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
  71. if (pmksa->pmksa == NULL)
  72. return;
  73. os_get_time(&now);
  74. sec = pmksa->pmksa->expiration - now.sec;
  75. if (sec < 0)
  76. sec = 0;
  77. eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
  78. entry = pmksa->sm->cur_pmksa ? pmksa->sm->cur_pmksa :
  79. pmksa_cache_get(pmksa, pmksa->sm->bssid, NULL);
  80. if (entry) {
  81. sec = pmksa->pmksa->reauth_time - now.sec;
  82. if (sec < 0)
  83. sec = 0;
  84. eloop_register_timeout(sec, 0, pmksa_cache_reauth, pmksa,
  85. NULL);
  86. }
  87. }
  88. /**
  89. * pmksa_cache_add - Add a PMKSA cache entry
  90. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  91. * @pmk: The new pairwise master key
  92. * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
  93. * @aa: Authenticator address
  94. * @spa: Supplicant address
  95. * @network_ctx: Network configuration context for this PMK
  96. * @akmp: WPA_KEY_MGMT_* used in key derivation
  97. * Returns: Pointer to the added PMKSA cache entry or %NULL on error
  98. *
  99. * This function create a PMKSA entry for a new PMK and adds it to the PMKSA
  100. * cache. If an old entry is already in the cache for the same Authenticator,
  101. * this entry will be replaced with the new entry. PMKID will be calculated
  102. * based on the PMK and the driver interface is notified of the new PMKID.
  103. */
  104. struct rsn_pmksa_cache_entry *
  105. pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
  106. const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
  107. {
  108. struct rsn_pmksa_cache_entry *entry, *pos, *prev;
  109. struct os_time now;
  110. if (pmk_len > PMK_LEN)
  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. rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
  118. wpa_key_mgmt_sha256(akmp));
  119. os_get_time(&now);
  120. entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
  121. entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
  122. pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
  123. entry->akmp = akmp;
  124. os_memcpy(entry->aa, aa, ETH_ALEN);
  125. entry->network_ctx = network_ctx;
  126. /* Replace an old entry for the same Authenticator (if found) with the
  127. * new entry */
  128. pos = pmksa->pmksa;
  129. prev = NULL;
  130. while (pos) {
  131. if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
  132. if (pos->pmk_len == pmk_len &&
  133. os_memcmp(pos->pmk, pmk, pmk_len) == 0 &&
  134. os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) ==
  135. 0) {
  136. wpa_printf(MSG_DEBUG, "WPA: reusing previous "
  137. "PMKSA entry");
  138. os_free(entry);
  139. return pos;
  140. }
  141. if (prev == NULL)
  142. pmksa->pmksa = pos->next;
  143. else
  144. prev->next = pos->next;
  145. if (pos == pmksa->sm->cur_pmksa) {
  146. /* We are about to replace the current PMKSA
  147. * cache entry. This happens when the PMKSA
  148. * caching attempt fails, so we don't want to
  149. * force pmksa_cache_free_entry() to disconnect
  150. * at this point. Let's just make sure the old
  151. * PMKSA cache entry will not be used in the
  152. * future.
  153. */
  154. wpa_printf(MSG_DEBUG, "RSN: replacing current "
  155. "PMKSA entry");
  156. pmksa->sm->cur_pmksa = NULL;
  157. }
  158. wpa_printf(MSG_DEBUG, "RSN: Replace PMKSA entry for "
  159. "the current AP");
  160. pmksa_cache_free_entry(pmksa, pos, 1);
  161. break;
  162. }
  163. prev = pos;
  164. pos = pos->next;
  165. }
  166. if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
  167. /* Remove the oldest entry to make room for the new entry */
  168. pos = pmksa->pmksa;
  169. pmksa->pmksa = pos->next;
  170. wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
  171. "entry (for " MACSTR ") to make room for new one",
  172. MAC2STR(pos->aa));
  173. wpa_sm_remove_pmkid(pmksa->sm, pos->aa, pos->pmkid);
  174. pmksa_cache_free_entry(pmksa, pos, 0);
  175. }
  176. /* Add the new entry; order by expiration time */
  177. pos = pmksa->pmksa;
  178. prev = NULL;
  179. while (pos) {
  180. if (pos->expiration > entry->expiration)
  181. break;
  182. prev = pos;
  183. pos = pos->next;
  184. }
  185. if (prev == NULL) {
  186. entry->next = pmksa->pmksa;
  187. pmksa->pmksa = entry;
  188. pmksa_cache_set_expiration(pmksa);
  189. } else {
  190. entry->next = prev->next;
  191. prev->next = entry;
  192. }
  193. pmksa->pmksa_count++;
  194. wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
  195. MAC2STR(entry->aa));
  196. wpa_sm_add_pmkid(pmksa->sm, entry->aa, entry->pmkid);
  197. return entry;
  198. }
  199. /**
  200. * pmksa_cache_deinit - Free all entries in PMKSA cache
  201. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  202. */
  203. void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa)
  204. {
  205. struct rsn_pmksa_cache_entry *entry, *prev;
  206. if (pmksa == NULL)
  207. return;
  208. entry = pmksa->pmksa;
  209. pmksa->pmksa = NULL;
  210. while (entry) {
  211. prev = entry;
  212. entry = entry->next;
  213. os_free(prev);
  214. }
  215. pmksa_cache_set_expiration(pmksa);
  216. os_free(pmksa);
  217. }
  218. /**
  219. * pmksa_cache_get - Fetch a PMKSA cache entry
  220. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  221. * @aa: Authenticator address or %NULL to match any
  222. * @pmkid: PMKID or %NULL to match any
  223. * Returns: Pointer to PMKSA cache entry or %NULL if no match was found
  224. */
  225. struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
  226. const u8 *aa, const u8 *pmkid)
  227. {
  228. struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
  229. while (entry) {
  230. if ((aa == NULL || os_memcmp(entry->aa, aa, ETH_ALEN) == 0) &&
  231. (pmkid == NULL ||
  232. os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
  233. return entry;
  234. entry = entry->next;
  235. }
  236. return NULL;
  237. }
  238. /**
  239. * pmksa_cache_notify_reconfig - Reconfiguration notification for PMKSA cache
  240. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  241. *
  242. * Clear references to old data structures when wpa_supplicant is reconfigured.
  243. */
  244. void pmksa_cache_notify_reconfig(struct rsn_pmksa_cache *pmksa)
  245. {
  246. struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
  247. while (entry) {
  248. entry->network_ctx = NULL;
  249. entry = entry->next;
  250. }
  251. }
  252. static struct rsn_pmksa_cache_entry *
  253. pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
  254. const struct rsn_pmksa_cache_entry *old_entry,
  255. const u8 *aa)
  256. {
  257. struct rsn_pmksa_cache_entry *new_entry;
  258. new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
  259. aa, pmksa->sm->own_addr,
  260. old_entry->network_ctx, old_entry->akmp);
  261. if (new_entry == NULL)
  262. return NULL;
  263. /* TODO: reorder entries based on expiration time? */
  264. new_entry->expiration = old_entry->expiration;
  265. new_entry->opportunistic = 1;
  266. return new_entry;
  267. }
  268. /**
  269. * pmksa_cache_get_opportunistic - Try to get an opportunistic PMKSA entry
  270. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  271. * @network_ctx: Network configuration context
  272. * @aa: Authenticator address for the new AP
  273. * Returns: Pointer to a new PMKSA cache entry or %NULL if not available
  274. *
  275. * Try to create a new PMKSA cache entry opportunistically by guessing that the
  276. * new AP is sharing the same PMK as another AP that has the same SSID and has
  277. * already an entry in PMKSA cache.
  278. */
  279. struct rsn_pmksa_cache_entry *
  280. pmksa_cache_get_opportunistic(struct rsn_pmksa_cache *pmksa, void *network_ctx,
  281. const u8 *aa)
  282. {
  283. struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
  284. if (network_ctx == NULL)
  285. return NULL;
  286. while (entry) {
  287. if (entry->network_ctx == network_ctx) {
  288. entry = pmksa_cache_clone_entry(pmksa, entry, aa);
  289. if (entry) {
  290. wpa_printf(MSG_DEBUG, "RSN: added "
  291. "opportunistic PMKSA cache entry "
  292. "for " MACSTR, MAC2STR(aa));
  293. }
  294. return entry;
  295. }
  296. entry = entry->next;
  297. }
  298. return NULL;
  299. }
  300. /**
  301. * pmksa_cache_get_current - Get the current used PMKSA entry
  302. * @sm: Pointer to WPA state machine data from wpa_sm_init()
  303. * Returns: Pointer to the current PMKSA cache entry or %NULL if not available
  304. */
  305. struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm)
  306. {
  307. if (sm == NULL)
  308. return NULL;
  309. return sm->cur_pmksa;
  310. }
  311. /**
  312. * pmksa_cache_clear_current - Clear the current PMKSA entry selection
  313. * @sm: Pointer to WPA state machine data from wpa_sm_init()
  314. */
  315. void pmksa_cache_clear_current(struct wpa_sm *sm)
  316. {
  317. if (sm == NULL)
  318. return;
  319. sm->cur_pmksa = NULL;
  320. }
  321. /**
  322. * pmksa_cache_set_current - Set the current PMKSA entry selection
  323. * @sm: Pointer to WPA state machine data from wpa_sm_init()
  324. * @pmkid: PMKID for selecting PMKSA or %NULL if not used
  325. * @bssid: BSSID for PMKSA or %NULL if not used
  326. * @network_ctx: Network configuration context
  327. * @try_opportunistic: Whether to allow opportunistic PMKSA caching
  328. * Returns: 0 if PMKSA was found or -1 if no matching entry was found
  329. */
  330. int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid,
  331. const u8 *bssid, void *network_ctx,
  332. int try_opportunistic)
  333. {
  334. struct rsn_pmksa_cache *pmksa = sm->pmksa;
  335. sm->cur_pmksa = NULL;
  336. if (pmkid)
  337. sm->cur_pmksa = pmksa_cache_get(pmksa, NULL, pmkid);
  338. if (sm->cur_pmksa == NULL && bssid)
  339. sm->cur_pmksa = pmksa_cache_get(pmksa, bssid, NULL);
  340. if (sm->cur_pmksa == NULL && try_opportunistic && bssid)
  341. sm->cur_pmksa = pmksa_cache_get_opportunistic(pmksa,
  342. network_ctx,
  343. bssid);
  344. if (sm->cur_pmksa) {
  345. wpa_hexdump(MSG_DEBUG, "RSN: PMKID",
  346. sm->cur_pmksa->pmkid, PMKID_LEN);
  347. return 0;
  348. }
  349. return -1;
  350. }
  351. /**
  352. * pmksa_cache_list - Dump text list of entries in PMKSA cache
  353. * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  354. * @buf: Buffer for the list
  355. * @len: Length of the buffer
  356. * Returns: number of bytes written to buffer
  357. *
  358. * This function is used to generate a text format representation of the
  359. * current PMKSA cache contents for the ctrl_iface PMKSA command.
  360. */
  361. int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
  362. {
  363. int i, ret;
  364. char *pos = buf;
  365. struct rsn_pmksa_cache_entry *entry;
  366. struct os_time now;
  367. os_get_time(&now);
  368. ret = os_snprintf(pos, buf + len - pos,
  369. "Index / AA / PMKID / expiration (in seconds) / "
  370. "opportunistic\n");
  371. if (ret < 0 || ret >= buf + len - pos)
  372. return pos - buf;
  373. pos += ret;
  374. i = 0;
  375. entry = pmksa->pmksa;
  376. while (entry) {
  377. i++;
  378. ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
  379. i, MAC2STR(entry->aa));
  380. if (ret < 0 || ret >= buf + len - pos)
  381. return pos - buf;
  382. pos += ret;
  383. pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
  384. PMKID_LEN);
  385. ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
  386. (int) (entry->expiration - now.sec),
  387. entry->opportunistic);
  388. if (ret < 0 || ret >= buf + len - pos)
  389. return pos - buf;
  390. pos += ret;
  391. entry = entry->next;
  392. }
  393. return pos - buf;
  394. }
  395. /**
  396. * pmksa_cache_init - Initialize PMKSA cache
  397. * @free_cb: Callback function to be called when a PMKSA cache entry is freed
  398. * @ctx: Context pointer for free_cb function
  399. * @sm: Pointer to WPA state machine data from wpa_sm_init()
  400. * Returns: Pointer to PMKSA cache data or %NULL on failure
  401. */
  402. struct rsn_pmksa_cache *
  403. pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
  404. void *ctx, int replace),
  405. void *ctx, struct wpa_sm *sm)
  406. {
  407. struct rsn_pmksa_cache *pmksa;
  408. pmksa = os_zalloc(sizeof(*pmksa));
  409. if (pmksa) {
  410. pmksa->free_cb = free_cb;
  411. pmksa->ctx = ctx;
  412. pmksa->sm = sm;
  413. }
  414. return pmksa;
  415. }
  416. #endif /* IEEE8021X_EAPOL and !CONFIG_NO_WPA2 */