139beb93cSSam Leffler /* 2e28a4053SRui Paulo * RSN pre-authentication (supplicant) 35b9c547cSRui Paulo * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> 439beb93cSSam Leffler * 5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license. 6f05cddf9SRui Paulo * See README for more details. 739beb93cSSam Leffler */ 839beb93cSSam Leffler 939beb93cSSam Leffler #include "includes.h" 1039beb93cSSam Leffler 1139beb93cSSam Leffler #include "common.h" 1239beb93cSSam Leffler #include "wpa.h" 1339beb93cSSam Leffler #include "eloop.h" 1439beb93cSSam Leffler #include "l2_packet/l2_packet.h" 1539beb93cSSam Leffler #include "eapol_supp/eapol_supp_sm.h" 1639beb93cSSam Leffler #include "preauth.h" 1739beb93cSSam Leffler #include "pmksa_cache.h" 1839beb93cSSam Leffler #include "wpa_i.h" 1939beb93cSSam Leffler 2039beb93cSSam Leffler 21780fb4a2SCy Schubert #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA) 2239beb93cSSam Leffler 2339beb93cSSam Leffler #define PMKID_CANDIDATE_PRIO_SCAN 1000 2439beb93cSSam Leffler 2539beb93cSSam Leffler 2639beb93cSSam Leffler struct rsn_pmksa_candidate { 27e28a4053SRui Paulo struct dl_list list; 2839beb93cSSam Leffler u8 bssid[ETH_ALEN]; 2939beb93cSSam Leffler int priority; 3039beb93cSSam Leffler }; 3139beb93cSSam Leffler 3239beb93cSSam Leffler 3339beb93cSSam Leffler /** 3439beb93cSSam Leffler * pmksa_candidate_free - Free all entries in PMKSA candidate list 3539beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 3639beb93cSSam Leffler */ 3739beb93cSSam Leffler void pmksa_candidate_free(struct wpa_sm *sm) 3839beb93cSSam Leffler { 39e28a4053SRui Paulo struct rsn_pmksa_candidate *entry, *n; 4039beb93cSSam Leffler 4139beb93cSSam Leffler if (sm == NULL) 4239beb93cSSam Leffler return; 4339beb93cSSam Leffler 44e28a4053SRui Paulo dl_list_for_each_safe(entry, n, &sm->pmksa_candidates, 45e28a4053SRui Paulo struct rsn_pmksa_candidate, list) { 46e28a4053SRui Paulo dl_list_del(&entry->list); 47e28a4053SRui Paulo os_free(entry); 4839beb93cSSam Leffler } 4939beb93cSSam Leffler } 5039beb93cSSam Leffler 5139beb93cSSam Leffler 52c1d255d3SCy Schubert static int rsn_preauth_key_mgmt(int akmp) 53c1d255d3SCy Schubert { 54c1d255d3SCy Schubert return !!(akmp & (WPA_KEY_MGMT_IEEE8021X | 55c1d255d3SCy Schubert WPA_KEY_MGMT_IEEE8021X_SHA256 | 56c1d255d3SCy Schubert WPA_KEY_MGMT_IEEE8021X_SUITE_B | 57*a90b9d01SCy Schubert WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 | 58*a90b9d01SCy Schubert WPA_KEY_MGMT_IEEE8021X_SHA384)); 59c1d255d3SCy Schubert } 60c1d255d3SCy Schubert 61c1d255d3SCy Schubert 6239beb93cSSam Leffler static void rsn_preauth_receive(void *ctx, const u8 *src_addr, 6339beb93cSSam Leffler const u8 *buf, size_t len) 6439beb93cSSam Leffler { 6539beb93cSSam Leffler struct wpa_sm *sm = ctx; 6639beb93cSSam Leffler 6739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RX pre-auth from " MACSTR, MAC2STR(src_addr)); 6839beb93cSSam Leffler wpa_hexdump(MSG_MSGDUMP, "RX pre-auth", buf, len); 6939beb93cSSam Leffler 7039beb93cSSam Leffler if (sm->preauth_eapol == NULL || 7139beb93cSSam Leffler is_zero_ether_addr(sm->preauth_bssid) || 72*a90b9d01SCy Schubert !ether_addr_equal(sm->preauth_bssid, src_addr)) { 7339beb93cSSam Leffler wpa_printf(MSG_WARNING, "RSN pre-auth frame received from " 7439beb93cSSam Leffler "unexpected source " MACSTR " - dropped", 7539beb93cSSam Leffler MAC2STR(src_addr)); 7639beb93cSSam Leffler return; 7739beb93cSSam Leffler } 7839beb93cSSam Leffler 79*a90b9d01SCy Schubert eapol_sm_rx_eapol(sm->preauth_eapol, src_addr, buf, len, 80*a90b9d01SCy Schubert FRAME_ENCRYPTION_UNKNOWN); 8139beb93cSSam Leffler } 8239beb93cSSam Leffler 8339beb93cSSam Leffler 845b9c547cSRui Paulo static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, 855b9c547cSRui Paulo enum eapol_supp_result result, 8639beb93cSSam Leffler void *ctx) 8739beb93cSSam Leffler { 8839beb93cSSam Leffler struct wpa_sm *sm = ctx; 8939beb93cSSam Leffler u8 pmk[PMK_LEN]; 9039beb93cSSam Leffler 915b9c547cSRui Paulo if (result == EAPOL_SUPP_RESULT_SUCCESS) { 9239beb93cSSam Leffler int res, pmk_len; 9339beb93cSSam Leffler pmk_len = PMK_LEN; 9439beb93cSSam Leffler res = eapol_sm_get_key(eapol, pmk, PMK_LEN); 9539beb93cSSam Leffler if (res) { 9639beb93cSSam Leffler /* 9739beb93cSSam Leffler * EAP-LEAP is an exception from other EAP methods: it 9839beb93cSSam Leffler * uses only 16-byte PMK. 9939beb93cSSam Leffler */ 10039beb93cSSam Leffler res = eapol_sm_get_key(eapol, pmk, 16); 10139beb93cSSam Leffler pmk_len = 16; 10239beb93cSSam Leffler } 10339beb93cSSam Leffler if (res == 0) { 10439beb93cSSam Leffler wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from pre-auth", 10539beb93cSSam Leffler pmk, pmk_len); 10639beb93cSSam Leffler sm->pmk_len = pmk_len; 107780fb4a2SCy Schubert pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 1085b9c547cSRui Paulo NULL, 0, 10939beb93cSSam Leffler sm->preauth_bssid, sm->own_addr, 11039beb93cSSam Leffler sm->network_ctx, 11185732ac8SCy Schubert WPA_KEY_MGMT_IEEE8021X, NULL); 11239beb93cSSam Leffler } else { 113e28a4053SRui Paulo wpa_msg(sm->ctx->msg_ctx, MSG_INFO, 114e28a4053SRui Paulo "RSN: failed to get master session key from " 115e28a4053SRui Paulo "pre-auth EAPOL state machines"); 1165b9c547cSRui Paulo result = EAPOL_SUPP_RESULT_FAILURE; 11739beb93cSSam Leffler } 11839beb93cSSam Leffler } 11939beb93cSSam Leffler 120e28a4053SRui Paulo wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " 121e28a4053SRui Paulo MACSTR " %s", MAC2STR(sm->preauth_bssid), 1225b9c547cSRui Paulo result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" : 1235b9c547cSRui Paulo "failed"); 12439beb93cSSam Leffler 12539beb93cSSam Leffler rsn_preauth_deinit(sm); 12639beb93cSSam Leffler rsn_preauth_candidate_process(sm); 12739beb93cSSam Leffler } 12839beb93cSSam Leffler 12939beb93cSSam Leffler 13039beb93cSSam Leffler static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx) 13139beb93cSSam Leffler { 13239beb93cSSam Leffler struct wpa_sm *sm = eloop_ctx; 13339beb93cSSam Leffler 134e28a4053SRui Paulo wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " 135e28a4053SRui Paulo MACSTR " timed out", MAC2STR(sm->preauth_bssid)); 13639beb93cSSam Leffler rsn_preauth_deinit(sm); 13739beb93cSSam Leffler rsn_preauth_candidate_process(sm); 13839beb93cSSam Leffler } 13939beb93cSSam Leffler 14039beb93cSSam Leffler 14139beb93cSSam Leffler static int rsn_preauth_eapol_send(void *ctx, int type, const u8 *buf, 14239beb93cSSam Leffler size_t len) 14339beb93cSSam Leffler { 14439beb93cSSam Leffler struct wpa_sm *sm = ctx; 14539beb93cSSam Leffler u8 *msg; 14639beb93cSSam Leffler size_t msglen; 14739beb93cSSam Leffler int res; 14839beb93cSSam Leffler 14939beb93cSSam Leffler /* TODO: could add l2_packet_sendmsg that allows fragments to avoid 15039beb93cSSam Leffler * extra copy here */ 15139beb93cSSam Leffler 15239beb93cSSam Leffler if (sm->l2_preauth == NULL) 15339beb93cSSam Leffler return -1; 15439beb93cSSam Leffler 15539beb93cSSam Leffler msg = wpa_sm_alloc_eapol(sm, type, buf, len, &msglen, NULL); 15639beb93cSSam Leffler if (msg == NULL) 15739beb93cSSam Leffler return -1; 15839beb93cSSam Leffler 15939beb93cSSam Leffler wpa_hexdump(MSG_MSGDUMP, "TX EAPOL (preauth)", msg, msglen); 16039beb93cSSam Leffler res = l2_packet_send(sm->l2_preauth, sm->preauth_bssid, 16139beb93cSSam Leffler ETH_P_RSN_PREAUTH, msg, msglen); 16239beb93cSSam Leffler os_free(msg); 16339beb93cSSam Leffler return res; 16439beb93cSSam Leffler } 16539beb93cSSam Leffler 16639beb93cSSam Leffler 16739beb93cSSam Leffler /** 16839beb93cSSam Leffler * rsn_preauth_init - Start new RSN pre-authentication 16939beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 17039beb93cSSam Leffler * @dst: Authenticator address (BSSID) with which to preauthenticate 17139beb93cSSam Leffler * @eap_conf: Current EAP configuration 17239beb93cSSam Leffler * Returns: 0 on success, -1 on another pre-authentication is in progress, 17339beb93cSSam Leffler * -2 on layer 2 packet initialization failure, -3 on EAPOL state machine 17439beb93cSSam Leffler * initialization failure, -4 on memory allocation failure 17539beb93cSSam Leffler * 17639beb93cSSam Leffler * This function request an RSN pre-authentication with a given destination 17739beb93cSSam Leffler * address. This is usually called for PMKSA candidates found from scan results 17839beb93cSSam Leffler * or from driver reports. In addition, ctrl_iface PREAUTH command can trigger 17939beb93cSSam Leffler * pre-authentication. 18039beb93cSSam Leffler */ 18139beb93cSSam Leffler int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, 18239beb93cSSam Leffler struct eap_peer_config *eap_conf) 18339beb93cSSam Leffler { 18439beb93cSSam Leffler struct eapol_config eapol_conf; 18539beb93cSSam Leffler struct eapol_ctx *ctx; 1865b9c547cSRui Paulo int ret; 18739beb93cSSam Leffler 18839beb93cSSam Leffler if (sm->preauth_eapol) 18939beb93cSSam Leffler return -1; 19039beb93cSSam Leffler 191e28a4053SRui Paulo wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, 192e28a4053SRui Paulo "RSN: starting pre-authentication with " MACSTR, MAC2STR(dst)); 19339beb93cSSam Leffler 19439beb93cSSam Leffler sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr, 19539beb93cSSam Leffler ETH_P_RSN_PREAUTH, 19639beb93cSSam Leffler rsn_preauth_receive, sm, 0); 19739beb93cSSam Leffler if (sm->l2_preauth == NULL) { 19839beb93cSSam Leffler wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 packet " 19939beb93cSSam Leffler "processing for pre-authentication"); 20039beb93cSSam Leffler return -2; 20139beb93cSSam Leffler } 20239beb93cSSam Leffler 20339beb93cSSam Leffler if (sm->bridge_ifname) { 20439beb93cSSam Leffler sm->l2_preauth_br = l2_packet_init(sm->bridge_ifname, 20539beb93cSSam Leffler sm->own_addr, 20639beb93cSSam Leffler ETH_P_RSN_PREAUTH, 20739beb93cSSam Leffler rsn_preauth_receive, sm, 0); 20839beb93cSSam Leffler if (sm->l2_preauth_br == NULL) { 20939beb93cSSam Leffler wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 " 21039beb93cSSam Leffler "packet processing (bridge) for " 21139beb93cSSam Leffler "pre-authentication"); 2125b9c547cSRui Paulo ret = -2; 2135b9c547cSRui Paulo goto fail; 21439beb93cSSam Leffler } 21539beb93cSSam Leffler } 21639beb93cSSam Leffler 21739beb93cSSam Leffler ctx = os_zalloc(sizeof(*ctx)); 21839beb93cSSam Leffler if (ctx == NULL) { 21939beb93cSSam Leffler wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context."); 2205b9c547cSRui Paulo ret = -4; 2215b9c547cSRui Paulo goto fail; 22239beb93cSSam Leffler } 22339beb93cSSam Leffler ctx->ctx = sm->ctx->ctx; 22439beb93cSSam Leffler ctx->msg_ctx = sm->ctx->ctx; 22539beb93cSSam Leffler ctx->preauth = 1; 22639beb93cSSam Leffler ctx->cb = rsn_preauth_eapol_cb; 22739beb93cSSam Leffler ctx->cb_ctx = sm; 22839beb93cSSam Leffler ctx->scard_ctx = sm->scard_ctx; 22939beb93cSSam Leffler ctx->eapol_send = rsn_preauth_eapol_send; 23039beb93cSSam Leffler ctx->eapol_send_ctx = sm; 23139beb93cSSam Leffler ctx->set_config_blob = sm->ctx->set_config_blob; 23239beb93cSSam Leffler ctx->get_config_blob = sm->ctx->get_config_blob; 23339beb93cSSam Leffler 23439beb93cSSam Leffler sm->preauth_eapol = eapol_sm_init(ctx); 23539beb93cSSam Leffler if (sm->preauth_eapol == NULL) { 23639beb93cSSam Leffler os_free(ctx); 23739beb93cSSam Leffler wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL " 23839beb93cSSam Leffler "state machines for pre-authentication"); 2395b9c547cSRui Paulo ret = -3; 2405b9c547cSRui Paulo goto fail; 24139beb93cSSam Leffler } 24239beb93cSSam Leffler os_memset(&eapol_conf, 0, sizeof(eapol_conf)); 24339beb93cSSam Leffler eapol_conf.accept_802_1x_keys = 0; 24439beb93cSSam Leffler eapol_conf.required_keys = 0; 24539beb93cSSam Leffler eapol_conf.fast_reauth = sm->fast_reauth; 24639beb93cSSam Leffler eapol_conf.workaround = sm->eap_workaround; 24739beb93cSSam Leffler eapol_sm_notify_config(sm->preauth_eapol, eap_conf, &eapol_conf); 24839beb93cSSam Leffler /* 24939beb93cSSam Leffler * Use a shorter startPeriod with preauthentication since the first 25039beb93cSSam Leffler * preauth EAPOL-Start frame may end up being dropped due to race 25139beb93cSSam Leffler * condition in the AP between the data receive and key configuration 25239beb93cSSam Leffler * after the 4-Way Handshake. 25339beb93cSSam Leffler */ 25439beb93cSSam Leffler eapol_sm_configure(sm->preauth_eapol, -1, -1, 5, 6); 25539beb93cSSam Leffler os_memcpy(sm->preauth_bssid, dst, ETH_ALEN); 25639beb93cSSam Leffler 257c1d255d3SCy Schubert eapol_sm_notify_portValid(sm->preauth_eapol, true); 25839beb93cSSam Leffler /* 802.1X::portControl = Auto */ 259c1d255d3SCy Schubert eapol_sm_notify_portEnabled(sm->preauth_eapol, true); 26039beb93cSSam Leffler 26139beb93cSSam Leffler eloop_register_timeout(sm->dot11RSNAConfigSATimeout, 0, 26239beb93cSSam Leffler rsn_preauth_timeout, sm, NULL); 26339beb93cSSam Leffler 26439beb93cSSam Leffler return 0; 2655b9c547cSRui Paulo 2665b9c547cSRui Paulo fail: 2675b9c547cSRui Paulo if (sm->l2_preauth_br) { 2685b9c547cSRui Paulo l2_packet_deinit(sm->l2_preauth_br); 2695b9c547cSRui Paulo sm->l2_preauth_br = NULL; 2705b9c547cSRui Paulo } 2715b9c547cSRui Paulo l2_packet_deinit(sm->l2_preauth); 2725b9c547cSRui Paulo sm->l2_preauth = NULL; 2735b9c547cSRui Paulo return ret; 27439beb93cSSam Leffler } 27539beb93cSSam Leffler 27639beb93cSSam Leffler 27739beb93cSSam Leffler /** 27839beb93cSSam Leffler * rsn_preauth_deinit - Abort RSN pre-authentication 27939beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 28039beb93cSSam Leffler * 28139beb93cSSam Leffler * This function aborts the current RSN pre-authentication (if one is started) 28239beb93cSSam Leffler * and frees resources allocated for it. 28339beb93cSSam Leffler */ 28439beb93cSSam Leffler void rsn_preauth_deinit(struct wpa_sm *sm) 28539beb93cSSam Leffler { 28639beb93cSSam Leffler if (sm == NULL || !sm->preauth_eapol) 28739beb93cSSam Leffler return; 28839beb93cSSam Leffler 28939beb93cSSam Leffler eloop_cancel_timeout(rsn_preauth_timeout, sm, NULL); 29039beb93cSSam Leffler eapol_sm_deinit(sm->preauth_eapol); 29139beb93cSSam Leffler sm->preauth_eapol = NULL; 29239beb93cSSam Leffler os_memset(sm->preauth_bssid, 0, ETH_ALEN); 29339beb93cSSam Leffler 29439beb93cSSam Leffler l2_packet_deinit(sm->l2_preauth); 29539beb93cSSam Leffler sm->l2_preauth = NULL; 29639beb93cSSam Leffler if (sm->l2_preauth_br) { 29739beb93cSSam Leffler l2_packet_deinit(sm->l2_preauth_br); 29839beb93cSSam Leffler sm->l2_preauth_br = NULL; 29939beb93cSSam Leffler } 30039beb93cSSam Leffler } 30139beb93cSSam Leffler 30239beb93cSSam Leffler 30339beb93cSSam Leffler /** 30439beb93cSSam Leffler * rsn_preauth_candidate_process - Process PMKSA candidates 30539beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 30639beb93cSSam Leffler * 30739beb93cSSam Leffler * Go through the PMKSA candidates and start pre-authentication if a candidate 30839beb93cSSam Leffler * without an existing PMKSA cache entry is found. Processed candidates will be 30939beb93cSSam Leffler * removed from the list. 31039beb93cSSam Leffler */ 31139beb93cSSam Leffler void rsn_preauth_candidate_process(struct wpa_sm *sm) 31239beb93cSSam Leffler { 313e28a4053SRui Paulo struct rsn_pmksa_candidate *candidate, *n; 31439beb93cSSam Leffler 315e28a4053SRui Paulo if (dl_list_empty(&sm->pmksa_candidates)) 31639beb93cSSam Leffler return; 31739beb93cSSam Leffler 31839beb93cSSam Leffler /* TODO: drop priority for old candidate entries */ 31939beb93cSSam Leffler 320e28a4053SRui Paulo wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: processing PMKSA candidate " 32139beb93cSSam Leffler "list"); 32239beb93cSSam Leffler if (sm->preauth_eapol || 32339beb93cSSam Leffler sm->proto != WPA_PROTO_RSN || 32439beb93cSSam Leffler wpa_sm_get_state(sm) != WPA_COMPLETED || 325c1d255d3SCy Schubert !rsn_preauth_key_mgmt(sm->key_mgmt)) { 326e28a4053SRui Paulo wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable " 327e28a4053SRui Paulo "state for new pre-authentication"); 32839beb93cSSam Leffler return; /* invalid state for new pre-auth */ 32939beb93cSSam Leffler } 33039beb93cSSam Leffler 331e28a4053SRui Paulo dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates, 332e28a4053SRui Paulo struct rsn_pmksa_candidate, list) { 33339beb93cSSam Leffler struct rsn_pmksa_cache_entry *p = NULL; 334*a90b9d01SCy Schubert p = pmksa_cache_get(sm->pmksa, candidate->bssid, sm->own_addr, 335*a90b9d01SCy Schubert NULL, NULL, 0); 336*a90b9d01SCy Schubert if (!ether_addr_equal(sm->bssid, candidate->bssid) && 33739beb93cSSam Leffler (p == NULL || p->opportunistic)) { 338e28a4053SRui Paulo wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA " 33939beb93cSSam Leffler "candidate " MACSTR 34039beb93cSSam Leffler " selected for pre-authentication", 34139beb93cSSam Leffler MAC2STR(candidate->bssid)); 342e28a4053SRui Paulo dl_list_del(&candidate->list); 34339beb93cSSam Leffler rsn_preauth_init(sm, candidate->bssid, 34439beb93cSSam Leffler sm->eap_conf_ctx); 34539beb93cSSam Leffler os_free(candidate); 34639beb93cSSam Leffler return; 34739beb93cSSam Leffler } 348e28a4053SRui Paulo wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA candidate " 34939beb93cSSam Leffler MACSTR " does not need pre-authentication anymore", 35039beb93cSSam Leffler MAC2STR(candidate->bssid)); 35139beb93cSSam Leffler /* Some drivers (e.g., NDIS) expect to get notified about the 35239beb93cSSam Leffler * PMKIDs again, so report the existing data now. */ 35339beb93cSSam Leffler if (p) { 35485732ac8SCy Schubert wpa_sm_add_pmkid(sm, NULL, candidate->bssid, p->pmkid, 355c1d255d3SCy Schubert NULL, p->pmk, p->pmk_len, 0, 0, 356c1d255d3SCy Schubert p->akmp); 35739beb93cSSam Leffler } 35839beb93cSSam Leffler 359e28a4053SRui Paulo dl_list_del(&candidate->list); 36039beb93cSSam Leffler os_free(candidate); 36139beb93cSSam Leffler } 362e28a4053SRui Paulo wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no more pending PMKSA " 36339beb93cSSam Leffler "candidates"); 36439beb93cSSam Leffler } 36539beb93cSSam Leffler 36639beb93cSSam Leffler 36739beb93cSSam Leffler /** 36839beb93cSSam Leffler * pmksa_candidate_add - Add a new PMKSA candidate 36939beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 37039beb93cSSam Leffler * @bssid: BSSID (authenticator address) of the candidate 37139beb93cSSam Leffler * @prio: Priority (the smaller number, the higher priority) 37239beb93cSSam Leffler * @preauth: Whether the candidate AP advertises support for pre-authentication 37339beb93cSSam Leffler * 37439beb93cSSam Leffler * This function is used to add PMKSA candidates for RSN pre-authentication. It 37539beb93cSSam Leffler * is called from scan result processing and from driver events for PMKSA 37639beb93cSSam Leffler * candidates, i.e., EVENT_PMKID_CANDIDATE events to wpa_supplicant_event(). 37739beb93cSSam Leffler */ 37839beb93cSSam Leffler void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, 37939beb93cSSam Leffler int prio, int preauth) 38039beb93cSSam Leffler { 381e28a4053SRui Paulo struct rsn_pmksa_candidate *cand, *pos; 38239beb93cSSam Leffler 38339beb93cSSam Leffler if (sm->network_ctx && sm->proactive_key_caching) 38439beb93cSSam Leffler pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx, 38585732ac8SCy Schubert bssid, 0); 38639beb93cSSam Leffler 38739beb93cSSam Leffler if (!preauth) { 38839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "RSN: Ignored PMKID candidate without " 38939beb93cSSam Leffler "preauth flag"); 39039beb93cSSam Leffler return; 39139beb93cSSam Leffler } 39239beb93cSSam Leffler 39339beb93cSSam Leffler /* If BSSID already on candidate list, update the priority of the old 39439beb93cSSam Leffler * entry. Do not override priority based on normal scan results. */ 395e28a4053SRui Paulo cand = NULL; 396e28a4053SRui Paulo dl_list_for_each(pos, &sm->pmksa_candidates, 397e28a4053SRui Paulo struct rsn_pmksa_candidate, list) { 398*a90b9d01SCy Schubert if (ether_addr_equal(pos->bssid, bssid)) { 399e28a4053SRui Paulo cand = pos; 40039beb93cSSam Leffler break; 40139beb93cSSam Leffler } 40239beb93cSSam Leffler } 40339beb93cSSam Leffler 40439beb93cSSam Leffler if (cand) { 405e28a4053SRui Paulo dl_list_del(&cand->list); 40639beb93cSSam Leffler if (prio < PMKID_CANDIDATE_PRIO_SCAN) 40739beb93cSSam Leffler cand->priority = prio; 40839beb93cSSam Leffler } else { 40939beb93cSSam Leffler cand = os_zalloc(sizeof(*cand)); 41039beb93cSSam Leffler if (cand == NULL) 41139beb93cSSam Leffler return; 41239beb93cSSam Leffler os_memcpy(cand->bssid, bssid, ETH_ALEN); 41339beb93cSSam Leffler cand->priority = prio; 41439beb93cSSam Leffler } 41539beb93cSSam Leffler 41639beb93cSSam Leffler /* Add candidate to the list; order by increasing priority value. i.e., 41739beb93cSSam Leffler * highest priority (smallest value) first. */ 418e28a4053SRui Paulo dl_list_for_each(pos, &sm->pmksa_candidates, 419e28a4053SRui Paulo struct rsn_pmksa_candidate, list) { 420e28a4053SRui Paulo if (cand->priority <= pos->priority) { 4215b9c547cSRui Paulo if (!pos->list.prev) { 4225b9c547cSRui Paulo /* 4235b9c547cSRui Paulo * This cannot really happen in pracrice since 4245b9c547cSRui Paulo * pos was fetched from the list and the prev 4255b9c547cSRui Paulo * pointer must be set. It looks like clang 4265b9c547cSRui Paulo * static analyzer gets confused with the 4275b9c547cSRui Paulo * dl_list_del(&cand->list) call above and ends 4285b9c547cSRui Paulo * up assuming pos->list.prev could be NULL. 4295b9c547cSRui Paulo */ 4305b9c547cSRui Paulo os_free(cand); 4315b9c547cSRui Paulo return; 4325b9c547cSRui Paulo } 433e28a4053SRui Paulo dl_list_add(pos->list.prev, &cand->list); 434e28a4053SRui Paulo cand = NULL; 43539beb93cSSam Leffler break; 43639beb93cSSam Leffler } 437e28a4053SRui Paulo } 438e28a4053SRui Paulo if (cand) 439e28a4053SRui Paulo dl_list_add_tail(&sm->pmksa_candidates, &cand->list); 44039beb93cSSam Leffler 441e28a4053SRui Paulo wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: added PMKSA cache " 44239beb93cSSam Leffler "candidate " MACSTR " prio %d", MAC2STR(bssid), prio); 44339beb93cSSam Leffler rsn_preauth_candidate_process(sm); 44439beb93cSSam Leffler } 44539beb93cSSam Leffler 44639beb93cSSam Leffler 44739beb93cSSam Leffler /* TODO: schedule periodic scans if current AP supports preauth */ 44839beb93cSSam Leffler 44939beb93cSSam Leffler /** 450e28a4053SRui Paulo * rsn_preauth_scan_results - Start processing scan results for canditates 45139beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 452e28a4053SRui Paulo * Returns: 0 if ready to process results or -1 to skip processing 45339beb93cSSam Leffler * 454e28a4053SRui Paulo * This functions is used to notify RSN code about start of new scan results 455e28a4053SRui Paulo * processing. The actual scan results will be provided by calling 456e28a4053SRui Paulo * rsn_preauth_scan_result() for each BSS if this function returned 0. 45739beb93cSSam Leffler */ 458e28a4053SRui Paulo int rsn_preauth_scan_results(struct wpa_sm *sm) 45939beb93cSSam Leffler { 46039beb93cSSam Leffler if (sm->ssid_len == 0) 461e28a4053SRui Paulo return -1; 46239beb93cSSam Leffler 46339beb93cSSam Leffler /* 46439beb93cSSam Leffler * TODO: is it ok to free all candidates? What about the entries 46539beb93cSSam Leffler * received from EVENT_PMKID_CANDIDATE? 46639beb93cSSam Leffler */ 46739beb93cSSam Leffler pmksa_candidate_free(sm); 46839beb93cSSam Leffler 469e28a4053SRui Paulo return 0; 47039beb93cSSam Leffler } 471e28a4053SRui Paulo 472e28a4053SRui Paulo 473e28a4053SRui Paulo /** 474e28a4053SRui Paulo * rsn_preauth_scan_result - Processing scan result for PMKSA canditates 475e28a4053SRui Paulo * @sm: Pointer to WPA state machine data from wpa_sm_init() 476e28a4053SRui Paulo * 477e28a4053SRui Paulo * Add all suitable APs (Authenticators) from scan results into PMKSA 478e28a4053SRui Paulo * candidate list. 479e28a4053SRui Paulo */ 480e28a4053SRui Paulo void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, 481e28a4053SRui Paulo const u8 *ssid, const u8 *rsn) 482e28a4053SRui Paulo { 483e28a4053SRui Paulo struct wpa_ie_data ie; 484e28a4053SRui Paulo struct rsn_pmksa_cache_entry *pmksa; 485e28a4053SRui Paulo 486e28a4053SRui Paulo if (ssid[1] != sm->ssid_len || 487e28a4053SRui Paulo os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0) 488e28a4053SRui Paulo return; /* Not for the current SSID */ 489e28a4053SRui Paulo 490*a90b9d01SCy Schubert if (ether_addr_equal(bssid, sm->bssid)) 491e28a4053SRui Paulo return; /* Ignore current AP */ 492e28a4053SRui Paulo 493e28a4053SRui Paulo if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) 494e28a4053SRui Paulo return; 495e28a4053SRui Paulo 496*a90b9d01SCy Schubert pmksa = pmksa_cache_get(sm->pmksa, bssid, sm->own_addr, NULL, NULL, 0); 497e28a4053SRui Paulo if (pmksa && (!pmksa->opportunistic || 498e28a4053SRui Paulo !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) 499e28a4053SRui Paulo return; 500e28a4053SRui Paulo 501c1d255d3SCy Schubert if (!rsn_preauth_key_mgmt(ie.key_mgmt)) 502c1d255d3SCy Schubert return; 503c1d255d3SCy Schubert 504e28a4053SRui Paulo /* Give less priority to candidates found from normal scan results. */ 505e28a4053SRui Paulo pmksa_candidate_add(sm, bssid, PMKID_CANDIDATE_PRIO_SCAN, 506e28a4053SRui Paulo ie.capabilities & WPA_CAPABILITY_PREAUTH); 50739beb93cSSam Leffler } 50839beb93cSSam Leffler 50939beb93cSSam Leffler 51039beb93cSSam Leffler #ifdef CONFIG_CTRL_IFACE 51139beb93cSSam Leffler /** 51239beb93cSSam Leffler * rsn_preauth_get_status - Get pre-authentication status 51339beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 51439beb93cSSam Leffler * @buf: Buffer for status information 51539beb93cSSam Leffler * @buflen: Maximum buffer length 51639beb93cSSam Leffler * @verbose: Whether to include verbose status information 51739beb93cSSam Leffler * Returns: Number of bytes written to buf. 51839beb93cSSam Leffler * 51939beb93cSSam Leffler * Query WPA2 pre-authentication for status information. This function fills in 52039beb93cSSam Leffler * a text area with current status information. If the buffer (buf) is not 52139beb93cSSam Leffler * large enough, status information will be truncated to fit the buffer. 52239beb93cSSam Leffler */ 52339beb93cSSam Leffler int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen, 52439beb93cSSam Leffler int verbose) 52539beb93cSSam Leffler { 52639beb93cSSam Leffler char *pos = buf, *end = buf + buflen; 52739beb93cSSam Leffler int res, ret; 52839beb93cSSam Leffler 52939beb93cSSam Leffler if (sm->preauth_eapol) { 53039beb93cSSam Leffler ret = os_snprintf(pos, end - pos, "Pre-authentication " 53139beb93cSSam Leffler "EAPOL state machines:\n"); 5325b9c547cSRui Paulo if (os_snprintf_error(end - pos, ret)) 53339beb93cSSam Leffler return pos - buf; 53439beb93cSSam Leffler pos += ret; 53539beb93cSSam Leffler res = eapol_sm_get_status(sm->preauth_eapol, 53639beb93cSSam Leffler pos, end - pos, verbose); 53739beb93cSSam Leffler if (res >= 0) 53839beb93cSSam Leffler pos += res; 53939beb93cSSam Leffler } 54039beb93cSSam Leffler 54139beb93cSSam Leffler return pos - buf; 54239beb93cSSam Leffler } 54339beb93cSSam Leffler #endif /* CONFIG_CTRL_IFACE */ 54439beb93cSSam Leffler 54539beb93cSSam Leffler 54639beb93cSSam Leffler /** 54739beb93cSSam Leffler * rsn_preauth_in_progress - Verify whether pre-authentication is in progress 54839beb93cSSam Leffler * @sm: Pointer to WPA state machine data from wpa_sm_init() 54939beb93cSSam Leffler */ 55039beb93cSSam Leffler int rsn_preauth_in_progress(struct wpa_sm *sm) 55139beb93cSSam Leffler { 55239beb93cSSam Leffler return sm->preauth_eapol != NULL; 55339beb93cSSam Leffler } 55439beb93cSSam Leffler 555780fb4a2SCy Schubert #endif /* IEEE8021X_EAPOL && !CONFIG_NO_WPA */ 556