summaryrefslogtreecommitdiffstats
path: root/sys/net80211/ieee80211_node.c
diff options
context:
space:
mode:
authordamien <damien@openbsd.org>2008-04-16 18:32:14 +0000
committerdamien <damien@openbsd.org>2008-04-16 18:32:14 +0000
commite03e709c3958618d707b3e552e240c86776ec696 (patch)
tree628285675a56f6dfbf10669ec96247423e5c7596 /sys/net80211/ieee80211_node.c
parentpass-phrase -> passphrase, in keeping with the rest of the docs; (diff)
downloadwireguard-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.c176
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);