Browse Source

TDLS: Use more thorough validation of TPK handshake

Jouni Malinen 14 years ago
parent
commit
1c0b2ad1ce
1 changed files with 114 additions and 67 deletions
  1. 114 67
      src/rsn_supp/tdls.c

+ 114 - 67
src/rsn_supp/tdls.c

@@ -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 "