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;
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to