#include "eapol_sm.h"
#include "wpa.h"
#include "sha1.h"
+#include "sha256.h"
#include "rc4.h"
#include "aes_wrap.h"
#include "crypto.h"
static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
struct wpa_group *group);
+static void wpa_request_new_ptk(struct wpa_state_machine *sm);
-/* Default timeouts are 100 ms, but this seems to be a bit too fast for most
- * WPA Supplicants, so use a bit longer timeout. */
-static const u32 dot11RSNAConfigGroupUpdateTimeOut = 1000; /* ms */
-static const u32 dot11RSNAConfigGroupUpdateCount = 3;
-static const u32 dot11RSNAConfigPairwiseUpdateTimeOut = 1000; /* ms */
-static const u32 dot11RSNAConfigPairwiseUpdateCount = 3;
+static const u32 dot11RSNAConfigGroupUpdateCount = 4;
+static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
+static const u32 eapol_key_timeout_first = 100; /* ms */
+static const u32 eapol_key_timeout_subseq = 1000; /* ms */
/* TODO: make these configurable */
static const int dot11RSNAConfigPMKLifetime = 43200;
}
+int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
+ int (*cb)(struct wpa_authenticator *a, void *ctx),
+ void *cb_ctx)
+{
+ if (wpa_auth->cb.for_each_auth == NULL)
+ return 0;
+ return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx);
+}
+
+
void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr,
logger_level level, const char *txt)
{
static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
{
+ int ret = 0;
#ifdef CONFIG_IEEE80211R
- return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_PSK;
-#else /* CONFIG_IEEE80211R */
- return 0;
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
+ ret = 1;
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_IEEE80211W
+ if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
+ ret = 1;
+#endif /* CONFIG_IEEE80211W */
+ return ret;
}
}
+static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
+{
+ struct wpa_authenticator *wpa_auth = eloop_ctx;
+ struct wpa_state_machine *sm = timeout_ctx;
+
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK");
+ wpa_request_new_ptk(sm);
+ wpa_sm_step(sm);
+}
+
+
static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
{
if (sm->pmksa == ctx)
* wpa_init - Initialize WPA authenticator
* @addr: Authenticator address
* @conf: Configuration for WPA authenticator
+ * @cb: Callback functions for WPA authenticator
* Returns: Pointer to WPA authenticator data or %NULL on failure
*/
struct wpa_authenticator * wpa_init(const u8 *addr,
return 0;
os_memcpy(&wpa_auth->conf, conf, sizeof(*conf));
- /*
- * TODO:
- * Disassociate stations if configuration changed
- * Update WPA/RSN IE
- */
+ if (wpa_auth_gen_wpa_ie(wpa_auth)) {
+ wpa_printf(MSG_ERROR, "Could not generate WPA IE.");
+ return -1;
+ }
+
return 0;
}
#endif /* CONFIG_IEEE80211R */
if (sm->started) {
- os_memset(sm->key_replay_counter, 0, WPA_REPLAY_COUNTER_LEN);
+ os_memset(&sm->key_replay, 0, sizeof(sm->key_replay));
sm->ReAuthenticationRequest = TRUE;
wpa_sm_step(sm);
return;
}
+void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
+{
+ /* WPA/RSN was not used - clear WPA state. This is needed if the STA
+ * reassociates back to the same AP while the previous entry for the
+ * STA has not yet been removed. */
+ if (sm == NULL)
+ return;
+
+ sm->wpa_key_mgmt = 0;
+}
+
+
static void wpa_free_sta_sm(struct wpa_state_machine *sm)
{
os_free(sm->last_rx_eapol_key);
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
eloop_cancel_timeout(wpa_sm_call_step, sm, NULL);
+ eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
if (sm->in_step_loop) {
/* Must not free state machine while wpa_sm_step() is running.
* Freeing will be completed in the end of wpa_sm_step(). */
}
+static int wpa_replay_counter_valid(struct wpa_state_machine *sm,
+ const u8 *replay_counter)
+{
+ int i;
+ for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+ if (!sm->key_replay[i].valid)
+ break;
+ if (os_memcmp(replay_counter, sm->key_replay[i].counter,
+ WPA_REPLAY_COUNTER_LEN) == 0)
+ return 1;
+ }
+ return 0;
+}
+
+
void wpa_receive(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
u8 *data, size_t data_len)
}
if (!(key_info & WPA_KEY_INFO_REQUEST) &&
- (!sm->key_replay_counter_valid ||
- os_memcmp(key->replay_counter, sm->key_replay_counter,
- WPA_REPLAY_COUNTER_LEN) != 0)) {
+ !wpa_replay_counter_valid(sm, key->replay_counter)) {
+ int i;
wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key %s with unexpected "
"replay counter", msgtxt);
- wpa_hexdump(MSG_DEBUG, "expected replay counter",
- sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN);
+ for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) {
+ if (!sm->key_replay[i].valid)
+ break;
+ wpa_hexdump(MSG_DEBUG, "pending replay counter",
+ sm->key_replay[i].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ }
wpa_hexdump(MSG_DEBUG, "received replay counter",
key->replay_counter, WPA_REPLAY_COUNTER_LEN);
return;
wpa_rekey_gtk(wpa_auth, NULL);
}
} else {
- /* Do not allow the same key replay counter to be reused. */
- sm->key_replay_counter_valid = FALSE;
+ /* Do not allow the same key replay counter to be reused. This
+ * does also invalidate all other pending replay counters if
+ * retransmissions were used, i.e., we will only process one of
+ * the pending replies and ignore rest if more than one is
+ * received. */
+ sm->key_replay[0].valid = FALSE;
}
#ifdef CONFIG_PEERKEY
os_memcpy(data, addr, ETH_ALEN);
os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
+#ifdef CONFIG_IEEE80211W
+ sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion",
+ data, sizeof(data), gtk, gtk_len);
+#else /* CONFIG_IEEE80211W */
sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion",
data, sizeof(data), gtk, gtk_len);
+#endif /* CONFIG_IEEE80211W */
wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN);
wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len);
int key_data_len, pad_len = 0;
u8 *buf, *pos;
int version, pairwise;
+ int i;
len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key);
WPA_PUT_BE16(key->key_length, 0);
/* FIX: STSL: what to use as key_replay_counter? */
- inc_byte_array(sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN);
- os_memcpy(key->replay_counter, sm->key_replay_counter,
+ for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) {
+ sm->key_replay[i].valid = sm->key_replay[i - 1].valid;
+ os_memcpy(sm->key_replay[i].counter,
+ sm->key_replay[i - 1].counter,
+ WPA_REPLAY_COUNTER_LEN);
+ }
+ inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN);
+ os_memcpy(key->replay_counter, sm->key_replay[0].counter,
WPA_REPLAY_COUNTER_LEN);
- sm->key_replay_counter_valid = TRUE;
+ sm->key_replay[0].valid = TRUE;
if (nonce)
os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN);
{
int timeout_ms;
int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
+ int ctr;
if (sm == NULL)
return;
__wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len,
keyidx, encr, 0);
- timeout_ms = pairwise ? dot11RSNAConfigPairwiseUpdateTimeOut :
- dot11RSNAConfigGroupUpdateTimeOut;
+ ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr;
+ if (ctr == 1)
+ timeout_ms = eapol_key_timeout_first;
+ else
+ timeout_ms = eapol_key_timeout_subseq;
eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
wpa_send_eapol_timeout, wpa_auth, sm);
}
os_memset(&sm->PTK, 0, sizeof(sm->PTK));
wpa_auth_set_key(sm->wpa_auth, 0, "none", sm->addr, 0, (u8 *) "", 0);
sm->pairwise_set = FALSE;
+ eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
}
void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
{
+ int remove_ptk = 1;
+
if (sm == NULL)
return;
break;
case WPA_REAUTH:
case WPA_REAUTH_EAPOL:
+ if (sm->GUpdateStationKeys) {
+ /*
+ * Reauthentication cancels the pending group key
+ * update for this STA.
+ */
+ sm->group->GKeyDoneStations--;
+ sm->GUpdateStationKeys = FALSE;
+ sm->PtkGroupInit = TRUE;
+ }
sm->ReAuthenticationRequest = TRUE;
break;
case WPA_ASSOC_FT:
sm->ft_completed = 0;
#endif /* CONFIG_IEEE80211R */
- sm->PTK_valid = FALSE;
- os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+#ifdef CONFIG_IEEE80211W
+ if (sm->mgmt_frame_prot && event == WPA_AUTH)
+ remove_ptk = 0;
+#endif /* CONFIG_IEEE80211W */
- if (event != WPA_REAUTH_EAPOL)
- wpa_remove_ptk(sm);
+ if (remove_ptk) {
+ sm->PTK_valid = FALSE;
+ os_memset(&sm->PTK, 0, sizeof(sm->PTK));
+
+ if (event != WPA_REAUTH_EAPOL)
+ wpa_remove_ptk(sm);
+ }
wpa_sm_step(sm);
}
wpa_remove_ptk(sm);
wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0);
sm->TimeoutCtr = 0;
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_authorized, 0);
}
SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
sm->PTKRequest = FALSE;
sm->TimeoutEvt = FALSE;
+
+ sm->TimeoutCtr++;
+ if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+ }
+
wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
"sending 1/4 msg of 4-Way Handshake");
/*
* one possible PSK for this STA.
*/
if (sm->wpa == WPA_VERSION_WPA2 &&
- sm->wpa_key_mgmt != WPA_KEY_MGMT_PSK) {
+ wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) {
pmkid = buf;
pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
* available with pre-calculated PMKID.
*/
rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr,
- sm->addr, &pmkid[2 + RSN_SELECTOR_LEN]);
+ sm->addr, &pmkid[2 + RSN_SELECTOR_LEN],
+ wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
}
}
wpa_send_eapol(sm->wpa_auth, sm,
WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL,
sm->ANonce, pmkid, pmkid_len, 0, 0);
- sm->TimeoutCtr++;
}
struct wpa_ptk *ptk)
{
#ifdef CONFIG_IEEE80211R
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_PSK)
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
#endif /* CONFIG_IEEE80211R */
wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce,
- (u8 *) ptk, sizeof(*ptk));
+ (u8 *) ptk, sizeof(*ptk),
+ wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
return 0;
}
* WPA-PSK: iterate through possible PSKs and select the one matching
* the packet */
for (;;) {
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk);
if (pmk == NULL)
break;
break;
}
- if (sm->wpa_key_mgmt != WPA_KEY_MGMT_PSK &&
- sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK)
+ if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
break;
}
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
/* PSK may have changed from the previous choice, so update
* state machine data based on whatever PSK was selected here.
*/
SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
sm->TimeoutEvt = FALSE;
+
+ sm->TimeoutCtr++;
+ if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+ }
+
/* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, GTK[GN])
*/
os_memset(rsc, 0, WPA_KEY_RSC_LEN);
WPA_KEY_INFO_KEY_TYPE,
_rsc, sm->ANonce, kde, pos - kde, keyidx, encr);
os_free(kde);
- sm->TimeoutCtr++;
}
/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
sm->pairwise_set = TRUE;
- if (sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_PSK) {
+ if (sm->wpa_auth->conf.wpa_ptk_rekey) {
+ eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+ eloop_register_timeout(sm->wpa_auth->conf.
+ wpa_ptk_rekey, 0, wpa_rekey_ptk,
+ sm->wpa_auth, sm);
+ }
+
+ if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
wpa_auth_set_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_authorized, 1);
}
SM_ENTER(WPA_PTK, AUTHENTICATION2);
break;
case WPA_PTK_AUTHENTICATION2:
- if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) &&
+ if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
wpa_auth_get_eapol(sm->wpa_auth, sm->addr,
WPA_EAPOL_keyRun) > 0)
SM_ENTER(WPA_PTK, INITPMK);
- else if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_PSK ||
- sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_PSK)
+ else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)
/* FIX: && 802.1X::keyRun */)
SM_ENTER(WPA_PTK, INITPSK);
break;
size_t kde_len;
SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group);
+
+ sm->GTimeoutCtr++;
+ if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) {
+ /* No point in sending the EAPOL-Key - we will disconnect
+ * immediately following this. */
+ return;
+ }
+
if (sm->wpa == WPA_VERSION_WPA)
sm->PInitAKeys = FALSE;
sm->TimeoutEvt = FALSE;
rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1);
if (sm->wpa == WPA_VERSION_WPA2)
os_free(kde);
- sm->GTimeoutCtr++;
}
SM_STEP(WPA_PTK_GROUP)
{
- if (sm->Init)
+ if (sm->Init || sm->PtkGroupInit) {
SM_ENTER(WPA_PTK_GROUP, IDLE);
- else switch (sm->wpa_ptk_group_state) {
+ sm->PtkGroupInit = FALSE;
+ } else switch (sm->wpa_ptk_group_state) {
case WPA_PTK_GROUP_IDLE:
if (sm->GUpdateStationKeys ||
(sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys))
"Not in PTKINITDONE; skip Group Key update");
return 0;
}
- sm->group->GKeyDoneStations++;
- sm->GUpdateStationKeys = TRUE;
+ if (sm->GUpdateStationKeys) {
+ /*
+ * This should not really happen, but just in case, make sure
+ * we do not count the same STA twice in GKeyDoneStations.
+ */
+ wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
+ "GUpdateStationKeys already set - do not "
+ "increment GKeyDoneStations");
+ } else {
+ sm->group->GKeyDoneStations++;
+ sm->GUpdateStationKeys = TRUE;
+ }
wpa_sm_step(sm);
return 0;
}
}
+int wpa_auth_get_pairwise(struct wpa_state_machine *sm)
+{
+ return sm->pairwise;
+}
+
+
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
{
if (sm == NULL)
if (pmksa_cache_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
sm->wpa_auth->addr, sm->addr, session_timeout,
- eapol))
+ eapol, sm->wpa_key_mgmt))
return 0;
return -1;
return -1;
if (pmksa_cache_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr,
- sta_addr, session_timeout, eapol))
+ sta_addr, session_timeout, eapol,
+ WPA_KEY_MGMT_IEEE8021X))
return 0;
return -1;