70f8cc8ec8
The three existing enums were already depending on using the same values in couple of places and it is just simpler to standardize on one of these to avoid need for mapping between different enums for the exact same thing.
440 lines
13 KiB
C
440 lines
13 KiB
C
/*
|
|
* wpa_supplicant - SME
|
|
* Copyright (c) 2009, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* Alternatively, this software may be distributed under the terms of BSD
|
|
* license.
|
|
*
|
|
* See README and COPYING for more details.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#include "common.h"
|
|
#include "common/ieee802_11_defs.h"
|
|
#include "eapol_supp/eapol_supp_sm.h"
|
|
#include "common/wpa_common.h"
|
|
#include "rsn_supp/wpa.h"
|
|
#include "rsn_supp/pmksa_cache.h"
|
|
#include "config.h"
|
|
#include "wpa_supplicant_i.h"
|
|
#include "driver_i.h"
|
|
#include "wpas_glue.h"
|
|
#include "wps_supplicant.h"
|
|
#include "notify.h"
|
|
#include "blacklist.h"
|
|
#include "bss.h"
|
|
#include "scan.h"
|
|
#include "sme.h"
|
|
|
|
void sme_authenticate(struct wpa_supplicant *wpa_s,
|
|
struct wpa_bss *bss, struct wpa_ssid *ssid)
|
|
{
|
|
struct wpa_driver_auth_params params;
|
|
struct wpa_ssid *old_ssid;
|
|
#ifdef CONFIG_IEEE80211R
|
|
const u8 *ie;
|
|
#endif /* CONFIG_IEEE80211R */
|
|
#ifdef CONFIG_IEEE80211R
|
|
const u8 *md = NULL;
|
|
#endif /* CONFIG_IEEE80211R */
|
|
int i, bssid_changed;
|
|
|
|
if (bss == NULL) {
|
|
wpa_printf(MSG_ERROR, "SME: No scan result available for the "
|
|
"network");
|
|
return;
|
|
}
|
|
|
|
wpa_s->current_bss = bss;
|
|
|
|
os_memset(¶ms, 0, sizeof(params));
|
|
wpa_s->reassociate = 0;
|
|
|
|
params.freq = bss->freq;
|
|
params.bssid = bss->bssid;
|
|
params.ssid = bss->ssid;
|
|
params.ssid_len = bss->ssid_len;
|
|
|
|
if (wpa_s->sme.ssid_len != params.ssid_len ||
|
|
os_memcmp(wpa_s->sme.ssid, params.ssid, params.ssid_len) != 0)
|
|
wpa_s->sme.prev_bssid_set = 0;
|
|
|
|
wpa_s->sme.freq = params.freq;
|
|
os_memcpy(wpa_s->sme.ssid, params.ssid, params.ssid_len);
|
|
wpa_s->sme.ssid_len = params.ssid_len;
|
|
|
|
params.auth_alg = AUTH_ALG_OPEN_SYSTEM;
|
|
#ifdef IEEE8021X_EAPOL
|
|
if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
|
|
if (ssid->leap) {
|
|
if (ssid->non_leap == 0)
|
|
params.auth_alg = AUTH_ALG_LEAP;
|
|
else
|
|
params.auth_alg |= AUTH_ALG_LEAP;
|
|
}
|
|
}
|
|
#endif /* IEEE8021X_EAPOL */
|
|
wpa_printf(MSG_DEBUG, "Automatic auth_alg selection: 0x%x",
|
|
params.auth_alg);
|
|
if (ssid->auth_alg) {
|
|
params.auth_alg = 0;
|
|
if (ssid->auth_alg & WPA_AUTH_ALG_OPEN)
|
|
params.auth_alg |= AUTH_ALG_OPEN_SYSTEM;
|
|
if (ssid->auth_alg & WPA_AUTH_ALG_SHARED)
|
|
params.auth_alg |= AUTH_ALG_SHARED_KEY;
|
|
if (ssid->auth_alg & WPA_AUTH_ALG_LEAP)
|
|
params.auth_alg |= AUTH_ALG_LEAP;
|
|
wpa_printf(MSG_DEBUG, "Overriding auth_alg selection: 0x%x",
|
|
params.auth_alg);
|
|
}
|
|
|
|
for (i = 0; i < NUM_WEP_KEYS; i++) {
|
|
if (ssid->wep_key_len[i])
|
|
params.wep_key[i] = ssid->wep_key[i];
|
|
params.wep_key_len[i] = ssid->wep_key_len[i];
|
|
}
|
|
params.wep_tx_keyidx = ssid->wep_tx_keyidx;
|
|
|
|
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
|
|
os_memset(wpa_s->bssid, 0, ETH_ALEN);
|
|
os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
|
|
if (bssid_changed)
|
|
wpas_notify_bssid_changed(wpa_s);
|
|
|
|
if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
|
|
wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
|
|
(ssid->key_mgmt & (WPA_KEY_MGMT_IEEE8021X | WPA_KEY_MGMT_PSK |
|
|
WPA_KEY_MGMT_FT_IEEE8021X |
|
|
WPA_KEY_MGMT_FT_PSK |
|
|
WPA_KEY_MGMT_IEEE8021X_SHA256 |
|
|
WPA_KEY_MGMT_PSK_SHA256))) {
|
|
int try_opportunistic;
|
|
try_opportunistic = ssid->proactive_key_caching &&
|
|
(ssid->proto & WPA_PROTO_RSN);
|
|
if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
|
|
wpa_s->current_ssid,
|
|
try_opportunistic) == 0)
|
|
eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
|
|
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
|
|
if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
|
|
wpa_s->sme.assoc_req_ie,
|
|
&wpa_s->sme.assoc_req_ie_len)) {
|
|
wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
|
|
"management and encryption suites");
|
|
return;
|
|
}
|
|
} else if (ssid->key_mgmt &
|
|
(WPA_KEY_MGMT_PSK | WPA_KEY_MGMT_IEEE8021X |
|
|
WPA_KEY_MGMT_WPA_NONE | WPA_KEY_MGMT_FT_PSK |
|
|
WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_PSK_SHA256 |
|
|
WPA_KEY_MGMT_IEEE8021X_SHA256)) {
|
|
wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
|
|
if (wpa_supplicant_set_suites(wpa_s, NULL, ssid,
|
|
wpa_s->sme.assoc_req_ie,
|
|
&wpa_s->sme.assoc_req_ie_len)) {
|
|
wpa_printf(MSG_WARNING, "SME: Failed to set WPA key "
|
|
"management and encryption suites (no scan "
|
|
"results)");
|
|
return;
|
|
}
|
|
#ifdef CONFIG_WPS
|
|
} else if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
|
|
struct wpabuf *wps_ie;
|
|
wps_ie = wps_build_assoc_req_ie(wpas_wps_get_req_type(ssid));
|
|
if (wps_ie && wpabuf_len(wps_ie) <=
|
|
sizeof(wpa_s->sme.assoc_req_ie)) {
|
|
wpa_s->sme.assoc_req_ie_len = wpabuf_len(wps_ie);
|
|
os_memcpy(wpa_s->sme.assoc_req_ie, wpabuf_head(wps_ie),
|
|
wpa_s->sme.assoc_req_ie_len);
|
|
} else
|
|
wpa_s->sme.assoc_req_ie_len = 0;
|
|
wpabuf_free(wps_ie);
|
|
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
|
|
#endif /* CONFIG_WPS */
|
|
} else {
|
|
wpa_supplicant_set_non_wpa_policy(wpa_s, ssid);
|
|
wpa_s->sme.assoc_req_ie_len = 0;
|
|
}
|
|
|
|
#ifdef CONFIG_IEEE80211R
|
|
ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
|
|
if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
|
|
md = ie + 2;
|
|
wpa_sm_set_ft_params(wpa_s->wpa, md, NULL, 0, NULL);
|
|
if (md) {
|
|
/* Prepare for the next transition */
|
|
wpa_ft_prepare_auth_request(wpa_s->wpa);
|
|
}
|
|
|
|
if (md && ssid->key_mgmt & (WPA_KEY_MGMT_FT_PSK |
|
|
WPA_KEY_MGMT_FT_IEEE8021X)) {
|
|
if (wpa_s->sme.assoc_req_ie_len + 5 <
|
|
sizeof(wpa_s->sme.assoc_req_ie)) {
|
|
struct rsn_mdie *mdie;
|
|
u8 *pos = wpa_s->sme.assoc_req_ie +
|
|
wpa_s->sme.assoc_req_ie_len;
|
|
*pos++ = WLAN_EID_MOBILITY_DOMAIN;
|
|
*pos++ = sizeof(*mdie);
|
|
mdie = (struct rsn_mdie *) pos;
|
|
os_memcpy(mdie->mobility_domain, md,
|
|
MOBILITY_DOMAIN_ID_LEN);
|
|
mdie->ft_capab = 0;
|
|
wpa_s->sme.assoc_req_ie_len += 5;
|
|
}
|
|
|
|
if (wpa_s->sme.ft_used &&
|
|
os_memcmp(md, wpa_s->sme.mobility_domain, 2) == 0) {
|
|
wpa_printf(MSG_DEBUG, "SME: Trying to use FT "
|
|
"over-the-air");
|
|
params.auth_alg = AUTH_ALG_FT;
|
|
params.ie = wpa_s->sme.ft_ies;
|
|
params.ie_len = wpa_s->sme.ft_ies_len;
|
|
}
|
|
}
|
|
#endif /* CONFIG_IEEE80211R */
|
|
|
|
#ifdef CONFIG_IEEE80211W
|
|
wpa_s->sme.mfp = ssid->ieee80211w;
|
|
if (ssid->ieee80211w != NO_MGMT_FRAME_PROTECTION && bss) {
|
|
const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
|
|
struct wpa_ie_data _ie;
|
|
if (rsn && wpa_parse_wpa_ie(rsn, 2 + rsn[1], &_ie) == 0 &&
|
|
_ie.capabilities &
|
|
(WPA_CAPABILITY_MFPC | WPA_CAPABILITY_MFPR)) {
|
|
wpa_printf(MSG_DEBUG, "WPA: Selected AP supports MFP: "
|
|
"require MFP");
|
|
wpa_s->sme.mfp = MGMT_FRAME_PROTECTION_REQUIRED;
|
|
}
|
|
}
|
|
#endif /* CONFIG_IEEE80211W */
|
|
|
|
wpa_supplicant_cancel_scan(wpa_s);
|
|
|
|
wpa_msg(wpa_s, MSG_INFO, "Trying to authenticate with " MACSTR
|
|
" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
|
|
wpa_ssid_txt(params.ssid, params.ssid_len), params.freq);
|
|
|
|
wpa_clear_keys(wpa_s, bss->bssid);
|
|
wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
|
|
old_ssid = wpa_s->current_ssid;
|
|
wpa_s->current_ssid = ssid;
|
|
wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
|
|
wpa_supplicant_initiate_eapol(wpa_s);
|
|
if (old_ssid != wpa_s->current_ssid)
|
|
wpas_notify_network_changed(wpa_s);
|
|
|
|
if (wpa_drv_authenticate(wpa_s, ¶ms) < 0) {
|
|
wpa_msg(wpa_s, MSG_INFO, "Authentication request to the "
|
|
"driver failed");
|
|
return;
|
|
}
|
|
|
|
/* TODO: add timeout on authentication */
|
|
|
|
/*
|
|
* Association will be started based on the authentication event from
|
|
* the driver.
|
|
*/
|
|
}
|
|
|
|
|
|
void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
|
|
{
|
|
struct wpa_driver_associate_params params;
|
|
struct wpa_ssid *ssid = wpa_s->current_ssid;
|
|
|
|
if (ssid == NULL) {
|
|
wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when "
|
|
"network is not selected");
|
|
return;
|
|
}
|
|
|
|
if (wpa_s->wpa_state != WPA_AUTHENTICATING) {
|
|
wpa_printf(MSG_DEBUG, "SME: Ignore authentication event when "
|
|
"not in authenticating state");
|
|
return;
|
|
}
|
|
|
|
if (os_memcmp(wpa_s->pending_bssid, data->auth.peer, ETH_ALEN) != 0) {
|
|
wpa_printf(MSG_DEBUG, "SME: Ignore authentication with "
|
|
"unexpected peer " MACSTR,
|
|
MAC2STR(data->auth.peer));
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "SME: Authentication response: peer=" MACSTR
|
|
" auth_type=%d status_code=%d",
|
|
MAC2STR(data->auth.peer), data->auth.auth_type,
|
|
data->auth.status_code);
|
|
wpa_hexdump(MSG_MSGDUMP, "SME: Authentication response IEs",
|
|
data->auth.ies, data->auth.ies_len);
|
|
|
|
if (data->auth.status_code != WLAN_STATUS_SUCCESS) {
|
|
wpa_printf(MSG_DEBUG, "SME: Authentication failed (status "
|
|
"code %d)", data->auth.status_code);
|
|
return;
|
|
}
|
|
|
|
#ifdef CONFIG_IEEE80211R
|
|
if (data->auth.auth_type == WLAN_AUTH_FT) {
|
|
union wpa_event_data edata;
|
|
os_memset(&edata, 0, sizeof(edata));
|
|
edata.ft_ies.ies = data->auth.ies;
|
|
edata.ft_ies.ies_len = data->auth.ies_len;
|
|
os_memcpy(edata.ft_ies.target_ap, data->auth.peer, ETH_ALEN);
|
|
wpa_supplicant_event(wpa_s, EVENT_FT_RESPONSE, &edata);
|
|
}
|
|
#endif /* CONFIG_IEEE80211R */
|
|
|
|
os_memset(¶ms, 0, sizeof(params));
|
|
params.bssid = data->auth.peer;
|
|
params.ssid = wpa_s->sme.ssid;
|
|
params.ssid_len = wpa_s->sme.ssid_len;
|
|
params.freq = wpa_s->sme.freq;
|
|
params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
|
|
wpa_s->sme.assoc_req_ie : NULL;
|
|
params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
|
|
#ifdef CONFIG_IEEE80211R
|
|
if (data->auth.auth_type == WLAN_AUTH_FT && wpa_s->sme.ft_ies) {
|
|
params.wpa_ie = wpa_s->sme.ft_ies;
|
|
params.wpa_ie_len = wpa_s->sme.ft_ies_len;
|
|
}
|
|
#endif /* CONFIG_IEEE80211R */
|
|
params.mode = ssid->mode;
|
|
params.mgmt_frame_protection = wpa_s->sme.mfp;
|
|
if (wpa_s->sme.prev_bssid_set)
|
|
params.prev_bssid = wpa_s->sme.prev_bssid;
|
|
|
|
wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
|
|
" (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
|
|
params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "",
|
|
params.freq);
|
|
|
|
wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
|
|
|
|
if (wpa_drv_associate(wpa_s, ¶ms) < 0) {
|
|
wpa_msg(wpa_s, MSG_INFO, "Association request to the driver "
|
|
"failed");
|
|
return;
|
|
}
|
|
|
|
/* TODO: add timeout on association */
|
|
}
|
|
|
|
|
|
int sme_update_ft_ies(struct wpa_supplicant *wpa_s, const u8 *md,
|
|
const u8 *ies, size_t ies_len)
|
|
{
|
|
if (md == NULL || ies == NULL) {
|
|
wpa_printf(MSG_DEBUG, "SME: Remove mobility domain");
|
|
os_free(wpa_s->sme.ft_ies);
|
|
wpa_s->sme.ft_ies = NULL;
|
|
wpa_s->sme.ft_ies_len = 0;
|
|
wpa_s->sme.ft_used = 0;
|
|
return 0;
|
|
}
|
|
|
|
os_memcpy(wpa_s->sme.mobility_domain, md, MOBILITY_DOMAIN_ID_LEN);
|
|
wpa_hexdump(MSG_DEBUG, "SME: FT IEs", ies, ies_len);
|
|
os_free(wpa_s->sme.ft_ies);
|
|
wpa_s->sme.ft_ies = os_malloc(ies_len);
|
|
if (wpa_s->sme.ft_ies == NULL)
|
|
return -1;
|
|
os_memcpy(wpa_s->sme.ft_ies, ies, ies_len);
|
|
wpa_s->sme.ft_ies_len = ies_len;
|
|
return 0;
|
|
}
|
|
|
|
|
|
void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
|
|
union wpa_event_data *data)
|
|
{
|
|
int bssid_changed;
|
|
int timeout = 5000;
|
|
|
|
wpa_printf(MSG_DEBUG, "SME: Association with " MACSTR " failed: "
|
|
"status code %d", MAC2STR(wpa_s->pending_bssid),
|
|
data->assoc_reject.status_code);
|
|
|
|
bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
|
|
|
|
/*
|
|
* For now, unconditionally terminate the previous authentication. In
|
|
* theory, this should not be needed, but mac80211 gets quite confused
|
|
* if the authentication is left pending.. Some roaming cases might
|
|
* benefit from using the previous authentication, so this could be
|
|
* optimized in the future.
|
|
*/
|
|
if (wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
|
|
WLAN_REASON_DEAUTH_LEAVING) < 0) {
|
|
wpa_msg(wpa_s, MSG_INFO,
|
|
"Deauth request to the driver failed");
|
|
}
|
|
wpa_s->sme.prev_bssid_set = 0;
|
|
|
|
if (wpa_blacklist_add(wpa_s, wpa_s->pending_bssid) == 0) {
|
|
struct wpa_blacklist *b;
|
|
b = wpa_blacklist_get(wpa_s, wpa_s->pending_bssid);
|
|
if (b && b->count < 3) {
|
|
/*
|
|
* Speed up next attempt if there could be other APs
|
|
* that could accept association.
|
|
*/
|
|
timeout = 100;
|
|
}
|
|
}
|
|
wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
|
|
os_memset(wpa_s->bssid, 0, ETH_ALEN);
|
|
os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
|
|
if (bssid_changed)
|
|
wpas_notify_bssid_changed(wpa_s);
|
|
|
|
/*
|
|
* TODO: if more than one possible AP is available in scan results,
|
|
* could try the other ones before requesting a new scan.
|
|
*/
|
|
wpa_supplicant_req_scan(wpa_s, timeout / 1000,
|
|
1000 * (timeout % 1000));
|
|
}
|
|
|
|
|
|
void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
|
|
union wpa_event_data *data)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "SME: Authentication timed out");
|
|
wpa_supplicant_req_scan(wpa_s, 5, 0);
|
|
}
|
|
|
|
|
|
void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
|
|
union wpa_event_data *data)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "SME: Association timed out");
|
|
wpa_supplicant_mark_disassoc(wpa_s);
|
|
wpa_supplicant_req_scan(wpa_s, 5, 0);
|
|
}
|
|
|
|
|
|
void sme_event_disassoc(struct wpa_supplicant *wpa_s,
|
|
union wpa_event_data *data)
|
|
{
|
|
wpa_printf(MSG_DEBUG, "SME: Disassociation event received");
|
|
if (!is_zero_ether_addr(wpa_s->bssid) &&
|
|
!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_USER_SPACE_MLME)) {
|
|
/*
|
|
* cfg80211/mac80211 can get into somewhat confused state if
|
|
* the AP only disassociates us and leaves us in authenticated
|
|
* state. For now, force the state to be cleared to avoid
|
|
* confusing errors if we try to associate with the AP again.
|
|
*/
|
|
wpa_printf(MSG_DEBUG, "SME: Deauthenticate to clear driver "
|
|
"state");
|
|
wpa_drv_deauthenticate(wpa_s, wpa_s->bssid,
|
|
WLAN_REASON_DEAUTH_LEAVING);
|
|
}
|
|
}
|