diff options
author | Stefan Sperling <stsp@cvs.openbsd.org> | 2017-03-01 19:28:49 +0000 |
---|---|---|
committer | Stefan Sperling <stsp@cvs.openbsd.org> | 2017-03-01 19:28:49 +0000 |
commit | 353445e9d181c65f16069e38930d0964f992c014 (patch) | |
tree | a5ffd5aee5d6ef87538497749ded4bc12d8f8610 /sys/net80211 | |
parent | 7e044a6343adf75c483b0708d64d940806878eb5 (diff) |
Fix a bug allowing a man-in-the-middle attack against WPA wireless clients.
A malicious AP could trick clients into connecting to the malicious AP
instead of the desired AP. All frames would then be sent in the clear.
This problem was found and reported by Mathy Vanhoef who also provided
an initial patch which we improved together.
Diffstat (limited to 'sys/net80211')
-rw-r--r-- | sys/net80211/ieee80211_input.c | 3 | ||||
-rw-r--r-- | sys/net80211/ieee80211_node.h | 11 | ||||
-rw-r--r-- | sys/net80211/ieee80211_pae_input.c | 36 | ||||
-rw-r--r-- | sys/net80211/ieee80211_pae_output.c | 4 | ||||
-rw-r--r-- | sys/net80211/ieee80211_proto.c | 12 |
5 files changed, 60 insertions, 6 deletions
diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index 84c0caf7c42..4dff49f2d41 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_input.c,v 1.186 2017/02/02 16:47:53 stsp Exp $ */ +/* $OpenBSD: ieee80211_input.c,v 1.187 2017/03/01 19:28:48 stsp Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe @@ -2319,6 +2319,7 @@ ieee80211_recv_assoc_resp(struct ieee80211com *ic, struct mbuf *m, */ if (ic->ic_flags & IEEE80211_F_RSNON) { /* XXX ic->ic_mgt_timer = 5; */ + ni->ni_rsn_supp_state = RSNA_SUPP_PTKSTART; } else if (ic->ic_flags & IEEE80211_F_WEPON) ni->ni_flags |= IEEE80211_NODE_TXRXPROT; diff --git a/sys/net80211/ieee80211_node.h b/sys/net80211/ieee80211_node.h index 0be94e5a03c..d1b6067e255 100644 --- a/sys/net80211/ieee80211_node.h +++ b/sys/net80211/ieee80211_node.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.h,v 1.65 2017/02/02 16:47:53 stsp Exp $ */ +/* $OpenBSD: ieee80211_node.h,v 1.66 2017/03/01 19:28:48 stsp Exp $ */ /* $NetBSD: ieee80211_node.h,v 1.9 2004/04/30 22:57:32 dyoung Exp $ */ /*- @@ -99,6 +99,14 @@ enum { RSNA_KEYERROR }; +/* Supplicant state machine: 4-Way Handshake (not documented in standard) */ +enum { + RSNA_SUPP_INITIALIZE, /* not expecting any messages */ + RSNA_SUPP_PTKSTART, /* awaiting handshake message 1 */ + RSNA_SUPP_PTKNEGOTIATING, /* got message 1 and derived PTK */ + RNSA_SUPP_PTKDONE /* got message 3 and authenticated AP */ +}; + struct ieee80211_rxinfo { u_int32_t rxi_flags; u_int32_t rxi_tstamp; @@ -208,6 +216,7 @@ struct ieee80211_node { /* RSN */ struct timeout ni_eapol_to; u_int ni_rsn_state; + u_int ni_rsn_supp_state; u_int ni_rsn_gstate; u_int ni_rsn_retries; u_int ni_rsnprotos; diff --git a/sys/net80211/ieee80211_pae_input.c b/sys/net80211/ieee80211_pae_input.c index c0a7327ab60..45f5dec238a 100644 --- a/sys/net80211/ieee80211_pae_input.c +++ b/sys/net80211/ieee80211_pae_input.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_pae_input.c,v 1.26 2017/02/02 16:47:53 stsp Exp $ */ +/* $OpenBSD: ieee80211_pae_input.c,v 1.27 2017/03/01 19:28:48 stsp Exp $ */ /*- * Copyright (c) 2007,2008 Damien Bergamini <damien.bergamini@free.fr> @@ -193,6 +193,15 @@ ieee80211_recv_4way_msg1(struct ieee80211com *ic, ic->ic_opmode != IEEE80211_M_IBSS) return; #endif + /* + * Message 1 is always expected while RSN is active since some + * APs will rekey the PTK by sending Msg1/4 after some time. + */ + if (ni->ni_rsn_supp_state == RSNA_SUPP_INITIALIZE) { + DPRINTF(("unexpected in state: %d\n", ni->ni_rsn_state)); + return; + } + /* enforce monotonicity of key request replay counter */ if (ni->ni_replaycnt_ok && BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { ic->ic_stats.is_rx_eapol_replay++; @@ -343,6 +352,13 @@ ieee80211_recv_4way_msg3(struct ieee80211com *ic, ic->ic_opmode != IEEE80211_M_IBSS) return; #endif + /* discard if we're not expecting this message */ + if (ni->ni_rsn_supp_state != RSNA_SUPP_PTKNEGOTIATING && + ni->ni_rsn_supp_state != RNSA_SUPP_PTKDONE) { + DPRINTF(("unexpected in state: %d\n", ni->ni_rsn_state)); + return; + } + /* enforce monotonicity of key request replay counter */ if (ni->ni_replaycnt_ok && BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { ic->ic_stats.is_rx_eapol_replay++; @@ -737,6 +753,12 @@ ieee80211_recv_rsn_group_msg1(struct ieee80211com *ic, ic->ic_opmode != IEEE80211_M_IBSS) return; #endif + /* discard if we're not expecting this message */ + if (ni->ni_rsn_supp_state != RNSA_SUPP_PTKDONE) { + DPRINTF(("unexpected in state: %d\n", ni->ni_rsn_state)); + return; + } + /* enforce monotonicity of key request replay counter */ if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { ic->ic_stats.is_rx_eapol_replay++; return; @@ -883,6 +905,12 @@ ieee80211_recv_wpa_group_msg1(struct ieee80211com *ic, ic->ic_opmode != IEEE80211_M_IBSS) return; #endif + /* discard if we're not expecting this message */ + if (ni->ni_rsn_supp_state != RNSA_SUPP_PTKDONE) { + DPRINTF(("unexpected in state: %d\n", ni->ni_rsn_state)); + return; + } + /* enforce monotonicity of key request replay counter */ if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { ic->ic_stats.is_rx_eapol_replay++; return; @@ -975,6 +1003,7 @@ ieee80211_recv_group_msg2(struct ieee80211com *ic, ni->ni_rsn_gstate)); return; } + /* enforce monotonicity of key request replay counter */ if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt) { ic->ic_stats.is_rx_eapol_replay++; return; @@ -1023,6 +1052,11 @@ ieee80211_recv_eapol_key_req(struct ieee80211com *ic, ic->ic_opmode != IEEE80211_M_IBSS) return; + /* discard if we're not expecting this message */ + if (ni->ni_rsn_state != RSNA_PTKINITDONE) { + DPRINTF(("unexpected in state: %d\n", ni->ni_rsn_state)); + return; + } /* enforce monotonicity of key request replay counter */ if (ni->ni_reqreplaycnt_ok && BE_READ_8(key->replaycnt) <= ni->ni_reqreplaycnt) { diff --git a/sys/net80211/ieee80211_pae_output.c b/sys/net80211/ieee80211_pae_output.c index 340e9a0fa9e..d385b00ad9d 100644 --- a/sys/net80211/ieee80211_pae_output.c +++ b/sys/net80211/ieee80211_pae_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_pae_output.c,v 1.28 2016/12/17 18:36:22 stsp Exp $ */ +/* $OpenBSD: ieee80211_pae_output.c,v 1.29 2017/03/01 19:28:48 stsp Exp $ */ /*- * Copyright (c) 2007,2008 Damien Bergamini <damien.bergamini@free.fr> @@ -310,6 +310,7 @@ ieee80211_send_4way_msg2(struct ieee80211com *ic, struct ieee80211_node *ni, u_int16_t info; u_int8_t *frm; + ni->ni_rsn_supp_state = RSNA_SUPP_PTKNEGOTIATING; m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) ? 2 + IEEE80211_WPAIE_MAXLEN : @@ -438,6 +439,7 @@ ieee80211_send_4way_msg4(struct ieee80211com *ic, struct ieee80211_node *ni) struct mbuf *m; u_int16_t info; + ni->ni_rsn_supp_state = RNSA_SUPP_PTKDONE; m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0); if (m == NULL) return ENOMEM; diff --git a/sys/net80211/ieee80211_proto.c b/sys/net80211/ieee80211_proto.c index 776c2e85270..23685291d1f 100644 --- a/sys/net80211/ieee80211_proto.c +++ b/sys/net80211/ieee80211_proto.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_proto.c,v 1.72 2017/02/07 15:07:14 stsp Exp $ */ +/* $OpenBSD: ieee80211_proto.c,v 1.73 2017/03/01 19:28:48 stsp Exp $ */ /* $NetBSD: ieee80211_proto.c,v 1.8 2004/04/30 23:58:20 dyoung Exp $ */ /*- @@ -356,8 +356,8 @@ ieee80211_set_shortslottime(struct ieee80211com *ic, int on) int ieee80211_keyrun(struct ieee80211com *ic, u_int8_t *macaddr) { -#ifndef IEEE80211_STA_ONLY struct ieee80211_node *ni; +#ifndef IEEE80211_STA_ONLY struct ieee80211_pmk *pmk; #endif @@ -366,6 +366,7 @@ ieee80211_keyrun(struct ieee80211com *ic, u_int8_t *macaddr) !(ic->ic_flags & IEEE80211_F_RSNON)) return ENETDOWN; + ni->ni_rsn_supp_state = RSNA_SUPP_PTKSTART; #ifndef IEEE80211_STA_ONLY if (ic->ic_opmode == IEEE80211_M_STA) #endif @@ -731,6 +732,10 @@ ieee80211_auth_open(struct ieee80211com *ic, const struct ieee80211_frame *wh, } ieee80211_new_state(ic, IEEE80211_S_AUTH, wh->i_fc[0] & IEEE80211_FC0_SUBTYPE_MASK); + + /* In IBSS mode no (re)association frames are sent. */ + if (ic->ic_flags & IEEE80211_F_RSNON) + ni->ni_rsn_supp_state = RSNA_SUPP_PTKSTART; break; case IEEE80211_M_AHDEMO: @@ -897,6 +902,7 @@ justcleanup: ieee80211_free_allnodes(ic); break; } + ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE; break; case IEEE80211_S_SCAN: ic->ic_flags &= ~IEEE80211_F_SIBSS; @@ -907,6 +913,7 @@ justcleanup: ieee80211_chan2mode(ic, ni->ni_chan)]; ni->ni_associd = 0; ni->ni_rstamp = 0; + ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE; switch (ostate) { case IEEE80211_S_INIT: #ifndef IEEE80211_STA_ONLY @@ -949,6 +956,7 @@ justcleanup: } break; case IEEE80211_S_AUTH: + ni->ni_rsn_supp_state = RSNA_SUPP_INITIALIZE; switch (ostate) { case IEEE80211_S_INIT: DPRINTF(("invalid transition\n")); |