diff --git a/contrib/wpa/src/drivers/driver_bsd.c b/contrib/wpa/src/drivers/driver_bsd.c index cfc6178993b6..55d73fcfc2c0 100644 --- a/contrib/wpa/src/drivers/driver_bsd.c +++ b/contrib/wpa/src/drivers/driver_bsd.c @@ -378,6 +378,9 @@ bsd_set_key(void *priv, struct wpa_driver_set_key_params *params) case WPA_ALG_CCMP: wk.ik_type = IEEE80211_CIPHER_AES_CCM; break; + case WPA_ALG_BIP_CMAC_128: + wk.ik_type = IEEE80211_CIPHER_BIP_CMAC_128; + break; default: wpa_printf(MSG_ERROR, "%s: unknown alg=%d", __func__, alg); return -1; @@ -449,6 +452,9 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params) case WPA_CIPHER_CCMP: v = IEEE80211_CIPHER_AES_CCM; break; + case WPA_CIPHER_BIP_CMAC_128: + v = IEEE80211_CIPHER_BIP_CMAC_128; + break; case WPA_CIPHER_TKIP: v = IEEE80211_CIPHER_TKIP; break; @@ -485,6 +491,8 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params) } v = 0; + if (params->wpa_pairwise & WPA_CIPHER_BIP_CMAC_128) + v |= 1<wpa_pairwise & WPA_CIPHER_CCMP) v |= 1<wpa_pairwise & WPA_CIPHER_TKIP) diff --git a/sbin/ifconfig/ifieee80211.c b/sbin/ifconfig/ifieee80211.c index d1d0c8a0b7d2..c78c99623a56 100644 --- a/sbin/ifconfig/ifieee80211.c +++ b/sbin/ifconfig/ifieee80211.c @@ -126,6 +126,7 @@ #define IEEE80211_NODE_VHT 0x100000 /* VHT enabled */ #define IEEE80211_NODE_LDPC 0x200000 /* LDPC enabled */ #define IEEE80211_NODE_UAPSD 0x400000 /* UAPSD enabled */ +#define IEEE80211_NODE_MFP 0x800000 /* MFP enabled */ #endif /* XXX should also figure out where to put these for k/u-space sharing. */ @@ -2693,6 +2694,8 @@ getflags(int flags) *cp++ = 'U'; if (flags & IEEE80211_NODE_LDPC) *cp++ = 'L'; + if (flags & IEEE80211_NODE_MFP) + *cp++ = 'F'; *cp = '\0'; return flagstring; } diff --git a/sys/conf/files b/sys/conf/files index 1f99c3586b86..6358df03164a 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -4215,6 +4215,8 @@ net80211/ieee80211_adhoc.c optional wlan \ compile-with "${NORMAL_C} -Wno-unused-function" net80211/ieee80211_ageq.c optional wlan net80211/ieee80211_amrr.c optional wlan | wlan_amrr +net80211/ieee80211_crypto_bip_cmac.c optional wlan wlan_bip_cmac +net80211/ieee80211_crypto_bip_gmac.c optional wlan wlan_bip_gmac net80211/ieee80211_crypto.c optional wlan \ compile-with "${NORMAL_C} -Wno-unused-function" net80211/ieee80211_crypto_ccmp.c optional wlan wlan_ccmp diff --git a/sys/dev/ath/if_ath.c b/sys/dev/ath/if_ath.c index 094517da5689..f20a77fb0864 100644 --- a/sys/dev/ath/if_ath.c +++ b/sys/dev/ath/if_ath.c @@ -1023,6 +1023,20 @@ ath_attach(u_int16_t devid, struct ath_softc *sc) sc->sc_rx_lnamixer = ath_hal_hasrxlnamixer(ah); sc->sc_hasdivcomb = ath_hal_hasdivantcomb(ah); + /* Add basic MFP support */ + /* + * The AR9160 and later allows for configurable MFP encryption + * and decryption. + * + * There's also some fun workarounds for Cisco's draft MFP + * versus 802.11w MFP for AES-CCM AAD assembly. + * + * But for now, just enable it here and treat AES-CCM MFP as + * normal data paths and IGTK as software encryption + * whilst everything else is being sorted out. + */ + ic->ic_caps |= IEEE80211_C_MFP; + /* * Some WB335 cards do not support antenna diversity. Since * we use a hardcoded value for AR9565 instead of using the diff --git a/sys/dev/ath/if_ath_keycache.c b/sys/dev/ath/if_ath_keycache.c index a58625ad2803..e8f4130a0990 100644 --- a/sys/dev/ath/if_ath_keycache.c +++ b/sys/dev/ath/if_ath_keycache.c @@ -201,6 +201,16 @@ ath_keyset(struct ath_softc *sc, struct ieee80211vap *vap, int ret; memset(&hk, 0, sizeof(hk)); + + /* + * If it's a IGTK key then just plain ignore it; + * it's already marked as handled in software and we don't + * need a keycache entry for it. + */ + if (ieee80211_is_key_igtk(vap, k)) { + return (1); + } + /* * Software crypto uses a "clear key" so non-crypto * state kept in the key cache are maintained and @@ -431,6 +441,18 @@ ath_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, * multi-station operation. */ if (k->wk_keyix != IEEE80211_KEYIX_NONE) { + /* + * Skip IGTK keys; they're global but not used + * in the normal hardware keyix slots. + */ + if (ieee80211_is_key_igtk(vap, k)) { + DPRINTF(sc, ATH_DEBUG_KEYCACHE, + "%s: iGTK key; skipping\n", __func__); + *keyix = *rxkeyix = + ieee80211_crypto_get_key_igtk_idx(vap, k); + return 1; + } + /* * Only global keys should have key index assigned. */ @@ -440,6 +462,9 @@ ath_key_alloc(struct ieee80211vap *vap, struct ieee80211_key *k, "%s: bogus group key\n", __func__); return 0; } + + /* TODO: bogus BIP key */ + if (vap->iv_opmode != IEEE80211_M_HOSTAP || !(k->wk_flags & IEEE80211_KEY_GROUP) || !sc->sc_mcastkey) { @@ -501,7 +526,17 @@ ath_key_delete(struct ieee80211vap *vap, const struct ieee80211_key *k) if (cip->ic_cipher == IEEE80211_CIPHER_TKIP && (k->wk_flags & IEEE80211_KEY_SWMIC) == 0 && sc->sc_splitmic) ath_hal_keyreset(ah, keyix+32); /* RX key */ - if (keyix >= IEEE80211_WEP_NKID) { + + /* + * Skip BIP keys; key indexes 4 and 5 are valid non-global + * keycache entries + */ + if (ieee80211_is_key_igtk(vap, k)) { + DPRINTF(sc, ATH_DEBUG_KEYCACHE, + "%s: keyix=%d but igtk key; skipping\n", + __func__, + keyix); + } else if (keyix >= IEEE80211_WEP_NKID) { /* * Don't touch keymap entries for global keys so * they are never considered for dynamic allocation. diff --git a/sys/dev/rtwn/if_rtwn_cam.c b/sys/dev/rtwn/if_rtwn_cam.c index d142cd0476e4..9421d2627437 100644 --- a/sys/dev/rtwn/if_rtwn_cam.c +++ b/sys/dev/rtwn/if_rtwn_cam.c @@ -185,11 +185,32 @@ rtwn_key_set_cb0(struct rtwn_softc *sc, const struct ieee80211_key *k) uint8_t algo, keyid; int i, error; + /* + * TODO: what about IGTK keys (4, 5) ? Would they allocated + * as "real" keycache entries? + */ + + /* + * TODO: I likely need to clean up by introducing + * "are these WEP group keys" and "are these IGTK group keys" + * inline functions and use them here, not the wk_keyix thing). + */ + if (sc->sc_hwcrypto == RTWN_CRYPTO_FULL && - k->wk_keyix < IEEE80211_WEP_NKID) + k->wk_keyix < IEEE80211_WEP_NKID) { + keyid = k->wk_keyix; + } else if (sc->sc_hwcrypto == RTWN_CRYPTO_FULL && + k->wk_flags & IEEE80211_KEY_GROUP) { keyid = k->wk_keyix; - else + } else if (sc->sc_hwcrypto == RTWN_CRYPTO_FULL && + (k->wk_flags & (IEEE80211_KEY_GROUP | IEEE80211_KEY_IGTK)) && + (k->wk_keyix == 4 || k->wk_keyix == 5)) { + device_printf(sc->sc_dev, "%s: got group key idx 4/5? (%d) (flags=0x%08x)\n", + __func__, k->wk_keyix, k->wk_flags); keyid = 0; + } else { + keyid = 0; + } /* Map net80211 cipher to HW crypto algorithm. */ switch (k->wk_cipher->ic_cipher) { diff --git a/sys/modules/Makefile b/sys/modules/Makefile index be7539e9eae1..9019d3a13436 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -419,6 +419,8 @@ SUBDIR= \ wlan \ wlan_acl \ wlan_amrr \ + wlan_bip_cmac \ + wlan_bip_gmac \ wlan_ccmp \ wlan_rssadapt \ wlan_tkip \ diff --git a/sys/net80211/ieee80211.c b/sys/net80211/ieee80211.c index c14b2305c09d..f4adabe54556 100644 --- a/sys/net80211/ieee80211.c +++ b/sys/net80211/ieee80211.c @@ -2705,7 +2705,8 @@ bool ieee80211_is_key_igtk(const struct ieee80211vap *vap, const struct ieee80211_key *key) { - return false; + /* TODO: don't hard-code these two key indexes? */ + return (key == &vap->iv_nw_keys[4] || key == &vap->iv_nw_keys[5]); } /* @@ -2716,9 +2717,160 @@ ieee80211_is_key_unicast(const struct ieee80211vap *vap, const struct ieee80211_key *key) { /* - * This is a short-cut for now; eventually we will need - * to support multiple unicast keys, IGTK, etc) so we - * will absolutely need to fix the key flags. + * TODO: fix this to use the key flags! Once they're + * actually validated to be CORRECT! + * + * (and maybe add a check here to match up the key type + * with the key flags.) */ - return (!ieee80211_is_key_global(vap, key)); + return (!(ieee80211_is_key_global(vap, key) + || ieee80211_is_key_igtk(vap, key))); } + +/* + * Table 9-47 - Category Values + */ +bool +ieee80211_mgmt_action_mfp_robust(struct ieee80211_node *ni, const struct mbuf *m0) +{ + struct ieee80211vap *vap = ni->ni_vap; + const struct ieee80211_frame *wh = + mtod(m0, const struct ieee80211_frame *); + const uint8_t *frm = (const uint8_t *)&wh[1]; + const uint8_t *efrm = mtod(m0, u_int8_t *) + m0->m_len; + const struct ieee80211_action *ia; + + /* Check action frame length! */ + /* XXX TODO: god I hate pointer math */ + if ((efrm - frm) < sizeof(struct ieee80211_action)) { + if_printf(vap->iv_ifp, "%s: short action frame?\n", __func__); + return (false); + } + + ia = (const struct ieee80211_action *) frm; + if_printf(vap->iv_ifp, "%s: action: category=%d subcategory=%d\n", + __func__, ia->ia_category, ia->ia_action); + + switch (ia->ia_category) { + case IEEE80211_ACTION_CAT_SM: + case IEEE80211_ACTION_CAT_QOS: + case 2: /* DLS */ + case IEEE80211_ACTION_CAT_BA: + case IEEE80211_ACTION_CAT_FAST_BBS_TRANSITION: /* Yes, typo in header */ + case IEEE80211_ACTION_CAT_SA_QUERY: + case IEEE80211_ACTION_CAT_PROTECTED_DUAL_OF_PUBLIC_ACTION: + case IEEE80211_ACTION_CAT_WNM: + case IEEE80211_ACTION_CAT_UNPROTECTED_WNM: + case IEEE80211_ACTION_CAT_MESH: + case IEEE80211_ACTION_CAT_MULTIHOP: + case IEEE80211_ACTION_CAT_DMG: + case IEEE80211_ACTION_CAT_FAST_SESSION_TRANSFER: + case IEEE80211_ACTION_CAT_ROBUST_AV_STREAMING: + case IEEE80211_ACTION_CAT_VENDOR_SPECIFIC_PROTECTED: + return true; + case IEEE80211_ACTION_CAT_RADIO_MEASUREMENT: + /* + * Note: Everything is protected except + * Link Measurement Request and Link Measurement Reports. + */ + return false; + case IEEE80211_ACTION_CAT_TDLS: + /* + * Note: these are always carried in a data frame, so + * we likely should log something as a warning here? + */ + return false; + default: + return false; + } + + return false; +} + +bool +ieee80211_mgmt_action_mfp_group_privacy(struct ieee80211_node *ni, + const struct mbuf *m0) +{ + /* XXX TODO */ + return false; +} + +bool +ieee80211_mgmt_verify_mfp(struct ieee80211_node *ni, + const struct mbuf *m0, uint8_t s_subtype, bool has_decrypted) +{ + const struct ieee80211_frame *wh; + int dir, type, subtype; + bool check = false; + struct ieee80211vap *vap = ni->ni_vap; + + wh = mtod(m0, const struct ieee80211_frame *); + dir = wh->i_fc[1] & IEEE80211_FC1_DIR_MASK; + type = wh->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + subtype = wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK; + + (void) dir; + + /* For now, let's look at mgmt */ + + /* + * Robust Management Frame Protection - 802.11-2016 + * 12.2.8 (Requirements for Robust Management Frame Protection.) + * + * DISASSOC, DEAUTH, and Robust Management Frames (Table 9-47.) + */ + switch (type) { + case IEEE80211_FC0_TYPE_MGT: + switch (subtype) { + case IEEE80211_FC0_SUBTYPE_BEACON: + /* Not protected */ + check = false; + break; + case IEEE80211_FC0_SUBTYPE_ATIM: + /* Not protected */ + check = false; + break; + case IEEE80211_FC0_SUBTYPE_DISASSOC: + check = true; + break; + case IEEE80211_FC0_SUBTYPE_AUTH: + check = false; + break; + case IEEE80211_FC0_SUBTYPE_DEAUTH: + check = true; + break; + case IEEE80211_FC0_SUBTYPE_ACTION: + case IEEE80211_FC0_SUBTYPE_ACTION_NOACK: + check = ieee80211_mgmt_action_mfp_robust(ni, m0); + break; + default: + check = false; + break; + } + default: + check = false; + break; + } + + /* XXX TODO: group addressed privacy check? */ + + if (check) { + if_printf(vap->iv_ifp, + "%s: type=%d, subtype=%d, has_decrypted=%d, mfp=%d\n", + __func__, + type, + subtype, + has_decrypted, + !! (ni->ni_flags & IEEE80211_NODE_MFP)); + + if ((ni->ni_flags & IEEE80211_NODE_MFP) && + (has_decrypted == false)) { + /* TODO: return failure */ + if_printf(vap->iv_ifp, + "%s: would fail MFP check!\n", __func__); + } + } + + return true; +} + diff --git a/sys/net80211/ieee80211.h b/sys/net80211/ieee80211.h index fe2a0a7f3b64..e471569fa3c2 100644 --- a/sys/net80211/ieee80211.h +++ b/sys/net80211/ieee80211.h @@ -1469,7 +1469,10 @@ enum { #define IEEE80211_WEP_TOTLEN (IEEE80211_WEP_IVLEN + \ IEEE80211_WEP_KIDLEN + \ IEEE80211_WEP_CRCLEN) -#define IEEE80211_WEP_NKID 4 /* number of key ids */ + +#define IEEE80211_WEP_NKID 4 /* number of WEP/global key ids */ +#define IEEE80211_MAX_NKID 6 /* number of WEP/global/iGTK key ids */ +#define IEEE80211_IGTK_NKID 2 /* number of iGTK key IDs */ /* * 802.11i defines an extended IV for use with non-WEP ciphers. diff --git a/sys/net80211/ieee80211_action.c b/sys/net80211/ieee80211_action.c index 35b57322d569..47e527958a17 100644 --- a/sys/net80211/ieee80211_action.c +++ b/sys/net80211/ieee80211_action.c @@ -124,6 +124,9 @@ ieee80211_send_action_unregister(int cat, int act) ieee80211_send_action_register(cat, act, send_inval); } +/* + * XXX TODO: MFP! + */ int ieee80211_send_action(struct ieee80211_node *ni, int cat, int act, void *sa) { @@ -235,6 +238,10 @@ ieee80211_recv_action_unregister(int cat, int act) ieee80211_recv_action_register(cat, act, recv_inval); } +/* + * TODO: MFP checks for both unicast and group management frames. + */ + int ieee80211_recv_action(struct ieee80211_node *ni, const struct ieee80211_frame *wh, diff --git a/sys/net80211/ieee80211_adhoc.c b/sys/net80211/ieee80211_adhoc.c index 38f73d87f3d4..e4923addbc4d 100644 --- a/sys/net80211/ieee80211_adhoc.c +++ b/sys/net80211/ieee80211_adhoc.c @@ -657,6 +657,7 @@ adhoc_input(struct ieee80211_node *ni, struct mbuf *m, vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ goto out; } + /* XXX MFP */ vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf); goto out; diff --git a/sys/net80211/ieee80211_crypto.c b/sys/net80211/ieee80211_crypto.c index 12ab81960b23..64754ede700a 100644 --- a/sys/net80211/ieee80211_crypto.c +++ b/sys/net80211/ieee80211_crypto.c @@ -40,6 +40,7 @@ #include #include +#include /* if_printf() */ #include #include /* XXX ETHER_HDR_LEN */ @@ -231,9 +232,11 @@ ieee80211_crypto_vattach(struct ieee80211vap *vap) /* NB: we assume everything is pre-zero'd */ vap->iv_max_keyix = IEEE80211_WEP_NKID; vap->iv_def_txkey = IEEE80211_KEYIX_NONE; - for (i = 0; i < IEEE80211_WEP_NKID; i++) + + for (i = 0; i < IEEE80211_MAX_NKID; i++) ieee80211_crypto_resetkey(vap, &vap->iv_nw_keys[i], IEEE80211_KEYIX_NONE); + /* * Initialize the driver key support routines to noop entries. * This is useful especially for the cipher test modules. @@ -546,7 +549,7 @@ ieee80211_crypto_delglobalkeys(struct ieee80211vap *vap) int i; ieee80211_key_update_begin(vap); - for (i = 0; i < IEEE80211_WEP_NKID; i++) + for (i = 0; i < IEEE80211_MAX_NKID; i++) (void) _ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[i]); ieee80211_key_update_end(vap); } @@ -613,6 +616,28 @@ ieee80211_crypto_get_key_wepidx(const struct ieee80211vap *vap, } /* + * Return index if the key is an IGTK key (4..5); -1 otherwise. + * + * This is different to "get_keyid" which defaults to returning + * 0 for unicast keys; it assumes that it won't be used for WEP. + */ +int +ieee80211_crypto_get_key_igtk_idx(const struct ieee80211vap *vap, + const struct ieee80211_key *k) +{ + if (ieee80211_is_key_igtk(vap, k)) { + /* XXX TODO: i hate this */ + return (k - vap->iv_nw_keys); + } + + return (-1); +} + +/* + * Return the group or unicast key. + * + * Don't call this for IGTK keyx! + * * Note: only supports a single unicast key (0). */ uint8_t @@ -622,6 +647,16 @@ ieee80211_crypto_get_keyid(struct ieee80211vap *vap, struct ieee80211_key *k) return (k - vap->iv_nw_keys); } + /* + * TODO: I wish this function could return an error, + * but then I'd have to audit all the places that called it + * and make sure THEY can handle the error! + */ + if (ieee80211_is_key_igtk(vap, k)) { + if_printf(vap->iv_ifp, "%s: IGTK key passed in! Invalid!\n", + __func__); + } + return (0); } @@ -857,11 +892,16 @@ ieee80211_crypto_reload_keys(struct ieee80211com *ic) TAILQ_FOREACH(vap, &ic->ic_vaps, iv_next) { if (vap->iv_state != IEEE80211_S_RUN) continue; + + /* Global/WEP keys */ for (i = 0; i < IEEE80211_WEP_NKID; i++) { const struct ieee80211_key *k = &vap->iv_nw_keys[i]; if (k->wk_flags & IEEE80211_KEY_DEVKEY) dev_key_set(vap, k); } + + /* TODO: do the two iGTK keys too if the driver supports it? */ + /* (The drivers will need to be made aware of the other two KIDs and ignore them if needed..) */ } /* * Unicast keys. diff --git a/sys/net80211/ieee80211_crypto.h b/sys/net80211/ieee80211_crypto.h index d8ca6e49f4c7..662254e4de21 100644 --- a/sys/net80211/ieee80211_crypto.h +++ b/sys/net80211/ieee80211_crypto.h @@ -91,6 +91,7 @@ struct ieee80211_key { #define IEEE80211_KEY_NOIVMGT 0x00008000 /* don't insert IV/MIC for mgmt */ #define IEEE80211_KEY_NOMIC 0x00010000 /* don't insert MIC for !mgmt */ #define IEEE80211_KEY_NOMICMGT 0x00020000 /* don't insert MIC for mgmt */ +#define IEEE80211_KEY_IGTK 0x00040000 /* key is used for IGTK */ ieee80211_keyix wk_keyix; /* h/w key index */ ieee80211_keyix wk_rxkeyix; /* optional h/w rx key index */ @@ -106,7 +107,7 @@ struct ieee80211_key { }; #define IEEE80211_KEY_COMMON /* common flags passed in by apps */\ (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV | IEEE80211_KEY_GROUP | \ - IEEE80211_KEY_NOREPLAY) + IEEE80211_KEY_NOREPLAY | IEEE80211_KEY_IGTK) #define IEEE80211_KEY_SWCRYPT \ (IEEE80211_KEY_SWENCRYPT | IEEE80211_KEY_SWDECRYPT) @@ -231,6 +232,8 @@ int ieee80211_crypto_available(u_int cipher); int ieee80211_crypto_get_key_wepidx(const struct ieee80211vap *, const struct ieee80211_key *k); +int ieee80211_crypto_get_key_igtk_idx(const struct ieee80211vap *, + const struct ieee80211_key *k); uint8_t ieee80211_crypto_get_keyid(struct ieee80211vap *vap, struct ieee80211_key *k); struct ieee80211_key *ieee80211_crypto_get_txkey(struct ieee80211_node *, diff --git a/sys/net80211/ieee80211_ddb.c b/sys/net80211/ieee80211_ddb.c index eca893fa6810..b37620f41e34 100644 --- a/sys/net80211/ieee80211_ddb.c +++ b/sys/net80211/ieee80211_ddb.c @@ -604,7 +604,7 @@ _db_show_vap(const struct ieee80211vap *vap, int showmesh, int showprocs) db_printf("\tmax_keyix %u", vap->iv_max_keyix); db_printf(" def_txkey %d", vap->iv_def_txkey); db_printf("\n"); - for (i = 0; i < IEEE80211_WEP_NKID; i++) + for (i = 0; i < IEEE80211_MAX_NKID; i++) _db_show_key("\tnw_keys[%u]", i, &vap->iv_nw_keys[i]); db_printf("\tauth %p(%s)", vap->iv_auth, vap->iv_auth->ia_name); diff --git a/sys/net80211/ieee80211_hostap.c b/sys/net80211/ieee80211_hostap.c index 1d741ca4d7bf..1b1719938c46 100644 --- a/sys/net80211/ieee80211_hostap.c +++ b/sys/net80211/ieee80211_hostap.c @@ -887,6 +887,7 @@ hostap_input(struct ieee80211_node *ni, struct mbuf *m, if (ieee80211_radiotap_active_vap(vap)) ieee80211_radiotap_rx(vap, m); need_tap = 0; + /* XXX MFP */ vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf); goto out; @@ -1422,8 +1423,10 @@ rsn_keymgmt(const uint8_t *sel) * Parse a WPA/RSN information element to collect parameters * and validate the parameters against what has been * configured for the system. + * + * Further information can be found in 802.11-2016 9.4.2.25 (RSNE). */ -static int +int ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm, struct ieee80211_rsnparms *rsn, const struct ieee80211_frame *wh) { @@ -1544,9 +1547,9 @@ ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm, frm += 2, len -= 2; } - /* XXX PMK Count / PMKID */ + /* XXX PMKID */ - /* XXX Group Cipher Management Suite */ + /* XXX Group Management Cipher Suite */ return 0; } @@ -2183,6 +2186,22 @@ hostap_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, return; } + /* + * TODO: check if the VAP has MFP enabled and MFP is + * available in the relevant IE; update node state, + * and reject if required if MFP is required by either + * side and not BOTH sides! + */ + if (rsn != NULL) { + if (vap->iv_mfp_cfg != 0) { + if_printf(vap->iv_ifp, + "%s: called; MFP on VAP, RSN IE, should go implement MFP negotiation!\n", + __func__); + } + + /* TODO: MFP negotiation/checks */ + } + /* * Do HT rate set handling and setup HT node state. */ diff --git a/sys/net80211/ieee80211_ht.c b/sys/net80211/ieee80211_ht.c index c747d29735a6..fb0d23f25457 100644 --- a/sys/net80211/ieee80211_ht.c +++ b/sys/net80211/ieee80211_ht.c @@ -1218,6 +1218,8 @@ ieee80211_ampdu_reorder(struct ieee80211_node *ni, struct mbuf *m, * Process a BAR ctl frame. Dispatch all frames up to * the sequence number of the frame. If this frame is * out of range it's discarded. + * + * XXX TODO: protected MFP block-ack? (10.24.9) */ void ieee80211_recv_bar(struct ieee80211_node *ni, struct mbuf *m0) @@ -2905,6 +2907,9 @@ ieee80211_bar_response(struct ieee80211_node *ni, * state associated with the node. * * NB: we only handle immediate ACK w/ compressed bitmap. + * + * XXX TODO: protected MFP block-ack? (10.24.9) + * */ int ieee80211_send_bar(struct ieee80211_node *ni, diff --git a/sys/net80211/ieee80211_ioctl.c b/sys/net80211/ieee80211_ioctl.c index 6cb192928a5d..ab2c4d0f6e7f 100644 --- a/sys/net80211/ieee80211_ioctl.c +++ b/sys/net80211/ieee80211_ioctl.c @@ -93,7 +93,7 @@ ieee80211_ioctl_getkey(u_long cmd, struct ieee80211vap *vap, return ENOENT; wk = &ni->ni_ucastkey; } else { - if (kid >= IEEE80211_WEP_NKID) + if (kid >= IEEE80211_MAX_NKID) return EINVAL; wk = &vap->iv_nw_keys[kid]; IEEE80211_ADDR_COPY(&ik.ik_macaddr, vap->iv_bss->ni_macaddr); @@ -1177,8 +1177,8 @@ ieee80211_ioctl_get80211(struct ieee80211vap *vap, u_long cmd, case IEEE80211_MFP_PROTMODE_REQUIRED: ireq->i_val = IEEE80211_MFP_REQUIRED; break; - } - break; + } + break; case IEEE80211_IOC_VHTCONF: ireq->i_val = vap->iv_vht_flags & IEEE80211_FVHT_MASK; break; @@ -1209,6 +1209,7 @@ ieee80211_ioctl_setkey(struct ieee80211vap *vap, struct ieee80211req *ireq) if (ik.ik_keylen > sizeof(ik.ik_keydata)) return E2BIG; kid = ik.ik_keyix; + if_printf(vap->iv_ifp, "%s: called; kid=%d\n", __func__, kid); if (kid == IEEE80211_KEYIX_NONE) { /* XXX unicast keys currently must be tx/rx */ if (ik.ik_flags != (IEEE80211_KEY_XMIT | IEEE80211_KEY_RECV)) @@ -1227,7 +1228,7 @@ ieee80211_ioctl_setkey(struct ieee80211vap *vap, struct ieee80211req *ireq) } wk = &ni->ni_ucastkey; } else { - if (kid >= IEEE80211_WEP_NKID) + if (kid >= IEEE80211_MAX_NKID) return EINVAL; wk = &vap->iv_nw_keys[kid]; /* @@ -1307,6 +1308,7 @@ ieee80211_ioctl_delkey(struct ieee80211vap *vap, struct ieee80211req *ireq) if (error) return error; kid = dk.idk_keyix; + if_printf(vap->iv_ifp, "%s: called; kid=%d\n", __func__, kid); /* XXX uint8_t -> uint16_t */ if (dk.idk_keyix == (uint8_t) IEEE80211_KEYIX_NONE) { struct ieee80211_node *ni; @@ -1327,7 +1329,7 @@ ieee80211_ioctl_delkey(struct ieee80211vap *vap, struct ieee80211req *ireq) ieee80211_node_delucastkey(ni); ieee80211_free_node(ni); } else { - if (kid >= IEEE80211_WEP_NKID) + if (kid >= IEEE80211_MAX_NKID) return EINVAL; /* XXX error return */ ieee80211_crypto_delkey(vap, &vap->iv_nw_keys[kid]); @@ -2505,6 +2507,7 @@ ieee80211_ioctl_setappie_locked(struct ieee80211vap *vap, break; case (IEEE80211_APPIE_WPA & IEEE80211_FC0_SUBTYPE_MASK): error = setappie(&vap->iv_appie_wpa, ireq); + if (error == 0) { /* * Must split single blob of data into separate @@ -2519,10 +2522,31 @@ ieee80211_ioctl_setappie_locked(struct ieee80211vap *vap, vap->iv_appie_wpa; uint8_t *data = appie->ie_data; + if_printf(vap->iv_ifp, "%s: APPIE_WPA: %*D\n", + __func__, + vap->iv_appie_wpa->ie_len, + vap->iv_appie_wpa->ie_data, + ":"); + /* XXX ie length validate is painful, cheat */ setwparsnie(vap, data, appie->ie_len); setwparsnie(vap, data + 2 + data[1], appie->ie_len - (2 + data[1])); + + if (vap->iv_wpa_ie != NULL) { + if_printf(vap->iv_ifp, "%s: WPA_IE: %*D\n", + __func__, + vap->iv_wpa_ie[1] + 2, + vap->iv_wpa_ie, + ":"); + } + if (vap->iv_rsn_ie != NULL) { + if_printf(vap->iv_ifp, "%s: RSN_IE: %*D\n", + __func__, + vap->iv_rsn_ie[1] + 2, + vap->iv_rsn_ie, + ":"); + } } if (vap->iv_opmode == IEEE80211_M_HOSTAP || vap->iv_opmode == IEEE80211_M_IBSS) { diff --git a/sys/net80211/ieee80211_mesh.c b/sys/net80211/ieee80211_mesh.c index 73e7ef7a8efd..aca3b2bff3c5 100644 --- a/sys/net80211/ieee80211_mesh.c +++ b/sys/net80211/ieee80211_mesh.c @@ -1800,6 +1800,7 @@ mesh_input(struct ieee80211_node *ni, struct mbuf *m, vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ goto out; } + /* XXX MFP */ vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf); goto out; case IEEE80211_FC0_TYPE_CTL: diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 8f8dc5f378b8..d6cdcddccfeb 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -439,6 +439,12 @@ ieee80211_create_ibss(struct ieee80211vap* vap, struct ieee80211_channel *chan) } } + /* Enable MFP if configured */ + if (vap->iv_mfp_cfg != 0) { + if_printf(vap->iv_ifp, "%s: called; mfp enabled\n", __func__); + ni->ni_flags |= IEEE80211_NODE_MFP; + } + /* XXX TODO: other bits and pieces - eg fast-frames? */ /* If we're an 11n channel then initialise the 11n bits */ @@ -993,6 +999,8 @@ ieee80211_sta_join(struct ieee80211vap *vap, struct ieee80211_channel *chan, if (ni->ni_ies.vhtopmode_ie != NULL) ieee80211_parse_vhtopmode(ni, ni->ni_ies.vhtopmode_ie); + /* XXX parse RSN IE for MFP? */ + /* XXX parse BSSLOAD IE */ /* XXX parse TXPWRENV IE */ /* XXX parse APCHANREP IE */ @@ -1007,6 +1015,86 @@ ieee80211_sta_join(struct ieee80211vap *vap, struct ieee80211_channel *chan, if (ieee80211_iserp_rateset(&ni->ni_rates)) ni->ni_flags |= IEEE80211_NODE_ERP; + /* + * XXX TODO: handle MFP negotiation! + * + * XXX TODO: this also means handling no RSN IE but + * having MFP required on the VAP. + */ + if (ni->ni_ies.rsn_ie != NULL) { + struct ieee80211_rsnparms rsn; + uint16_t rsn_caps; + int ret; + bool enable_mfp = false; + + /* + * ieee80211_parse_rsn makes a bunch of decisions as well + * as parsing; we don't need those decisions. + * Let's hope they're OK. + */ + ret = ieee80211_parse_rsn(vap, ni->ni_ies.rsn_ie, &rsn, NULL); // NB: no frame here + if (ret != 0) { + vap->iv_stats.is_rx_assoc_badwpaie++; + return 0; + } + + /* This is the only field we need here */ + rsn_caps = rsn.rsn_caps; + + if_printf(vap->iv_ifp, + "%s: called; MFP config: %d, rsn_caps=0x%04x\n", + __func__, vap->iv_mfp_cfg, rsn_caps); + if_printf(vap->iv_ifp, "%s: RSN IE: %*D\n", + __func__, + ni->ni_ies.rsn_ie[1] + 2, + (u_char *) ni->ni_ies.rsn_ie, + ":"); + + /* + * Look at RSN_CAP_MFP_CAPABLE and RSN_CAP_MFP_REQUIRED + * versus iv_mfp_cfg. + */ + switch (vap->iv_mfp_cfg) { + case IEEE80211_MFP_PROTMODE_DISABLED: + /* If disabled, then MFP bits must be none or capable; required == fail */ + if (rsn_caps & RSN_CAP_MFP_REQUIRED) { + /* XXX counter */ + if_printf(vap->iv_ifp, + "%s: MFP DISABLED but RSN MFP REQUIRED, fail!", + __func__); + return 0; + } + break; + case IEEE80211_MFP_PROTMODE_OPTIONAL: + /* If optional, then MFP bits can be none, capable, required */ + break; + case IEEE80211_MFP_PROTMODE_REQUIRED: + /* If reqiured, then MFP bits must be capable or required, none == fail */ + if ((rsn_caps & (RSN_CAP_MFP_REQUIRED | RSN_CAP_MFP_CAPABLE)) == 0) { + /* XXX counter */ + if_printf(vap->iv_ifp, + "%s: MFP REQUIRED but RSN MFP NONE, fail!", + __func__); + return 0; + } + } + + + if ((vap->iv_mfp_cfg != IEEE80211_MFP_PROTMODE_DISABLED) + && (rsn_caps & (RSN_CAP_MFP_REQUIRED | RSN_CAP_MFP_CAPABLE))) + enable_mfp = true; + + if (enable_mfp == true) { + ni->ni_flags |= IEEE80211_NODE_MFP; + } else { + ni->ni_flags &= ~IEEE80211_NODE_MFP; + } + if_printf(vap->iv_ifp, + "%s: MFP is %s\n", + __func__, + (enable_mfp == true ? "enabled" : "disabled")); + } + /* * Setup HT state for this node if it's available, otherwise * non-STA modes won't pick this state up. @@ -1845,6 +1933,15 @@ ieee80211_init_neighbor(struct ieee80211_node *ni, (ni->ni_vap->iv_vht_flags & IEEE80211_FVHT_VHT)) { do_vht_setup = 1; } + + /* XXX TODO: parse RSN IE for MFP? */ + } + + /* + * XXX TODO: handle MFP negotiation! + */ + if (ni->ni_ies.rsn_ie != NULL) { + if_printf(ni->ni_vap->iv_ifp, "%s: called; MFP TODO\n", __func__); } /* NB: must be after ni_chan is setup */ diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index 954e8e03563f..e577dc0440c1 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -145,6 +145,7 @@ struct ieee80211_node { #define IEEE80211_NODE_VHT 0x100000 /* VHT enabled */ #define IEEE80211_NODE_LDPC 0x200000 /* LDPC enabled */ #define IEEE80211_NODE_UAPSD 0x400000 /* U-APSD power save enabled */ +#define IEEE80211_NODE_MFP 0x800000 /* MFP enabled */ uint16_t ni_associd; /* association ID */ uint16_t ni_vlan; /* vlan tag */ uint16_t ni_txpower; /* current transmit power */ diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index 22bbfe98f4b5..bf0810a0a5ab 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -1008,6 +1008,20 @@ ieee80211_send_setup( * dispatched to the driver, then it is responsible for freeing the * reference (and potentially free'ing up any associated storage); * otherwise deal with reclaiming any reference (on error). + * + * TODO: MFP encryption depending upon destination node, type and + * MFP? + * + * TODO: MFP Group addressed mgmt frames may need a MIC IE - 9.4.2.55 - + * Management MIC element (IE 76) . This element always is at the + * end! + * + * 12.2.7 - Requirements for the Protected Management Field + * (includes individually addressed robust management frames) + * 12.2.8 - Requirements for Robust Management Frame Protection + * Robust Mgmt Frames: Disassoc, Deauth, Robust Action + - Table 9-47 - Action Frames + * - group addressed robust management frames - 11.13 */ int ieee80211_mgmt_output(struct ieee80211_node *ni, struct mbuf *m, int type, diff --git a/sys/net80211/ieee80211_sta.c b/sys/net80211/ieee80211_sta.c index 5da7999951d6..ac19c1a1bb4d 100644 --- a/sys/net80211/ieee80211_sta.c +++ b/sys/net80211/ieee80211_sta.c @@ -931,6 +931,8 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, * It also applies here. */ + /* TODO: handle MFP check on received frames for this node */ + /* * Again, having encrypted flag set check would be good, but * then we have to also handle crypto_decap() like above. @@ -970,6 +972,13 @@ sta_input(struct ieee80211_node *ni, struct mbuf *m, wh = mtod(m, struct ieee80211_frame *); wh->i_fc[1] &= ~IEEE80211_FC1_PROTECTED; } + + /* Verify the management frame passes MFP checks */ + if (ieee80211_mgmt_verify_mfp(ni, m, subtype, has_decrypted) == false) { + IEEE80211_DISCARD(vap, IEEE80211_MSG_ANY, + wh, NULL, "type=%d subtype=%d, has_decrypted=%d, MFP check failed", type, subtype, has_decrypted); + /* XXX TODO: skip */ + } vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf); goto out; @@ -1411,6 +1420,7 @@ sta_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, int subtype, wh = mtod(m0, struct ieee80211_frame *); frm = (uint8_t *)&wh[1]; efrm = mtod(m0, uint8_t *) + m0->m_len; + switch (subtype) { case IEEE80211_FC0_SUBTYPE_PROBE_RESP: case IEEE80211_FC0_SUBTYPE_BEACON: { diff --git a/sys/net80211/ieee80211_tdma.c b/sys/net80211/ieee80211_tdma.c index 90ab2525e488..e6e5102dc537 100644 --- a/sys/net80211/ieee80211_tdma.c +++ b/sys/net80211/ieee80211_tdma.c @@ -395,6 +395,7 @@ tdma_recv_mgmt(struct ieee80211_node *ni, struct mbuf *m0, * 2x parsing of the frame but should happen infrequently */ } + /* XXX MFP? */ ts->tdma_recv_mgmt(ni, m0, subtype, rxs, rssi, nf); } diff --git a/sys/net80211/ieee80211_var.h b/sys/net80211/ieee80211_var.h index 69b262a9a27a..21d5bfe2dd38 100644 --- a/sys/net80211/ieee80211_var.h +++ b/sys/net80211/ieee80211_var.h @@ -509,7 +509,7 @@ struct ieee80211vap { /* Key management */ uint16_t iv_max_keyix; /* max h/w key index */ ieee80211_keyix iv_def_txkey; /* default/group tx key index */ - struct ieee80211_key iv_nw_keys[IEEE80211_WEP_NKID]; + struct ieee80211_key iv_nw_keys[IEEE80211_MAX_NKID]; int (*iv_key_alloc)(struct ieee80211vap *, struct ieee80211_key *, ieee80211_keyix *, ieee80211_keyix *); @@ -612,7 +612,7 @@ struct ieee80211vap { uint8_t iv_uapsdinfo; /* sta mode QoS Info flags */ /* per-VAP MFP configuration */ - enum ieee80211_mfp_mode iv_mfp_cfg; + enum ieee80211_mfp_mode iv_mfp_cfg; /* Optional transmit/receive histogram statistics */ struct ieee80211_rx_histogram *rx_histogram; @@ -831,9 +831,24 @@ bool ieee80211_is_key_global(const struct ieee80211vap *vap, const struct ieee80211_key *key); bool ieee80211_is_key_igtk(const struct ieee80211vap *vap, const struct ieee80211_key *key); +bool ieee80211_is_key_igtk(const struct ieee80211vap *vap, + const struct ieee80211_key *key); bool ieee80211_is_key_unicast(const struct ieee80211vap *vap, const struct ieee80211_key *key); +/* Note: this is in ieee80211.c, needs to be moved out */ +bool ieee80211_mgmt_verify_mfp(struct ieee80211_node *ni, + const struct mbuf *m, uint8_t subtype, bool has_decrypted); +bool ieee80211_mgmt_action_mfp_robust(struct ieee80211_node *ni, + const struct mbuf *m0); +bool ieee80211_mgmt_action_mfp_group_privacy(struct ieee80211_node *ni, + const struct mbuf *m0); + +/* Note: this is in ieee80211_hostap.c, it needs to be moved out */ +int ieee80211_parse_rsn(struct ieee80211vap *vap, const uint8_t *frm, + struct ieee80211_rsnparms *rsn, + const struct ieee80211_frame *wh); + void ieee80211_radiotap_attach(struct ieee80211com *, struct ieee80211_radiotap_header *th, int tlen, uint32_t tx_radiotap, diff --git a/sys/net80211/ieee80211_wds.c b/sys/net80211/ieee80211_wds.c index 79c2d8a14e00..ac8ded212eb0 100644 --- a/sys/net80211/ieee80211_wds.c +++ b/sys/net80211/ieee80211_wds.c @@ -714,6 +714,7 @@ wds_input(struct ieee80211_node *ni, struct mbuf *m, vap->iv_stats.is_rx_mgtdiscard++; /* XXX */ goto out; } + /* XXX MFP? */ vap->iv_recv_mgmt(ni, m, subtype, rxs, rssi, nf); goto out;