Index: sys/net80211/ieee80211_hostap.c =================================================================== --- sys/net80211/ieee80211_hostap.c +++ sys/net80211/ieee80211_hostap.c @@ -1153,28 +1153,36 @@ * 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 @@ { 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 @@ 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 @@ } w = 0; for (; n > 0; n--) { - w |= 1<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<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 @@ * 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 @@ { uint8_t len = frm[1]; uint32_t w; - int n; + int error, n; /* * Check the length once for fixed parts: @@ -1373,6 +1410,7 @@ 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_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 @@ return IEEE80211_REASON_IE_INVALID; } w = 0; + for (; n > 0; n--) { - w |= 1<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<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 @@ 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;