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
This commit is contained in:
Andriy Voskoboinyk 2016-09-13 22:59:38 +00:00
parent 462b35ac65
commit 95d9a127b2
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=305785

View File

@ -1153,28 +1153,36 @@ hostap_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
* 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 *vap, const uint8_t *frm,
{
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 *vap, const uint8_t *frm,
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 *vap, const uint8_t *frm,
}
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 *vap, const uint8_t *frm,
* 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 *vap, const uint8_t *frm,
{
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 *vap, const uint8_t *frm,
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 *vap, const uint8_t *frm,
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 *vap, const uint8_t *frm,
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, struct ieee80211_rsnparms *rsnparms,
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;