svn commit: r305785 - head/sys/net80211

Andriy Voskoboinyk avos at FreeBSD.org
Tue Sep 13 22:59:39 UTC 2016


Author: avos
Date: Tue Sep 13 22:59:38 2016
New Revision: 305785
URL: https://svnweb.freebsd.org/changeset/base/305785

Log:
  net80211: improve error checking in ieee80211_parse_{wpa,rsn}()
  
  - Add few checks for group/pairwise ciphers into
  ieee80211_parse_{wpa,rsn}().
  - Split error code and cipher value in wpa_cipher() / rsn_cipher(); current
  hack with (1 << 32) does not work - it's 1, not 0 (detected by CSA).
  - Return IEEE80211_REASON_UNSUPP_RSN_IE_VERSION instead of
  IEEE80211_REASON_IE_INVALID when version field is not equal to RSN_VERSION.
  
  Tested with wpi(4) / urtwn(4) (HOSTAP mode).
  
  Reviewed by:	adrian
  Differential Revision:	https://reviews.freebsd.org/D7887

Modified:
  head/sys/net80211/ieee80211_hostap.c

Modified: head/sys/net80211/ieee80211_hostap.c
==============================================================================
--- head/sys/net80211/ieee80211_hostap.c	Tue Sep 13 22:56:21 2016	(r305784)
+++ head/sys/net80211/ieee80211_hostap.c	Tue Sep 13 22:59:38 2016	(r305785)
@@ -1153,28 +1153,36 @@ bad:
  * record any key length.
  */
 static int
-wpa_cipher(const uint8_t *sel, uint8_t *keylen)
+wpa_cipher(const uint8_t *sel, uint8_t *keylen, uint8_t *cipher)
 {
 #define	WPA_SEL(x)	(((x)<<24)|WPA_OUI)
 	uint32_t w = le32dec(sel);
 
 	switch (w) {
 	case WPA_SEL(WPA_CSE_NULL):
-		return IEEE80211_CIPHER_NONE;
+		*cipher = IEEE80211_CIPHER_NONE;
+		break;
 	case WPA_SEL(WPA_CSE_WEP40):
 		if (keylen)
 			*keylen = 40 / NBBY;
-		return IEEE80211_CIPHER_WEP;
+		*cipher = IEEE80211_CIPHER_WEP;
+		break;
 	case WPA_SEL(WPA_CSE_WEP104):
 		if (keylen)
 			*keylen = 104 / NBBY;
-		return IEEE80211_CIPHER_WEP;
+		*cipher = IEEE80211_CIPHER_WEP;
+		break;
 	case WPA_SEL(WPA_CSE_TKIP):
-		return IEEE80211_CIPHER_TKIP;
+		*cipher = IEEE80211_CIPHER_TKIP;
+		break;
 	case WPA_SEL(WPA_CSE_CCMP):
-		return IEEE80211_CIPHER_AES_CCM;
+		*cipher = IEEE80211_CIPHER_AES_CCM;
+		break;
+	default:
+		return (EINVAL);
 	}
-	return 32;		/* NB: so 1<< is discarded */
+
+	return (0);
 #undef WPA_SEL
 }
 
