diff options
| author | 2008-04-16 18:32:14 +0000 | |
|---|---|---|
| committer | 2008-04-16 18:32:14 +0000 | |
| commit | e03e709c3958618d707b3e552e240c86776ec696 (patch) | |
| tree | 628285675a56f6dfbf10669ec96247423e5c7596 /sys/net80211/ieee80211_node.c | |
| parent | pass-phrase -> passphrase, in keeping with the rest of the docs; (diff) | |
| download | wireguard-openbsd-e03e709c3958618d707b3e552e240c86776ec696.tar.xz wireguard-openbsd-e03e709c3958618d707b3e552e240c86776ec696.zip | |
Kernel implementation of the 4-way handshake and group-key
handshake protocols (both supplicant and authenticator state
machines) as defined in the IEEE 802.11i standard.
Software implementation of the TKIP (Temporal Key Integrity
Protocol) and CCMP (CTR with CBC-MAC Protocol) protocols.
This diff doesn't implement any of the 802.1X authentication
protocols and thus only PSK authentication (using pre-shared
keys) is currently supported.
In concrete terms, this adds support for WPA-PSK and WPA2-PSK
protocols, both in station and hostap modes.
The following drivers are marked as WPA-capable and should
work: bwi(4), malo(4), ral(4), iwn(4), wpi(4), ural(4),
rum(4), upgt(4), and zyd(4)
The following options have been added to ifconfig(8):
wpa, wpapsk, wpaprotos, wpaakms, wpaciphers, wpagroupcipher
wpa-psk(8) can be used to generate keys from passphrases.
tested by many@
ok deraadt@
Diffstat (limited to 'sys/net80211/ieee80211_node.c')
| -rw-r--r-- | sys/net80211/ieee80211_node.c | 176 |
1 files changed, 167 insertions, 9 deletions
diff --git a/sys/net80211/ieee80211_node.c b/sys/net80211/ieee80211_node.c index 1384c998a10..53f4fe4af29 100644 --- a/sys/net80211/ieee80211_node.c +++ b/sys/net80211/ieee80211_node.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_node.c,v 1.30 2007/10/29 15:40:23 chl Exp $ */ +/* $OpenBSD: ieee80211_node.c,v 1.31 2008/04/16 18:32:15 damien Exp $ */ /* $NetBSD: ieee80211_node.c,v 1.14 2004/05/09 09:18:47 dyoung Exp $ */ /*- @@ -79,7 +79,9 @@ void ieee80211_setup_node(struct ieee80211com *, struct ieee80211_node *, void ieee80211_free_node(struct ieee80211com *, struct ieee80211_node *); struct ieee80211_node *ieee80211_alloc_node_helper(struct ieee80211com *); void ieee80211_node_cleanup(struct ieee80211com *, struct ieee80211_node *); +void ieee80211_node_join_rsn(struct ieee80211com *, struct ieee80211_node *); void ieee80211_node_join_11g(struct ieee80211com *, struct ieee80211_node *); +void ieee80211_node_leave_rsn(struct ieee80211com *, struct ieee80211_node *); void ieee80211_node_leave_11g(struct ieee80211com *, struct ieee80211_node *); void ieee80211_set_tim(struct ieee80211com *, int, int); @@ -110,7 +112,6 @@ ieee80211_node_attach(struct ifnet *ifp) printf("%s: no memory for AID bitmap!\n", __func__); ic->ic_max_aid = 0; } - if (ic->ic_caps & (IEEE80211_C_HOSTAP | IEEE80211_C_IBSS)) { ic->ic_tim_len = howmany(ic->ic_max_aid, 8); ic->ic_tim_bitmap = malloc(ic->ic_tim_len, M_DEVBUF, @@ -120,6 +121,8 @@ ieee80211_node_attach(struct ifnet *ifp) ic->ic_tim_len = 0; } else ic->ic_set_tim = ieee80211_set_tim; + timeout_set(&ic->ic_rsn_timeout, + ieee80211_gtk_rekey_timeout, ic); } } @@ -299,6 +302,36 @@ ieee80211_create_ibss(struct ieee80211com* ic, struct ieee80211_channel *chan) ni->ni_capinfo = IEEE80211_CAPINFO_IBSS; if (ic->ic_flags & IEEE80211_F_WEPON) ni->ni_capinfo |= IEEE80211_CAPINFO_PRIVACY; + if (ic->ic_flags & IEEE80211_F_RSNON) { + struct ieee80211_key *k; + u_int8_t gtk[IEEE80211_PMK_LEN]; + + /* initialize 256-bit global key counter to a random value */ + arc4random_bytes(ic->ic_globalcnt, EAPOL_KEY_NONCE_LEN); + + ni->ni_rsnprotos = ic->ic_rsnprotos; + ni->ni_rsnakms = ic->ic_rsnakms; + ni->ni_rsnciphers = ic->ic_rsnciphers; + ni->ni_rsngroupcipher = ic->ic_rsngroupcipher; + ni->ni_rsncaps = 0; + + ic->ic_def_txkey = 1; + k = &ic->ic_nw_keys[ic->ic_def_txkey]; + arc4random_bytes(gtk, sizeof(gtk)); + ieee80211_map_gtk(gtk, ni->ni_rsngroupcipher, + ic->ic_def_txkey, 1, 0, k); + (*ic->ic_set_key)(ic, ni, k); /* XXX */ + + /* + * In HostAP mode, multicast traffic is sent using ic_bss + * as the Tx node, so mark our node as valid so we can send + * multicast frames using the group key we've just configured. + */ + ni->ni_port_valid = 1; + + /* schedule a GTK rekeying after 3600s */ + timeout_add(&ic->ic_rsn_timeout, 3600 * hz); + } if (ic->ic_phytype == IEEE80211_T_FH) { ni->ni_fhdwell = 200; /* XXX */ ni->ni_fhindex = 1; @@ -325,14 +358,14 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) if ((ni->ni_capinfo & IEEE80211_CAPINFO_ESS) == 0) fail |= 0x02; } - if (ic->ic_flags & IEEE80211_F_WEPON) { + if (ic->ic_flags & (IEEE80211_F_WEPON | IEEE80211_F_RSNON)) { if ((ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) == 0) fail |= 0x04; } else { - /* XXX does this mean privacy is supported or required? */ if (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) fail |= 0x04; } + rate = ieee80211_fix_rate(ic, ni, IEEE80211_F_DONEGO); if (rate & IEEE80211_RATE_BASIC) fail |= 0x08; @@ -343,6 +376,24 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) if ((ic->ic_flags & IEEE80211_F_DESBSSID) && !IEEE80211_ADDR_EQ(ic->ic_des_bssid, ni->ni_bssid)) fail |= 0x20; + + if (ic->ic_flags & IEEE80211_F_RSNON) { + /* + * If at least one RSN IE field from the AP's RSN IE fails + * to overlap with any value the STA supports, the STA shall + * decline to associate with that AP. + */ + if ((ni->ni_rsnprotos & ic->ic_rsnprotos) == 0) + fail |= 0x40; + if ((ni->ni_rsnakms & ic->ic_rsnakms) == 0) + fail |= 0x40; + if ((ni->ni_rsnakms & ic->ic_rsnakms) == IEEE80211_AKM_PSK && + !(ic->ic_flags & IEEE80211_F_PSK)) + fail |= 0x40; + if ((ni->ni_rsnciphers & ic->ic_rsnciphers) == 0) + fail |= 0x40; + } + #ifdef IEEE80211_DEBUG if (ic->ic_if.if_flags & IFF_DEBUG) { printf(" %c %s", fail ? '-' : '+', @@ -359,10 +410,14 @@ ieee80211_match_bss(struct ieee80211com *ic, struct ieee80211_node *ni) (ni->ni_capinfo & IEEE80211_CAPINFO_IBSS) ? "ibss" : "????", fail & 0x02 ? '!' : ' '); - printf(" %3s%c ", + printf(" %7s%c ", (ni->ni_capinfo & IEEE80211_CAPINFO_PRIVACY) ? - "wep" : "no", + "privacy" : "no", fail & 0x04 ? '!' : ' '); + printf(" %3s%c ", + (ic->ic_flags & IEEE80211_F_RSNON) ? + "rsn" : "no", + fail & 0x40 ? '!' : ' '); ieee80211_print_essid(ni->ni_essid, ni->ni_esslen); printf("%s\n", fail & 0x10 ? "!" : ""); } @@ -472,20 +527,51 @@ ieee80211_end_scan(struct ifnet *ifp) if (selbs == NULL) goto notfound; (*ic->ic_node_copy)(ic, ic->ic_bss, selbs); + ni = ic->ic_bss; /* * Set the erp state (mostly the slot time) to deal with * the auto-select case; this should be redundant if the * mode is locked. */ - ic->ic_curmode = ieee80211_chan2mode(ic, selbs->ni_chan); + ic->ic_curmode = ieee80211_chan2mode(ic, ni->ni_chan); ieee80211_reset_erp(ic); + if (ic->ic_flags & IEEE80211_F_RSNON) { + /* prefer RSN (WPA2) over WPA */ + ni->ni_rsnprotos &= ic->ic_rsnprotos; + if (ni->ni_rsnprotos & IEEE80211_PROTO_RSN) + ni->ni_rsnprotos = IEEE80211_PROTO_RSN; + else + ni->ni_rsnprotos = IEEE80211_PROTO_WPA; + /* + * If a pre-shared key is configured and AP supports PSK, + * choose PSK as AKMP. + */ + ni->ni_rsnakms &= ic->ic_rsnakms; + if ((ni->ni_rsnakms & IEEE80211_AKM_PSK) && + (ic->ic_flags & IEEE80211_F_PSK)) + ni->ni_rsnakms = IEEE80211_AKM_PSK; + else + ni->ni_rsnakms = IEEE80211_AKM_IEEE8021X; + + /* prefer CCMP over TKIP if the AP supports it */ + ni->ni_rsnciphers &= ic->ic_rsnciphers; + if (ni->ni_rsnciphers & IEEE80211_CIPHER_CCMP) + ni->ni_rsnciphers = IEEE80211_CIPHER_CCMP; + else + ni->ni_rsnciphers = IEEE80211_CIPHER_TKIP; + + ni->ni_rsncipher = ni->ni_rsnciphers; + + } else if (ic->ic_flags & IEEE80211_F_WEPON) + ni->ni_rsncipher = IEEE80211_CIPHER_USEGROUP; + ieee80211_node_newstate(selbs, IEEE80211_STA_BSS); if (ic->ic_opmode == IEEE80211_M_IBSS) { - ieee80211_fix_rate(ic, ic->ic_bss, IEEE80211_F_DOFRATE | + ieee80211_fix_rate(ic, ni, IEEE80211_F_DOFRATE | IEEE80211_F_DONEGO | IEEE80211_F_DODEL); - if (ic->ic_bss->ni_rates.rs_nrates == 0) + if (ni->ni_rates.rs_nrates == 0) goto notfound; ieee80211_new_state(ic, IEEE80211_S_RUN, -1); } else { @@ -529,6 +615,10 @@ ieee80211_node_alloc(struct ieee80211com *ic) void ieee80211_node_cleanup(struct ieee80211com *ic, struct ieee80211_node *ni) { + if (ni->ni_rsnie != NULL) { + free(ni->ni_rsnie, M_DEVBUF); + ni->ni_rsnie = NULL; + } } void @@ -544,6 +634,9 @@ ieee80211_node_copy(struct ieee80211com *ic, { ieee80211_node_cleanup(ic, dst); *dst = *src; + dst->ni_rsnie = NULL; + if (src->ni_rsnie != NULL) + ieee80211_save_ie(src->ni_rsnie, &dst->ni_rsnie); } u_int8_t @@ -564,6 +657,8 @@ ieee80211_setup_node(struct ieee80211com *ic, IEEE80211_ADDR_COPY(ni->ni_macaddr, macaddr); ieee80211_node_newstate(ni, IEEE80211_STA_CACHE); + ni->ni_ic = ic; /* back-pointer */ + /* * Note we don't enable the inactive timer when acting * as a station. Nodes created in this mode represent @@ -945,6 +1040,37 @@ ieee80211_iserp_sta(const struct ieee80211_node *ni) } /* + * Handle a station joining an RSN network. + */ +void +ieee80211_node_join_rsn(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + IEEE80211_DPRINTF(("station %s associated using proto %d akm 0x%x " + "cipher 0x%x groupcipher 0x%x\n", ether_sprintf(ni->ni_macaddr), + ni->ni_rsnprotos, ni->ni_rsnakms, ni->ni_rsnciphers, + ni->ni_rsngroupcipher)); + + ni->ni_rsn_state = RSNA_AUTHENTICATION; + ic->ic_rsnsta++; + + ni->ni_key_count = 0; + ni->ni_port_valid = 0; + ni->ni_replaycnt = -1; /* XXX */ + ni->ni_rsn_retries = 0; + ni->ni_rsncipher = ni->ni_rsnciphers; + timeout_set(&ni->ni_rsn_timeout, ieee80211_eapol_timeout, ni); + + ni->ni_rsn_state = RSNA_AUTHENTICATION_2; + + /* generate a new authenticator nonce (ANonce) */ + arc4random_bytes(ni->ni_nonce, EAPOL_KEY_NONCE_LEN); + + /* initiate 4-way handshake */ + if (ni->ni_rsnakms == IEEE80211_AKM_PSK) + (void)ieee80211_send_4way_msg1(ic, ni); +} + +/* * Handle a station joining an 11g network. */ void @@ -1032,6 +1158,12 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, IEEE80211_SEND_MGMT(ic, ni, resp, IEEE80211_STATUS_SUCCESS); ieee80211_node_newstate(ni, IEEE80211_STA_ASSOC); + if (!(ic->ic_flags & IEEE80211_F_RSNON)) { + ni->ni_port_valid = 1; + ni->ni_rsncipher = IEEE80211_CIPHER_USEGROUP; + } else + ieee80211_node_join_rsn(ic, ni); + #if NBRIDGE > 0 /* * If the parent interface belongs to a bridge, learn @@ -1044,6 +1176,29 @@ ieee80211_node_join(struct ieee80211com *ic, struct ieee80211_node *ni, } /* + * Handle a station leaving an RSN network. + */ +void +ieee80211_node_leave_rsn(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + ni->ni_rsn_state = RSNA_DISCONNECTED; + ic->ic_rsnsta--; + + ni->ni_rsn_state = RSNA_INITIALIZE; + if ((ni->ni_flags & IEEE80211_NODE_REKEY) && + --ic->ic_rsn_keydonesta == 0) + ieee80211_setkeysdone(ic); + ni->ni_flags &= ~IEEE80211_NODE_REKEY; + + ni->ni_rsn_gstate = RSNA_IDLE; + + timeout_del(&ni->ni_rsn_timeout); + ni->ni_rsn_retries = 0; + ni->ni_port_valid = 0; + (*ic->ic_delete_key)(ic, ni, &ni->ni_pairwise_key); +} + +/* * Handle a station leaving an 11g network. */ void @@ -1114,6 +1269,9 @@ ieee80211_node_leave(struct ieee80211com *ic, struct ieee80211_node *ni) IEEE80211_AID_CLR(ni->ni_associd, ic->ic_aid_bitmap); ni->ni_associd = 0; + if (ic->ic_flags & IEEE80211_F_RSNON) + ieee80211_node_leave_rsn(ic, ni); + if (ic->ic_curmode == IEEE80211_MODE_11G) ieee80211_node_leave_11g(ic, ni); |
