wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
peer->rsnie_p, timeoutie, (u8 *) ftie,
mic);
- if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
"dropping packet");
wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
if (peer->tpk_set) {
wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
dtoken, lnkid, (u8 *) ftie, mic);
- if (os_memcmp(mic, ftie->mic, 16) != 0) {
+ if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
"dropping packet");
return -1;
}
-static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm,
+ struct wpa_tdls_peer *peer)
+{
+ struct wpa_tdls_peer *cur, *prev;
+
+ cur = sm->tdls;
+ prev = NULL;
+ while (cur && cur != peer) {
+ prev = cur;
+ cur = cur->next;
+ }
+
+ if (cur != peer) {
+ wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR
+ " to remove it from the list",
+ MAC2STR(peer->addr));
+ return;
+ }
+
+ if (prev)
+ prev->next = peer->next;
+ else
+ sm->tdls = peer->next;
+}
+
+
+static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
{
wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
MAC2STR(peer->addr));
}
+static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+ wpa_tdls_peer_clear(sm, peer);
+ wpa_tdls_peer_remove_from_list(sm, peer);
+ os_free(peer);
+}
+
+
static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
struct wpa_tdls_lnkid *lnkid)
{
struct wpa_tdls_peer *peer,
u8 dialog_token)
{
+ size_t buf_len = 0;
+ struct wpa_tdls_timeoutie timeoutie;
+ u16 rsn_capab;
+ u8 *rbuf, *pos, *count_pos;
+ u16 count;
+ struct rsn_ie_hdr *hdr;
+ int status;
+
wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
"(peer " MACSTR ")", MAC2STR(peer->addr));
+ if (!wpa_tdls_get_privacy(sm))
+ goto skip_rsn_ies;
- return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
- dialog_token, 0, 0, NULL, 0);
+ /* Filling RSN IE */
+ hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
+ hdr->elem_id = WLAN_EID_RSN;
+ WPA_PUT_LE16(hdr->version, RSN_VERSION);
+ pos = (u8 *) (hdr + 1);
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+ pos += RSN_SELECTOR_LEN;
+ count_pos = pos;
+ pos += 2;
+ count = 0;
+
+ /*
+ * AES-CCMP is the default encryption preferred for TDLS, so
+ * RSN IE is filled only with CCMP cipher suite.
+ * Note: TKIP is not used to encrypt TDLS link.
+ *
+ * Regardless of the cipher used on the AP connection, select CCMP
+ * here.
+ */
+ RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+ pos += RSN_SELECTOR_LEN;
+ count++;
+ WPA_PUT_LE16(count_pos, count);
+ WPA_PUT_LE16(pos, 1);
+ pos += 2;
+ RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+ pos += RSN_SELECTOR_LEN;
+
+ rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+ rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+ WPA_PUT_LE16(pos, rsn_capab);
+ pos += 2;
+ hdr->len = (pos - (u8 *) hdr) - 2;
+ peer->rsnie_i_len = pos - peer->rsnie_i;
+
+ wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response",
+ (u8 *) hdr, hdr->len + 2);
+skip_rsn_ies:
+ buf_len = 0;
+ if (wpa_tdls_get_privacy(sm)) {
+ /* Peer RSN IE, Lifetime */
+ buf_len += peer->rsnie_i_len +
+ sizeof(struct wpa_tdls_timeoutie);
+ }
+ rbuf = os_zalloc(buf_len + 1);
+ if (rbuf == NULL) {
+ wpa_tdls_peer_free(sm, peer);
+ return -1;
+ }
+ pos = rbuf;
+
+ if (!wpa_tdls_get_privacy(sm))
+ goto skip_ies;
+ /* Initiator RSN IE */
+ pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
+ /* Lifetime */
+ peer->lifetime = TPK_LIFETIME;
+ pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+ sizeof(timeoutie), peer->lifetime);
+ wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
+skip_ies:
+ status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
+ dialog_token, 0, 0, rbuf, pos - rbuf);
+ os_free(rbuf);
+
+ return status;
}
dialog_token = buf[sizeof(struct wpa_tdls_frame)];
+ /*
+ * Some APs will tack on a weird IE to the end of a TDLS
+ * discovery request packet. This needn't fail the response,
+ * since the required IE are verified separately.
+ */
if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
len - (sizeof(struct wpa_tdls_frame) + 1),
- &kde) < 0)
- return -1;
+ &kde) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
+ }
if (!kde.lnkid) {
wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
}
+static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+ int add)
+{
+ return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid,
+ peer->capability,
+ peer->supp_rates, peer->supp_rates_len,
+ peer->ht_capabilities,
+ peer->vht_capabilities,
+ peer->qos_info, peer->ext_capab,
+ peer->ext_capab_len,
+ peer->supp_channels,
+ peer->supp_channels_len,
+ peer->supp_oper_classes,
+ peer->supp_oper_classes_len);
+}
+
+
static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
const u8 *buf, size_t len)
{
wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
"direct link is enabled - tear down the "
"old link first");
- wpa_tdls_disable_peer_link(sm, peer);
- }
-
- /*
- * An entry is already present, so check if we already sent a
- * TDLS Setup Request. If so, compare MAC addresses and let the
- * STA with the lower MAC address continue as the initiator.
- * The other negotiation is terminated.
- */
- if (peer->initiator) {
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+ wpa_tdls_peer_clear(sm, peer);
+ } else if (peer->initiator) {
+ /*
+ * An entry is already present, so check if we already
+ * sent a TDLS Setup Request. If so, compare MAC
+ * addresses and let the STA with the lower MAC address
+ * continue as the initiator. The other negotiation is
+ * terminated.
+ */
if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
wpa_printf(MSG_DEBUG, "TDLS: Discard request "
"from peer with higher address "
MACSTR " (terminate previously "
"initiated negotiation",
MAC2STR(src_addr));
- wpa_tdls_disable_peer_link(sm, peer);
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
+ peer->addr);
+ wpa_tdls_peer_clear(sm, peer);
}
}
}
lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS");
- status = WLAN_STATUS_NOT_IN_SAME_BSS;
+ status = WLAN_STATUS_REQUEST_DECLINED;
goto error;
}
if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
wpa_msg(sm->ctx->ctx, MSG_WARNING,
"TDLS: Failed to get random data for responder nonce");
- wpa_tdls_peer_free(sm, peer);
goto error;
}
}
wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
skip_rsn_check:
- /* add the peer to the driver as a "setup in progress" peer */
- wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0,
- NULL, 0, NULL, 0, NULL, 0);
+ /* add supported rates, capabilities, and qos_info to the TDLS peer */
+ if (wpa_tdls_addset_peer(sm, peer, 1) < 0)
+ goto error;
+
peer->tpk_in_progress = 1;
wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
- wpa_tdls_disable_peer_link(sm, peer);
+ wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
goto error;
}
error:
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken,
status);
+ if (peer)
+ wpa_tdls_peer_free(sm, peer);
return -1;
}
#endif /* CONFIG_TDLS_TESTING */
}
- /* add supported rates, capabilities, and qos_info to the TDLS peer */
- if (wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->aid,
- peer->capability,
- peer->supp_rates, peer->supp_rates_len,
- peer->ht_capabilities,
- peer->vht_capabilities,
- peer->qos_info, peer->ext_capab,
- peer->ext_capab_len,
- peer->supp_channels,
- peer->supp_channels_len,
- peer->supp_oper_classes,
- peer->supp_oper_classes_len) < 0)
- return -1;
-
if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
"driver");
skip_rsn:
peer->dtoken = dtoken;
+ /* add supported rates, capabilities, and qos_info to the TDLS peer */
+ if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
+ goto error;
+
wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
"TPK Handshake Message 3");
- if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0) {
- wpa_tdls_disable_peer_link(sm, peer);
- return -1;
- }
+ if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
+ goto error;
if (!peer->tpk_success) {
/*
pos += 2 /* status code */ + 1 /* dialog token */;
ielen = len - (pos - buf); /* start of IE in buf */
+
+ /*
+ * Don't reject the message if failing to parse IEs. The IEs we need are
+ * explicitly checked below. Some APs piggy-back broken IEs to the end
+ * of a TDLS Confirm packet, which will fail the link if we don't ignore
+ * this error.
+ */
if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
- wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3");
- goto error;
+ wpa_printf(MSG_DEBUG,
+ "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
}
if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
}
skip_rsn:
+ /* add supported rates, capabilities, and qos_info to the TDLS peer */
+ if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
+ goto error;
+
if (!peer->tpk_success) {
/*
* Enable Link only when tpk_success is 0, signifying that this
ret = wpa_tdls_enable_link(sm, peer);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
- wpa_tdls_do_teardown(
- sm, peer,
- WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+ goto error;
}
}
return ret;
error:
- wpa_tdls_disable_peer_link(sm, peer);
+ wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
return -1;
}
peer->initiator = 1;
/* add the peer to the driver as a "setup in progress" peer */
- wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0,
- NULL, 0, NULL, 0, NULL, 0);
+ if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
+ NULL, 0, NULL, 0, NULL, 0, NULL, 0)) {
+ wpa_tdls_disable_peer_link(sm, peer);
+ return -1;
+ }
peer->tpk_in_progress = 1;
void wpa_tdls_teardown_peers(struct wpa_sm *sm)
{
- struct wpa_tdls_peer *peer;
+ struct wpa_tdls_peer *peer, *tmp;
peer = sm->tdls;
wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
while (peer) {
+ tmp = peer->next;
wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
MAC2STR(peer->addr));
if (sm->tdls_external_setup)
else
wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
- peer = peer->next;
+ peer = tmp;
}
}
struct wpa_tdls_peer *peer, *tmp;
peer = sm->tdls;
- sm->tdls = NULL;
while (peer) {
int res;
wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
MAC2STR(peer->addr), res);
wpa_tdls_peer_free(sm, peer);
- os_free(peer);
peer = tmp;
}
}