/* * hostapd - PeerKey for Direct Link Setup (DLS) * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * * This software may be distributed under the terms of the BSD license. * See README for more details. */ #include "utils/includes.h" #include "utils/common.h" #include "utils/eloop.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "crypto/random.h" #include "wpa_auth.h" #include "wpa_auth_i.h" #include "wpa_auth_ie.h" #ifdef CONFIG_PEERKEY static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) { #if 0 struct wpa_authenticator *wpa_auth = eloop_ctx; struct wpa_stsl_negotiation *neg = timeout_ctx; #endif /* TODO: ? */ } struct wpa_stsl_search { const u8 *addr; struct wpa_state_machine *sm; }; static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) { struct wpa_stsl_search *search = ctx; if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { search->sm = sm; return 1; } return 0; } static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, const u8 *peer, u16 mui, u16 error_type) { u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; u8 *pos; struct rsn_error_kde error; wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "Sending SMK Error"); pos = kde; if (peer) { pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); } error.mui = host_to_be16(mui); error.error_type = host_to_be16(error_type); pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, (u8 *) &error, sizeof(error), NULL, 0); __wpa_send_eapol(wpa_auth, sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, NULL, NULL, kde, pos - kde, 0, 0, 0); } void wpa_smk_m1(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; u8 *buf, *pos; size_t buf_len; if (wpa_parse_kde_ies((const u8 *) (key + 1), WPA_GET_BE16(key->key_data_length), &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); return; } if (kde.rsn_ie == NULL || kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN) { wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " "SMK M1"); return; } /* Initiator = sm->addr; Peer = kde.mac_addr */ search.addr = kde.mac_addr; search.sm = NULL; if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == 0 || search.sm == NULL) { wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR " aborted - STA not associated anymore", MAC2STR(kde.mac_addr)); wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, STK_ERR_STA_NR); /* FIX: wpa_stsl_remove(wpa_auth, neg); */ return; } buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; buf = os_malloc(buf_len); if (buf == NULL) return; /* Initiator RSN IE */ os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); pos = buf + kde.rsn_ie_len; /* Initiator MAC Address */ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, NULL, 0); /* SMK M2: * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) */ wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, "Sending SMK M2"); __wpa_send_eapol(wpa_auth, search.sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, NULL, key->key_nonce, buf, pos - buf, 0, 0, 0); os_free(buf); } static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key, struct wpa_eapol_ie_parse *kde, const u8 *smk) { u8 *buf, *pos; size_t buf_len; u32 lifetime; /* SMK M4: * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, * Lifetime KDE) */ buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); pos = buf = os_malloc(buf_len); if (buf == NULL) return; /* Initiator MAC Address */ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, NULL, 0); /* Initiator Nonce */ pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, NULL, 0); /* SMK with PNonce */ pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, key->key_nonce, WPA_NONCE_LEN); /* Lifetime */ lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, (u8 *) &lifetime, sizeof(lifetime), NULL, 0); wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Sending SMK M4"); __wpa_send_eapol(wpa_auth, sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, NULL, key->key_nonce, buf, pos - buf, 0, 1, 0); os_free(buf); } static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key, struct wpa_eapol_ie_parse *kde, const u8 *smk, const u8 *peer) { u8 *buf, *pos; size_t buf_len; u32 lifetime; /* SMK M5: * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, * Lifetime KDE)) */ buf_len = kde->rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); pos = buf = os_malloc(buf_len); if (buf == NULL) return; /* Peer RSN IE */ os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len); pos = buf + kde->rsn_ie_len; /* Peer MAC Address */ pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); /* PNonce */ pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, WPA_NONCE_LEN, NULL, 0); /* SMK and INonce */ pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, kde->nonce, WPA_NONCE_LEN); /* Lifetime */ lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, (u8 *) &lifetime, sizeof(lifetime), NULL, 0); wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, "Sending SMK M5"); __wpa_send_eapol(wpa_auth, sm, WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SMK_MESSAGE, NULL, kde->nonce, buf, pos - buf, 0, 1, 0); os_free(buf); } void wpa_smk_m3(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; if (wpa_parse_kde_ies((const u8 *) (key + 1), WPA_GET_BE16(key->key_data_length), &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); return; } if (kde.rsn_ie == NULL || kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " "Nonce KDE in SMK M3"); return; } /* Peer = sm->addr; Initiator = kde.mac_addr; * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ search.addr = kde.mac_addr; search.sm = NULL; if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == 0 || search.sm == NULL) { wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR " aborted - STA not associated anymore", MAC2STR(kde.mac_addr)); wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, STK_ERR_STA_NR); /* FIX: wpa_stsl_remove(wpa_auth, neg); */ return; } if (random_get_bytes(smk, PMK_LEN)) { wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); return; } /* SMK = PRF-256(Random number, "SMK Derivation", * AA || Time || INonce || PNonce) */ os_memcpy(buf, wpa_auth->addr, ETH_ALEN); pos = buf + ETH_ALEN; wpa_get_ntp_timestamp(pos); pos += 8; os_memcpy(pos, kde.nonce, WPA_NONCE_LEN); pos += WPA_NONCE_LEN; os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN); #ifdef CONFIG_IEEE80211W sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), smk, PMK_LEN); #else /* CONFIG_IEEE80211W */ sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), smk, PMK_LEN); #endif /* CONFIG_IEEE80211W */ wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN); wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); /* Authenticator does not need SMK anymore and it is required to forget * it. */ os_memset(smk, 0, sizeof(*smk)); } void wpa_smk_error(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *sm, struct wpa_eapol_key *key) { struct wpa_eapol_ie_parse kde; struct wpa_stsl_search search; struct rsn_error_kde error; u16 mui, error_type; if (wpa_parse_kde_ies((const u8 *) (key + 1), WPA_GET_BE16(key->key_data_length), &kde) < 0) { wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); return; } if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || kde.error == NULL || kde.error_len < sizeof(error)) { wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " "SMK Error"); return; } search.addr = kde.mac_addr; search.sm = NULL; if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == 0 || search.sm == NULL) { wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " "associated for SMK Error message from " MACSTR, MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); return; } os_memcpy(&error, kde.error, sizeof(error)); mui = be_to_host16(error.mui); error_type = be_to_host16(error.error_type); wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, "STA reported SMK Error: Peer " MACSTR " MUI %d Error Type %d", MAC2STR(kde.mac_addr), mui, error_type); wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); } int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, struct wpa_stsl_negotiation *neg) { struct wpa_stsl_negotiation *pos, *prev; if (wpa_auth == NULL) return -1; pos = wpa_auth->stsl_negotiations; prev = NULL; while (pos) { if (pos == neg) { if (prev) prev->next = pos->next; else wpa_auth->stsl_negotiations = pos->next; eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); os_free(pos); return 0; } prev = pos; pos = pos->next; } return -1; } #endif /* CONFIG_PEERKEY */