TDLS: Use more thorough validation of TPK handshake
This commit is contained in:
parent
f0bfbe2a6c
commit
1c0b2ad1ce
1 changed files with 114 additions and 67 deletions
|
@ -139,7 +139,7 @@ static int wpa_tdls_del_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
|
|||
{
|
||||
if (wpa_sm_set_key(sm, WPA_ALG_NONE, peer->addr,
|
||||
0, 0, NULL, 0, NULL, 0) < 0) {
|
||||
wpa_printf(MSG_WARNING, "TDLS: Failed to delete PTK-TK from "
|
||||
wpa_printf(MSG_WARNING, "TDLS: Failed to delete TPK-TK from "
|
||||
"the driver");
|
||||
return -1;
|
||||
}
|
||||
|
@ -793,6 +793,9 @@ skip_ftie:
|
|||
static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
|
||||
u8 tdls_action, u8 dialog_token, u16 status)
|
||||
{
|
||||
wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
|
||||
" (action=%u status=%u)",
|
||||
MAC2STR(dst), tdls_action, status);
|
||||
return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
|
||||
NULL, 0);
|
||||
}
|
||||
|
@ -1155,6 +1158,10 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
|
|||
#endif
|
||||
u8 dtoken;
|
||||
u16 ielen;
|
||||
u16 status = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||
|
||||
if (len < 3 + 3)
|
||||
return -1;
|
||||
|
||||
cpos = buf;
|
||||
cpos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
|
||||
|
@ -1168,47 +1175,54 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
|
|||
|
||||
ielen = len - (cpos - buf); /* start of IE in buf */
|
||||
if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) {
|
||||
wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M1");
|
||||
return -1;
|
||||
wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M1");
|
||||
return -1;
|
||||
wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
|
||||
"TPK M1");
|
||||
goto error;
|
||||
}
|
||||
wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M1",
|
||||
kde.lnkid, kde.lnkid_len);
|
||||
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");
|
||||
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
|
||||
dtoken, WLAN_STATUS_NOT_IN_SAME_BSS);
|
||||
return -1;
|
||||
status = WLAN_STATUS_NOT_IN_SAME_BSS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
wpa_printf(MSG_DEBUG, "TDLS: TPK M1 - TPK initiator " MACSTR,
|
||||
MAC2STR(src_addr));
|
||||
|
||||
if (!wpa_tdls_get_privacy(sm))
|
||||
if (!wpa_tdls_get_privacy(sm)) {
|
||||
if (kde.rsn_ie) {
|
||||
wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M1 while "
|
||||
"security is disabled");
|
||||
status = WLAN_STATUS_SECURITY_DISABLED;
|
||||
goto error;
|
||||
}
|
||||
goto skip_rsn;
|
||||
|
||||
if (kde.ftie == NULL) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M1");
|
||||
return -1;
|
||||
}
|
||||
ftie = (struct wpa_tdls_ftie *) kde.ftie;
|
||||
if (kde.rsn_ie == NULL) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M1");
|
||||
return -1;
|
||||
|
||||
if (kde.ftie == NULL || kde.rsn_ie == NULL) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M1");
|
||||
status = WLAN_STATUS_INVALID_PARAMETERS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
|
||||
wpa_printf(MSG_INFO, "TDLS: Too long Initiator RSN IE in "
|
||||
"TPK M1");
|
||||
return -1;
|
||||
status = WLAN_STATUS_INVALID_RSNIE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
|
||||
wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M1");
|
||||
return -1;
|
||||
status = WLAN_STATUS_INVALID_RSNIE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
cipher = ie.pairwise_cipher;
|
||||
|
@ -1217,10 +1231,32 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
|
|||
cipher = WPA_CIPHER_CCMP;
|
||||
} else {
|
||||
wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M1");
|
||||
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
|
||||
dtoken,
|
||||
WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID);
|
||||
return -1;
|
||||
status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((ie.capabilities &
|
||||
(WPA_CAPABILITY_NO_PAIRWISE | WPA_CAPABILITY_PEERKEY_ENABLED)) !=
|
||||
WPA_CAPABILITY_PEERKEY_ENABLED) {
|
||||
wpa_printf(MSG_INFO, "TDLS: Invalid RSN Capabilities in "
|
||||
"TPK M1");
|
||||
status = WLAN_STATUS_INVALID_RSN_IE_CAPAB;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Lifetime */
|
||||
if (kde.key_lifetime == NULL) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
|
||||
status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
|
||||
goto error;
|
||||
}
|
||||
timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
|
||||
lifetime = WPA_GET_LE32(timeoutie->value);
|
||||
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
|
||||
if (lifetime < 300) {
|
||||
wpa_printf(MSG_INFO, "TDLS: Too short TPK lifetime");
|
||||
status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
|
||||
goto error;
|
||||
}
|
||||
|
||||
skip_rsn:
|
||||
|
@ -1238,7 +1274,7 @@ skip_rsn:
|
|||
MAC2STR(src_addr));
|
||||
peer = os_malloc(sizeof(*peer));
|
||||
if (peer == NULL)
|
||||
return -1;
|
||||
goto error;
|
||||
os_memset(peer, 0, sizeof(*peer));
|
||||
os_memcpy(peer->addr, src_addr, ETH_ALEN);
|
||||
peer->next = sm->tdls;
|
||||
|
@ -1282,6 +1318,7 @@ skip_rsn:
|
|||
goto skip_rsn_check;
|
||||
}
|
||||
|
||||
ftie = (struct wpa_tdls_ftie *) kde.ftie;
|
||||
os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
|
||||
os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
|
||||
peer->rsnie_i_len = kde.rsn_ie_len;
|
||||
|
@ -1291,7 +1328,7 @@ skip_rsn:
|
|||
wpa_msg(sm->ctx->ctx, MSG_WARNING,
|
||||
"TDLS: Failed to get random data for responder nonce");
|
||||
wpa_tdls_peer_free(sm, peer);
|
||||
return -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
#if 0
|
||||
|
@ -1336,19 +1373,9 @@ skip_rsn:
|
|||
os_memcpy(peer->rsnie_p, peer->rsnie_i, peer->rsnie_i_len);
|
||||
peer->rsnie_p_len = peer->rsnie_i_len;
|
||||
|
||||
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for PTK handshake",
|
||||
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for TPK handshake",
|
||||
peer->rsnie_p, peer->rsnie_p_len);
|
||||
|
||||
/* Lifetime */
|
||||
if (kde.key_lifetime == NULL) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M1");
|
||||
return -1;
|
||||
}
|
||||
timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
|
||||
lifetime = WPA_GET_LE32(timeoutie->value);
|
||||
wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", lifetime);
|
||||
if (lifetime < 300)
|
||||
lifetime = 300; /* minimum seconds */
|
||||
peer->lifetime = lifetime;
|
||||
|
||||
wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
|
||||
|
@ -1358,6 +1385,11 @@ skip_rsn_check:
|
|||
wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken,
|
||||
status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1409,12 +1441,14 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
|
|||
status = WPA_GET_LE16(pos);
|
||||
pos += 2 /* status code */;
|
||||
|
||||
if (status != 0) {
|
||||
if (status != WLAN_STATUS_SUCCESS) {
|
||||
wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
|
||||
status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
status = WLAN_STATUS_UNSPECIFIED_FAILURE;
|
||||
|
||||
/* TODO: need to verify dialog token matches here or in kernel */
|
||||
dtoken = *pos++; /* dialog token */
|
||||
|
||||
|
@ -1427,13 +1461,13 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
|
|||
ielen = len - (pos - buf); /* start of IE in buf */
|
||||
if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) {
|
||||
wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2");
|
||||
return -1;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
|
||||
"TPK M2");
|
||||
return -1;
|
||||
goto error;
|
||||
}
|
||||
wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M2",
|
||||
kde.lnkid, kde.lnkid_len);
|
||||
|
@ -1441,9 +1475,8 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
|
|||
|
||||
if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
|
||||
wpa_printf(MSG_INFO, "TDLS: TPK M2 from different BSS");
|
||||
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
|
||||
dtoken, WLAN_STATUS_NOT_IN_SAME_BSS);
|
||||
return -1;
|
||||
status = WLAN_STATUS_NOT_IN_SAME_BSS;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (!wpa_tdls_get_privacy(sm)) {
|
||||
|
@ -1452,13 +1485,20 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
|
|||
goto skip_rsn;
|
||||
}
|
||||
|
||||
if (kde.rsn_ie == NULL) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M2");
|
||||
return -1;
|
||||
if (kde.ftie == NULL || kde.rsn_ie == NULL) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No FTIE or RSN IE in TPK M2");
|
||||
status = WLAN_STATUS_INVALID_PARAMETERS;
|
||||
goto error;
|
||||
}
|
||||
wpa_hexdump(MSG_DEBUG, "TDLS: RSNIE Received from TPK M2",
|
||||
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
|
||||
kde.rsn_ie, kde.rsn_ie_len);
|
||||
|
||||
/*
|
||||
* FIX: bitwise comparison of RSN IE is not the correct way of
|
||||
* validation this. It can be different, but certain fields must
|
||||
* match. Since we list only a single pairwise cipher in TPK M1, the
|
||||
* memcmp is likely to work in most cases, though.
|
||||
*/
|
||||
if (kde.rsn_ie_len != peer->rsnie_i_len ||
|
||||
os_memcmp(peer->rsnie_i, kde.rsn_ie, peer->rsnie_i_len) != 0) {
|
||||
wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M2 does "
|
||||
|
@ -1467,34 +1507,26 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
|
|||
peer->rsnie_i, peer->rsnie_i_len);
|
||||
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
|
||||
kde.rsn_ie, kde.rsn_ie_len);
|
||||
wpa_tdls_send_error(sm, src_addr,
|
||||
WLAN_TDLS_SETUP_CONFIRM, dtoken,
|
||||
WLAN_STATUS_UNSUPPORTED_RSN_IE_VERSION);
|
||||
|
||||
return -1;
|
||||
status = WLAN_STATUS_INVALID_RSNIE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (wpa_parse_wpa_ie_rsn(kde.rsn_ie, kde.rsn_ie_len, &ie) < 0) {
|
||||
wpa_printf(MSG_INFO, "TDLS: Failed to parse RSN IE in TPK M2");
|
||||
return -1;
|
||||
status = WLAN_STATUS_INVALID_RSNIE;
|
||||
goto error;
|
||||
}
|
||||
|
||||
cipher = ie.pairwise_cipher;
|
||||
if (cipher & WPA_CIPHER_CCMP) {
|
||||
if (cipher == WPA_CIPHER_CCMP) {
|
||||
wpa_printf(MSG_DEBUG, "TDLS: Using CCMP for direct link");
|
||||
cipher = WPA_CIPHER_CCMP;
|
||||
} else {
|
||||
wpa_printf(MSG_INFO, "TDLS: No acceptable cipher in TPK M2");
|
||||
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
|
||||
dtoken,
|
||||
WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID);
|
||||
return -1;
|
||||
status = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (kde.ftie == NULL) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M2");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M2",
|
||||
kde.ftie, sizeof(*ftie));
|
||||
ftie = (struct wpa_tdls_ftie *) kde.ftie;
|
||||
|
@ -1502,6 +1534,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
|
|||
if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
|
||||
wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M2 does "
|
||||
"not match with FTIE SNonce used in TPK M1");
|
||||
/* Silently discard the frame */
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -1514,7 +1547,8 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
|
|||
/* Lifetime */
|
||||
if (kde.key_lifetime == NULL) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M2");
|
||||
return -1;
|
||||
status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
|
||||
goto error;
|
||||
}
|
||||
timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
|
||||
lifetime = WPA_GET_LE32(timeoutie->value);
|
||||
|
@ -1523,9 +1557,8 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
|
|||
if (lifetime != peer->lifetime) {
|
||||
wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
|
||||
"TPK M2 (expected %u)", lifetime, peer->lifetime);
|
||||
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
|
||||
dtoken, WLAN_STATUS_UNACCEPTABLE_LIFETIME);
|
||||
return -1;
|
||||
status = WLAN_STATUS_UNACCEPTABLE_LIFETIME;
|
||||
goto error;
|
||||
}
|
||||
|
||||
wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
|
||||
|
@ -1533,6 +1566,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
|
|||
/* Process MIC check to see if TPK M2 is right */
|
||||
if (wpa_supplicant_verify_tdls_mic(2, peer, (u8 *) lnkid,
|
||||
(u8 *) timeoutie, ftie) < 0) {
|
||||
/* Discard the frame */
|
||||
wpa_tdls_del_key(sm, peer);
|
||||
wpa_tdls_peer_free(sm, peer);
|
||||
return -1;
|
||||
|
@ -1550,6 +1584,11 @@ skip_rsn:
|
|||
wpa_tdls_enable_link(sm, peer);
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken,
|
||||
status);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1579,6 +1618,8 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
|
|||
}
|
||||
wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
|
||||
|
||||
if (len < 3 + 3)
|
||||
return -1;
|
||||
pos = buf;
|
||||
pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
|
||||
|
||||
|
@ -1622,11 +1663,17 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
|
|||
ftie = (struct wpa_tdls_ftie *) kde.ftie;
|
||||
|
||||
if (kde.rsn_ie == NULL) {
|
||||
wpa_printf(MSG_INFO, "TDLS: No RSN IE KDE in TPK M3");
|
||||
wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
|
||||
return -1;
|
||||
}
|
||||
wpa_hexdump(MSG_DEBUG, "TDLS: RSNIE Received from TPK M3",
|
||||
wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
|
||||
kde.rsn_ie, kde.rsn_ie_len);
|
||||
if (kde.rsn_ie_len != peer->rsnie_p_len ||
|
||||
os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) {
|
||||
wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match "
|
||||
"with the one sent in TPK M2");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) {
|
||||
wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
|
||||
|
|
Loading…
Reference in a new issue