@@ -1212,7 +1220,7 @@ ieee80211_parse_wpa(struct ieee80211vap 
 {
 	uint8_t len = frm[1];
 	uint32_t w;
-	int n;
+	int error, n;
 
 	/*
 	 * Check the length once for fixed parts: OUI, type,
@@ -1245,7 +1253,14 @@ ieee80211_parse_wpa(struct ieee80211vap 
 	memset(rsn, 0, sizeof(*rsn));
 
 	/* multicast/group cipher */
-	rsn->rsn_mcastcipher = wpa_cipher(frm, &rsn->rsn_mcastkeylen);
+	error = wpa_cipher(frm, &rsn->rsn_mcastkeylen, &rsn->rsn_mcastcipher);
+	if (error != 0) {
+		IEEE80211_DISCARD_IE(vap,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "unknown mcast cipher suite %08X",
+		    le32dec(frm));
+		return IEEE80211_REASON_GROUP_CIPHER_INVALID;
+	}
 	frm += 4, len -= 4;
 
 	/* unicast ciphers */
@@ -1260,13 +1275,26 @@ ieee80211_parse_wpa(struct ieee80211vap 
 	}
 	w = 0;
 	for (; n > 0; n--) {
-		w |= 1<<wpa_cipher(frm, &rsn->rsn_ucastkeylen);
+		uint8_t cipher;
+
+		error = wpa_cipher(frm, &rsn->rsn_ucastkeylen, &cipher);
+		if (error == 0)
+			w |= 1 << cipher;
+
 		frm += 4, len -= 4;
 	}
-	if (w & (1<<IEEE80211_CIPHER_TKIP))
-		rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
-	else
+	if (w == 0) {
+		IEEE80211_DISCARD_IE(vap,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "WPA", "no usable pairwise cipher suite found (w=%d)",
+		    w);
+		return IEEE80211_REASON_PAIRWISE_CIPHER_INVALID;
+	}
+	/* XXX other? */
+	if (w & (1 << IEEE80211_CIPHER_AES_CCM))
 		rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+	else
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
 
 	/* key management algorithms */
 	n = le16dec(frm);
@@ -1300,30 +1328,39 @@ ieee80211_parse_wpa(struct ieee80211vap 
  * record any key length.
  */
 static int
-rsn_cipher(const uint8_t *sel, uint8_t *keylen)
+rsn_cipher(const uint8_t *sel, uint8_t *keylen, uint8_t *cipher)
 {
 #define	RSN_SEL(x)	(((x)<<24)|RSN_OUI)
 	uint32_t w = le32dec(sel);
 
 	switch (w) {
 	case RSN_SEL(RSN_CSE_NULL):
-		return IEEE80211_CIPHER_NONE;
+		*cipher = IEEE80211_CIPHER_NONE;
+		break;
 	case RSN_SEL(RSN_CSE_WEP40):
 		if (keylen)
 			*keylen = 40 / NBBY;
-		return IEEE80211_CIPHER_WEP;
+		*cipher = IEEE80211_CIPHER_WEP;
+		break;
 	case RSN_SEL(RSN_CSE_WEP104):
 		if (keylen)
 			*keylen = 104 / NBBY;
-		return IEEE80211_CIPHER_WEP;
+		*cipher = IEEE80211_CIPHER_WEP;
+		break;
 	case RSN_SEL(RSN_CSE_TKIP):
-		return IEEE80211_CIPHER_TKIP;
+		*cipher = IEEE80211_CIPHER_TKIP;
+		break;
 	case RSN_SEL(RSN_CSE_CCMP):
-		return IEEE80211_CIPHER_AES_CCM;
+		*cipher = IEEE80211_CIPHER_AES_CCM;
+		break;
 	case RSN_SEL(RSN_CSE_WRAP):
-		return IEEE80211_CIPHER_AES_OCB;
+		*cipher = IEEE80211_CIPHER_AES_OCB;
+		break;
+	default:
+		return (EINVAL);
 	}
-	return 32;		/* NB: so 1<< is discarded */
+
+	return (0);
 #undef WPA_SEL
 }
 
@@ -1360,7 +1397,7 @@ ieee80211_parse_rsn(struct ieee80211vap 
 {
 	uint8_t len = frm[1];
 	uint32_t w;
-	int n;
+	int error, n;
 
 	/*
 	 * Check the length once for fixed parts: 
@@ -1373,6 +1410,7 @@ ieee80211_parse_rsn(struct ieee80211vap 
 		    wh, "WPA", "not RSN, flags 0x%x", vap->iv_flags);
 		return IEEE80211_REASON_IE_INVALID;
 	}
+	/* XXX may be shorter */
 	if (len < 10) {
 		IEEE80211_DISCARD_IE(vap,
 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
@@ -1385,14 +1423,28 @@ ieee80211_parse_rsn(struct ieee80211vap 
 		IEEE80211_DISCARD_IE(vap,
 		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
 		    wh, "RSN", "bad version %u", w);
-		return IEEE80211_REASON_IE_INVALID;
+		return IEEE80211_REASON_UNSUPP_RSN_IE_VERSION;
 	}
 	frm += 2, len -= 2;
 
 	memset(rsn, 0, sizeof(*rsn));
 
 	/* multicast/group cipher */
-	rsn->rsn_mcastcipher = rsn_cipher(frm, &rsn->rsn_mcastkeylen);
+	error = rsn_cipher(frm, &rsn->rsn_mcastkeylen, &rsn->rsn_mcastcipher);
+	if (error != 0) {
+		IEEE80211_DISCARD_IE(vap,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "unknown mcast cipher suite %08X",
+		    le32dec(frm));
+		return IEEE80211_REASON_GROUP_CIPHER_INVALID;
+	}
+	if (rsn->rsn_mcastcipher == IEEE80211_CIPHER_NONE) {
+		IEEE80211_DISCARD_IE(vap,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "invalid mcast cipher suite %d",
+		    rsn->rsn_mcastcipher);
+		return IEEE80211_REASON_GROUP_CIPHER_INVALID;
+	}
 	frm += 4, len -= 4;
 
 	/* unicast ciphers */
@@ -1406,14 +1458,33 @@ ieee80211_parse_rsn(struct ieee80211vap 
 		return IEEE80211_REASON_IE_INVALID;
 	}
 	w = 0;
+
 	for (; n > 0; n--) {
-		w |= 1<<rsn_cipher(frm, &rsn->rsn_ucastkeylen);
+		uint8_t cipher;
+
+		error = rsn_cipher(frm, &rsn->rsn_ucastkeylen, &cipher);
+		if (error == 0)
+			w |= 1 << cipher;
+
 		frm += 4, len -= 4;
 	}
-	if (w & (1<<IEEE80211_CIPHER_TKIP))
+        if (w & (1 << IEEE80211_CIPHER_AES_CCM))
+                rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+	else if (w & (1 << IEEE80211_CIPHER_AES_OCB))
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_OCB;
+	else if (w & (1 << IEEE80211_CIPHER_TKIP))
 		rsn->rsn_ucastcipher = IEEE80211_CIPHER_TKIP;
-	else
-		rsn->rsn_ucastcipher = IEEE80211_CIPHER_AES_CCM;
+	else if ((w & (1 << IEEE80211_CIPHER_NONE)) &&
+	    (rsn->rsn_mcastcipher == IEEE80211_CIPHER_WEP ||
+	     rsn->rsn_mcastcipher == IEEE80211_CIPHER_TKIP))
+		rsn->rsn_ucastcipher = IEEE80211_CIPHER_NONE;
+	else {
+		IEEE80211_DISCARD_IE(vap,
+		    IEEE80211_MSG_ELEMID | IEEE80211_MSG_WPA,
+		    wh, "RSN", "no usable pairwise cipher suite found (w=%d)",
+		    w);
+		return IEEE80211_REASON_PAIRWISE_CIPHER_INVALID;
+	}
 
 	/* key management algorithms */
 	n = le16dec(frm);
@@ -1510,6 +1581,7 @@ wpa_assocreq(struct ieee80211_node *ni, 
 	else
 		reason = ieee80211_parse_rsn(vap, rsn, rsnparms, wh);
 	if (reason != 0) {
+		/* XXX wpa->rsn fallback? */
 		/* XXX distinguish WPA/RSN? */
 		vap->iv_stats.is_rx_assoc_badwpaie++;
 		goto bad;


More information about the svn-src-head mailing list