From: damien Date: Mon, 21 Jul 2008 19:05:21 +0000 (+0000) Subject: move processing of EAPOL frames away from ieee80211_{input,output}.c X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=278155999e6f15d168351222554fd6cb51f90669;p=openbsd move processing of EAPOL frames away from ieee80211_{input,output}.c --- diff --git a/sys/conf/files b/sys/conf/files index d599c5de112..95a4f23fd4a 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1,4 +1,4 @@ -# $OpenBSD: files,v 1.437 2008/07/08 05:22:00 dlg Exp $ +# $OpenBSD: files,v 1.438 2008/07/21 19:05:21 damien Exp $ # $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 @@ -793,6 +793,8 @@ file net80211/ieee80211_input.c wlan file net80211/ieee80211_ioctl.c wlan file net80211/ieee80211_node.c wlan file net80211/ieee80211_output.c wlan +file net80211/ieee80211_pae_input.c wlan +file net80211/ieee80211_pae_output.c wlan file net80211/ieee80211_proto.c wlan file net80211/ieee80211_rssadapt.c wlan file net80211/ieee80211_regdomain.c wlan diff --git a/sys/net80211/ieee80211_input.c b/sys/net80211/ieee80211_input.c index de5744fc04d..0fa629c0a33 100644 --- a/sys/net80211/ieee80211_input.c +++ b/sys/net80211/ieee80211_input.c @@ -1,5 +1,5 @@ /* $NetBSD: ieee80211_input.c,v 1.24 2004/05/31 11:12:24 dyoung Exp $ */ -/* $OpenBSD: ieee80211_input.c,v 1.81 2008/07/21 18:43:18 damien Exp $ */ +/* $OpenBSD: ieee80211_input.c,v 1.82 2008/07/21 19:05:21 damien Exp $ */ /*- * Copyright (c) 2001 Atsushi Onoe @@ -62,8 +62,6 @@ #include -#include - int ieee80211_parse_edca_params_body(struct ieee80211com *, const u_int8_t *); int ieee80211_parse_edca_params(struct ieee80211com *, const u_int8_t *); @@ -72,10 +70,6 @@ enum ieee80211_cipher ieee80211_parse_rsn_cipher(const u_int8_t[]); enum ieee80211_akm ieee80211_parse_rsn_akm(const u_int8_t[]); int ieee80211_parse_rsn_body(struct ieee80211com *, const u_int8_t *, u_int, struct ieee80211_rsnparams *); -int ieee80211_parse_rsn(struct ieee80211com *, const u_int8_t *, - struct ieee80211_rsnparams *); -int ieee80211_parse_wpa(struct ieee80211com *, const u_int8_t *, - struct ieee80211_rsnparams *); int ieee80211_save_ie(const u_int8_t *, u_int8_t **); void ieee80211_recv_pspoll(struct ieee80211com *, struct mbuf *); void ieee80211_recv_probe_resp(struct ieee80211com *, struct mbuf *, @@ -94,25 +88,6 @@ void ieee80211_recv_disassoc(struct ieee80211com *, struct mbuf *, struct ieee80211_node *); void ieee80211_recv_action(struct ieee80211com *, struct mbuf *, struct ieee80211_node *); -void ieee80211_recv_4way_msg1(struct ieee80211com *, - struct ieee80211_eapol_key *, struct ieee80211_node *); -void ieee80211_recv_4way_msg2(struct ieee80211com *, - struct ieee80211_eapol_key *, struct ieee80211_node *, - const u_int8_t *); -void ieee80211_recv_4way_msg3(struct ieee80211com *, - struct ieee80211_eapol_key *, struct ieee80211_node *); -void ieee80211_recv_4way_msg4(struct ieee80211com *, - struct ieee80211_eapol_key *, struct ieee80211_node *); -void ieee80211_recv_4way_msg2or4(struct ieee80211com *, - struct ieee80211_eapol_key *, struct ieee80211_node *); -void ieee80211_recv_rsn_group_msg1(struct ieee80211com *, - struct ieee80211_eapol_key *, struct ieee80211_node *); -void ieee80211_recv_wpa_group_msg1(struct ieee80211com *, - struct ieee80211_eapol_key *, struct ieee80211_node *); -void ieee80211_recv_group_msg2(struct ieee80211com *, - struct ieee80211_eapol_key *, struct ieee80211_node *); -void ieee80211_recv_eapol_key_req(struct ieee80211com *, - struct ieee80211_eapol_key *, struct ieee80211_node *); /* * Retrieve the length in bytes of a 802.11 header. @@ -1889,861 +1864,6 @@ ieee80211_recv_mgmt(struct ieee80211com *ic, struct mbuf *m0, } } -/* unaligned big endian access */ -#define BE_READ_2(p) \ - ((u_int16_t)(p)[0] << 8 | (u_int16_t)(p)[1]) - -#define BE_READ_8(p) \ - ((u_int64_t)(p)[0] << 56 | (u_int64_t)(p)[1] << 48 | \ - (u_int64_t)(p)[2] << 40 | (u_int64_t)(p)[3] << 32 | \ - (u_int64_t)(p)[4] << 24 | (u_int64_t)(p)[5] << 16 | \ - (u_int64_t)(p)[6] << 8 | (u_int64_t)(p)[7]) - -/* unaligned little endian access */ -#define LE_READ_6(p) \ - ((u_int64_t)(p)[5] << 40 | (u_int64_t)(p)[4] << 32 | \ - (u_int64_t)(p)[3] << 24 | (u_int64_t)(p)[2] << 16 | \ - (u_int64_t)(p)[1] << 8 | (u_int64_t)(p)[0]) - -/* - * 4-Way Handshake Message 1 is sent by the authenticator to the supplicant - * (see 8.5.3.1). - */ -void -ieee80211_recv_4way_msg1(struct ieee80211com *ic, - struct ieee80211_eapol_key *key, struct ieee80211_node *ni) -{ - struct ieee80211_ptk tptk; - const u_int8_t *frm, *efrm; - const u_int8_t *pmkid; - const u_int8_t *pmk; - - if (ic->ic_opmode != IEEE80211_M_STA && - ic->ic_opmode != IEEE80211_M_IBSS) - return; - - if (ni->ni_replaycnt_ok && - BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { - ic->ic_stats.is_rx_eapol_replay++; - return; - } - /* save authenticator's nonce (ANonce) */ - memcpy(ni->ni_nonce, key->nonce, EAPOL_KEY_NONCE_LEN); - - /* parse key data field (may contain an encapsulated PMKID) */ - frm = (const u_int8_t *)&key[1]; - efrm = frm + BE_READ_2(key->paylen); - - pmkid = NULL; - while (frm + 2 <= efrm) { - if (frm + 2 + frm[1] > efrm) - break; - switch (frm[0]) { - case IEEE80211_ELEMID_VENDOR: - if (frm[1] < 4) - break; - if (memcmp(&frm[2], IEEE80211_OUI, 3) == 0) { - switch (frm[5]) { - case IEEE80211_KDE_PMKID: - pmkid = frm; - break; - } - } - break; - } - frm += 2 + frm[1]; - } - /* check that the PMKID KDE is valid (if present) */ - if (pmkid != NULL && pmkid[1] < 4 + 16) - return; - - /* generate a new supplicant's nonce (SNonce) */ - arc4random_buf(ic->ic_nonce, EAPOL_KEY_NONCE_LEN); - - /* retrieve PMK and derive TPTK */ - if ((pmk = ieee80211_get_pmk(ic, ni, pmkid)) == NULL) { - /* no PMK configured for this STA/PMKID */ - return; - } - ieee80211_derive_ptk(pmk, IEEE80211_PMK_LEN, ni->ni_macaddr, - ic->ic_myaddr, key->nonce, ic->ic_nonce, (u_int8_t *)&tptk, - sizeof(tptk)); - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: received msg %d/%d of the %s handshake from %s\n", - ic->ic_if.if_xname, 1, 4, "4-way", - ether_sprintf(ni->ni_macaddr)); - - /* send message 2 to authenticator using TPTK */ - (void)ieee80211_send_4way_msg2(ic, ni, key->replaycnt, &tptk); -} - -/* - * 4-Way Handshake Message 2 is sent by the supplicant to the authenticator - * (see 8.5.3.2). - */ -void -ieee80211_recv_4way_msg2(struct ieee80211com *ic, - struct ieee80211_eapol_key *key, struct ieee80211_node *ni, - const u_int8_t *rsnie) -{ - struct ieee80211_ptk tptk; - const u_int8_t *pmk; - - if (ic->ic_opmode != IEEE80211_M_HOSTAP && - ic->ic_opmode != IEEE80211_M_IBSS) - return; - - /* discard if we're not expecting this message */ - if (ni->ni_rsn_state != RSNA_PTKSTART && - ni->ni_rsn_state != RSNA_PTKCALCNEGOTIATING) { - IEEE80211_DPRINTF(("%s: unexpected in state: %d\n", - __func__, ni->ni_rsn_state)); - return; - } - ni->ni_rsn_state = RSNA_PTKCALCNEGOTIATING; - - /* replay counter has already been verified by caller */ - - /* retrieve PMK and derive TPTK */ - if ((pmk = ieee80211_get_pmk(ic, ni, NULL)) == NULL) { - /* no PMK configured for this STA */ - return; /* will timeout.. */ - } - ieee80211_derive_ptk(pmk, IEEE80211_PMK_LEN, ic->ic_myaddr, - ni->ni_macaddr, ni->ni_nonce, key->nonce, (u_int8_t *)&tptk, - sizeof(tptk)); - - /* check Key MIC field using KCK */ - if (ieee80211_eapol_key_check_mic(key, tptk.kck) != 0) { - IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); - ic->ic_stats.is_rx_eapol_badmic++; - return; /* will timeout.. */ - } - - timeout_del(&ni->ni_rsn_timeout); - ni->ni_rsn_state = RSNA_PTKCALCNEGOTIATING_2; - ni->ni_rsn_retries = 0; - - /* install TPTK as PTK now that MIC is verified */ - memcpy(&ni->ni_ptk, &tptk, sizeof(tptk)); - - /* - * The RSN IE must match bit-wise with what the STA included in its - * (Re)Association Request. - */ - if (ni->ni_rsnie == NULL || rsnie[1] != ni->ni_rsnie[1] || - memcmp(rsnie, ni->ni_rsnie, 2 + rsnie[1]) != 0) { - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_RSN_DIFFERENT_IE); - ieee80211_node_leave(ic, ni); - return; - } - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: received msg %d/%d of the %s handshake from %s\n", - ic->ic_if.if_xname, 2, 4, "4-way", - ether_sprintf(ni->ni_macaddr)); - - /* send message 3 to supplicant */ - (void)ieee80211_send_4way_msg3(ic, ni); -} - -/* - * 4-Way Handshake Message 3 is sent by the authenticator to the supplicant - * (see 8.5.3.3). - */ -void -ieee80211_recv_4way_msg3(struct ieee80211com *ic, - struct ieee80211_eapol_key *key, struct ieee80211_node *ni) -{ - struct ieee80211_ptk tptk; - struct ieee80211_key *k; - const u_int8_t *frm, *efrm; - const u_int8_t *rsnie1, *rsnie2, *gtk; - const u_int8_t *pmk; - u_int16_t info, reason = 0; - - if (ic->ic_opmode != IEEE80211_M_STA && - ic->ic_opmode != IEEE80211_M_IBSS) - return; - - if (ni->ni_replaycnt_ok && - BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { - ic->ic_stats.is_rx_eapol_replay++; - return; - } - - /* check that ANonce matches that of message 1 */ - if (memcmp(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN) != 0) { - IEEE80211_DPRINTF(("%s: ANonce does not match msg 1/4\n", - __func__)); - return; - } - /* retrieve PMK and derive TPTK */ - if ((pmk = ieee80211_get_pmk(ic, ni, NULL)) == NULL) { - /* no PMK configured for this STA */ - return; - } - ieee80211_derive_ptk(pmk, IEEE80211_PMK_LEN, ni->ni_macaddr, - ic->ic_myaddr, key->nonce, ic->ic_nonce, (u_int8_t *)&tptk, - sizeof(tptk)); - - info = BE_READ_2(key->info); - - /* check Key MIC field using KCK */ - if (ieee80211_eapol_key_check_mic(key, tptk.kck) != 0) { - IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); - ic->ic_stats.is_rx_eapol_badmic++; - return; - } - /* install TPTK as PTK now that MIC is verified */ - memcpy(&ni->ni_ptk, &tptk, sizeof(tptk)); - - /* if encrypted, decrypt Key Data field using KEK */ - if ((info & EAPOL_KEY_ENCRYPTED) && - ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0) { - IEEE80211_DPRINTF(("%s: decryption failed\n", __func__)); - return; - } - - /* parse key data field */ - frm = (const u_int8_t *)&key[1]; - efrm = frm + BE_READ_2(key->paylen); - - /* - * Some WPA1+WPA2 APs (like hostapd) appear to include both WPA and - * RSN IEs in message 3/4. We only take into account the IE of the - * version of the protocol we negotiated at association time. - */ - rsnie1 = rsnie2 = gtk = NULL; - while (frm + 2 <= efrm) { - if (frm + 2 + frm[1] > efrm) - break; - switch (frm[0]) { - case IEEE80211_ELEMID_RSN: - if (ni->ni_rsnprotos != IEEE80211_PROTO_RSN) - break; - if (rsnie1 == NULL) - rsnie1 = frm; - else if (rsnie2 == NULL) - rsnie2 = frm; - /* ignore others if more than two RSN IEs */ - break; - case IEEE80211_ELEMID_VENDOR: - if (frm[1] < 4) - break; - if (memcmp(&frm[2], IEEE80211_OUI, 3) == 0) { - switch (frm[5]) { - case IEEE80211_KDE_GTK: - gtk = frm; - break; - } - } else if (memcmp(&frm[2], MICROSOFT_OUI, 3) == 0) { - switch (frm[5]) { - case 1: /* WPA */ - if (ni->ni_rsnprotos != - IEEE80211_PROTO_WPA) - break; - rsnie1 = frm; - break; - } - } - break; - } - frm += 2 + frm[1]; - } - /* first WPA/RSN IE is mandatory */ - if (rsnie1 == NULL) { - IEEE80211_DPRINTF(("%s: missing RSN IE\n", __func__)); - return; - } - /* key data must be encrypted if GTK is included */ - if (gtk != NULL && !(info & EAPOL_KEY_ENCRYPTED)) { - IEEE80211_DPRINTF(("%s: GTK not encrypted\n", __func__)); - return; - } - /* - * Check that first WPA/RSN IE is identical to the one received in - * the beacon or probe response frame. - */ - if (ni->ni_rsnie == NULL || rsnie1[1] != ni->ni_rsnie[1] || - memcmp(rsnie1, ni->ni_rsnie, 2 + rsnie1[1]) != 0) { - reason = IEEE80211_REASON_RSN_DIFFERENT_IE; - goto deauth; - } - - /* - * If a second RSN information element is present, use its pairwise - * cipher suite or deauthenticate. - */ - if (rsnie2 != NULL) { - struct ieee80211_rsnparams rsn; - - if (ieee80211_parse_rsn(ic, rsnie2, &rsn) == 0) { - if (rsn.rsn_akms != ni->ni_rsnakms || - rsn.rsn_groupcipher != ni->ni_rsngroupcipher || - rsn.rsn_nciphers != 1 || - !(rsn.rsn_ciphers & ic->ic_rsnciphers)) { - reason = IEEE80211_REASON_BAD_PAIRWISE_CIPHER; - goto deauth; - } - /* use pairwise cipher suite of second RSN IE */ - ni->ni_rsnciphers = rsn.rsn_ciphers; - ni->ni_rsncipher = ni->ni_rsnciphers; - } - } - - /* update the last seen value of the key replay counter field */ - ni->ni_replaycnt = BE_READ_8(key->replaycnt); - ni->ni_replaycnt_ok = 1; - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: received msg %d/%d of the %s handshake from %s\n", - ic->ic_if.if_xname, 3, 4, "4-way", - ether_sprintf(ni->ni_macaddr)); - - /* send message 4 to authenticator */ - if (ieee80211_send_4way_msg4(ic, ni) != 0) - return; /* ..authenticator will retry */ - - if (info & EAPOL_KEY_INSTALL) { - u_int64_t prsc; - - /* check that key length matches that of pairwise cipher */ - if (BE_READ_2(key->keylen) != - ieee80211_cipher_keylen(ni->ni_rsncipher)) { - reason = IEEE80211_REASON_AUTH_LEAVE; - goto deauth; - } - /* install the PTK */ - prsc = (gtk == NULL) ? LE_READ_6(key->rsc) : 0; - k = &ni->ni_pairwise_key; - ieee80211_map_ptk(&ni->ni_ptk, ni->ni_rsncipher, prsc, k); - if ((*ic->ic_set_key)(ic, ni, k) != 0) { - reason = IEEE80211_REASON_AUTH_LEAVE; - goto deauth; - } - } - if (gtk != NULL) { - u_int64_t rsc; - u_int8_t kid; - - /* check that the GTK KDE is valid */ - if (gtk[1] < 4 + 2) { - reason = IEEE80211_REASON_AUTH_LEAVE; - goto deauth; - } - /* check that key length matches that of group cipher */ - if (gtk[1] - 6 != - ieee80211_cipher_keylen(ni->ni_rsngroupcipher)) { - reason = IEEE80211_REASON_AUTH_LEAVE; - goto deauth; - } - /* install the GTK */ - kid = gtk[6] & 3; - rsc = LE_READ_6(key->rsc); - k = &ic->ic_nw_keys[kid]; - ieee80211_map_gtk(>k[8], ni->ni_rsngroupcipher, kid, - gtk[6] & (1 << 2), rsc, k); - if ((*ic->ic_set_key)(ic, ni, k) != 0) { - reason = IEEE80211_REASON_AUTH_LEAVE; - goto deauth; - } - } - if (info & EAPOL_KEY_SECURE) { - if (ic->ic_opmode != IEEE80211_M_IBSS || - ++ni->ni_key_count == 2) { - IEEE80211_DPRINTF(("%s: marking port %s valid\n", - __func__, ether_sprintf(ni->ni_macaddr))); - ni->ni_port_valid = 1; - } - } - deauth: - if (reason != 0) { - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, - reason); - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - } -} - -/* - * 4-Way Handshake Message 4 is sent by the supplicant to the authenticator - * (see 8.5.3.4). - */ -void -ieee80211_recv_4way_msg4(struct ieee80211com *ic, - struct ieee80211_eapol_key *key, struct ieee80211_node *ni) -{ - if (ic->ic_opmode != IEEE80211_M_HOSTAP && - ic->ic_opmode != IEEE80211_M_IBSS) - return; - - /* discard if we're not expecting this message */ - if (ni->ni_rsn_state != RSNA_PTKINITNEGOTIATING) { - IEEE80211_DPRINTF(("%s: unexpected in state: %d\n", - __func__, ni->ni_rsn_state)); - return; - } - - /* replay counter has already been verified by caller */ - - /* check Key MIC field using KCK */ - if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) { - IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); - ic->ic_stats.is_rx_eapol_badmic++; - return; /* will timeout.. */ - } - - timeout_del(&ni->ni_rsn_timeout); - ni->ni_rsn_state = RSNA_PTKINITDONE; - ni->ni_rsn_retries = 0; - - if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP) { - /* install the PTK */ - struct ieee80211_key *k = &ni->ni_pairwise_key; - ieee80211_map_ptk(&ni->ni_ptk, ni->ni_rsncipher, 0, k); - if ((*ic->ic_set_key)(ic, ni, k) != 0) { - IEEE80211_SEND_MGMT(ic, ni, - IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_ASSOC_TOOMANY); - ieee80211_node_leave(ic, ni); - return; - } - } - if (ic->ic_opmode != IEEE80211_M_IBSS || ++ni->ni_key_count == 2) { - IEEE80211_DPRINTF(("%s: marking port %s valid\n", __func__, - ether_sprintf(ni->ni_macaddr))); - ni->ni_port_valid = 1; - } - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: received msg %d/%d of the %s handshake from %s\n", - ic->ic_if.if_xname, 4, 4, "4-way", - ether_sprintf(ni->ni_macaddr)); - - /* initiate a group key handshake for WPA */ - if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) - (void)ieee80211_send_group_msg1(ic, ni); - else - ni->ni_rsn_gstate = RSNA_IDLE; -} - -/* - * Differentiate Message 2 from Message 4 of the 4-Way Handshake based on - * the presence of an RSN or WPA Information Element. - */ -void -ieee80211_recv_4way_msg2or4(struct ieee80211com *ic, - struct ieee80211_eapol_key *key, struct ieee80211_node *ni) -{ - const u_int8_t *frm, *efrm; - const u_int8_t *rsnie; - - if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt) { - ic->ic_stats.is_rx_eapol_replay++; - return; - } - - /* parse key data field (check if an RSN IE is present) */ - frm = (const u_int8_t *)&key[1]; - efrm = frm + BE_READ_2(key->paylen); - - rsnie = NULL; - while (frm + 2 <= efrm) { - if (frm + 2 + frm[1] > efrm) - break; - switch (frm[0]) { - case IEEE80211_ELEMID_RSN: - rsnie = frm; - break; - case IEEE80211_ELEMID_VENDOR: - if (frm[1] < 4) - break; - if (memcmp(&frm[2], MICROSOFT_OUI, 3) == 0) { - switch (frm[5]) { - case 1: /* WPA */ - rsnie = frm; - break; - } - } - } - frm += 2 + frm[1]; - } - if (rsnie != NULL) - ieee80211_recv_4way_msg2(ic, key, ni, rsnie); - else - ieee80211_recv_4way_msg4(ic, key, ni); -} - -/* - * Group Key Handshake Message 1 is sent by the authenticator to the - * supplicant (see 8.5.4.1). - */ -void -ieee80211_recv_rsn_group_msg1(struct ieee80211com *ic, - struct ieee80211_eapol_key *key, struct ieee80211_node *ni) -{ - struct ieee80211_key *k; - const u_int8_t *frm, *efrm; - const u_int8_t *gtk; - u_int64_t rsc; - u_int16_t info; - u_int8_t kid; - - if (ic->ic_opmode != IEEE80211_M_STA && - ic->ic_opmode != IEEE80211_M_IBSS) - return; - - if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { - ic->ic_stats.is_rx_eapol_replay++; - return; - } - /* check Key MIC field using KCK */ - if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) { - IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); - ic->ic_stats.is_rx_eapol_badmic++; - return; - } - info = BE_READ_2(key->info); - - /* check that encrypted and decrypt Key Data field using KEK */ - if (!(info & EAPOL_KEY_ENCRYPTED) || - ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0) { - IEEE80211_DPRINTF(("%s: decryption failed\n", __func__)); - return; - } - - /* parse key data field (shall contain a GTK KDE) */ - frm = (const u_int8_t *)&key[1]; - efrm = frm + BE_READ_2(key->paylen); - - gtk = NULL; - while (frm + 2 <= efrm) { - if (frm + 2 + frm[1] > efrm) - break; - switch (frm[0]) { - case IEEE80211_ELEMID_VENDOR: - if (frm[1] < 4) - break; - if (memcmp(&frm[2], IEEE80211_OUI, 3) == 0) { - switch (frm[5]) { - case IEEE80211_KDE_GTK: - gtk = frm; - break; - } - } - break; - } - frm += 2 + frm[1]; - } - /* check that the GTK KDE is present and valid */ - if (gtk == NULL || gtk[1] < 4 + 2) { - IEEE80211_DPRINTF(("%s: missing or invalid GTK KDE\n", - __func__)); - return; - } - - /* check that key length matches that of group cipher */ - if (gtk[1] - 6 != ieee80211_cipher_keylen(ni->ni_rsngroupcipher)) - return; - - /* install the GTK */ - kid = gtk[6] & 3; - rsc = LE_READ_6(key->rsc); - k = &ic->ic_nw_keys[kid]; - ieee80211_map_gtk(>k[8], ni->ni_rsngroupcipher, kid, - gtk[6] & (1 << 2), rsc, k); - if ((*ic->ic_set_key)(ic, ni, k) != 0) { - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_AUTH_LEAVE); - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - return; - } - if (info & EAPOL_KEY_SECURE) { - if (ic->ic_opmode != IEEE80211_M_IBSS || - ++ni->ni_key_count == 2) { - IEEE80211_DPRINTF(("%s: marking port %s valid\n", - __func__, ether_sprintf(ni->ni_macaddr))); - ni->ni_port_valid = 1; - } - } - /* update the last seen value of the key replay counter field */ - ni->ni_replaycnt = BE_READ_8(key->replaycnt); - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: received msg %d/%d of the %s handshake from %s\n", - ic->ic_if.if_xname, 1, 2, "group key", - ether_sprintf(ni->ni_macaddr)); - - /* send message 2 to authenticator */ - (void)ieee80211_send_group_msg2(ic, ni, k); -} - -void -ieee80211_recv_wpa_group_msg1(struct ieee80211com *ic, - struct ieee80211_eapol_key *key, struct ieee80211_node *ni) -{ - struct ieee80211_key *k; - const u_int8_t *frm; - u_int64_t rsc; - u_int16_t info; - u_int8_t kid; - int keylen; - - if (ic->ic_opmode != IEEE80211_M_STA && - ic->ic_opmode != IEEE80211_M_IBSS) - return; - - if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { - ic->ic_stats.is_rx_eapol_replay++; - return; - } - /* check Key MIC field using KCK */ - if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) { - IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); - ic->ic_stats.is_rx_eapol_badmic++; - return; - } - /* - * EAPOL-Key data field is encrypted even though WPA doesn't set - * the ENCRYPTED bit in the info field. - */ - if (ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0) { - IEEE80211_DPRINTF(("%s: decryption failed\n", __func__)); - return; - } - info = BE_READ_2(key->info); - keylen = ieee80211_cipher_keylen(ni->ni_rsngroupcipher); - - /* check that key length matches that of group cipher */ - if (BE_READ_2(key->keylen) != keylen) - return; - - /* check that the data length is large enough to hold the key */ - if (BE_READ_2(key->paylen) < keylen) - return; - - /* key data field contains the GTK */ - frm = (const u_int8_t *)&key[1]; - - /* install the GTK */ - kid = (info >> EAPOL_KEY_WPA_KID_SHIFT) & 3; - rsc = LE_READ_6(key->rsc); - k = &ic->ic_nw_keys[kid]; - ieee80211_map_gtk(frm, ni->ni_rsngroupcipher, kid, - info & EAPOL_KEY_WPA_TX, rsc, k); - if ((*ic->ic_set_key)(ic, ni, k) != 0) { - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_AUTH_LEAVE); - ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); - return; - } - if (info & EAPOL_KEY_SECURE) { - if (ic->ic_opmode != IEEE80211_M_IBSS || - ++ni->ni_key_count == 2) { - IEEE80211_DPRINTF(("%s: marking port %s valid\n", - __func__, ether_sprintf(ni->ni_macaddr))); - ni->ni_port_valid = 1; - } - } - /* update the last seen value of the key replay counter field */ - ni->ni_replaycnt = BE_READ_8(key->replaycnt); - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: received msg %d/%d of the %s handshake from %s\n", - ic->ic_if.if_xname, 1, 2, "group key", - ether_sprintf(ni->ni_macaddr)); - - /* send message 2 to authenticator */ - (void)ieee80211_send_group_msg2(ic, ni, k); -} - -/* - * Group Key Handshake Message 2 is sent by the supplicant to the - * authenticator (see 8.5.4.2). - */ -void -ieee80211_recv_group_msg2(struct ieee80211com *ic, - struct ieee80211_eapol_key *key, struct ieee80211_node *ni) -{ - if (ic->ic_opmode != IEEE80211_M_HOSTAP && - ic->ic_opmode != IEEE80211_M_IBSS) - return; - - /* discard if we're not expecting this message */ - if (ni->ni_rsn_gstate != RSNA_REKEYNEGOTIATING) { - IEEE80211_DPRINTF(("%s: unexpected in state: %d\n", - __func__, ni->ni_rsn_state)); - return; - } - if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt) { - ic->ic_stats.is_rx_eapol_replay++; - return; - } - /* check Key MIC field using KCK */ - if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) { - IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); - ic->ic_stats.is_rx_eapol_badmic++; - return; - } - - timeout_del(&ni->ni_rsn_timeout); - ni->ni_rsn_gstate = RSNA_REKEYESTABLISHED; - - 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; - ni->ni_rsn_retries = 0; - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: received msg %d/%d of the %s handshake from %s\n", - ic->ic_if.if_xname, 2, 2, "group key", - ether_sprintf(ni->ni_macaddr)); -} - -/* - * EAPOL-Key Request frames are sent by the supplicant to request that the - * authenticator initiates either a 4-Way Handshake or Group Key Handshake, - * or to report a MIC failure in a TKIP MSDU. - */ -void -ieee80211_recv_eapol_key_req(struct ieee80211com *ic, - struct ieee80211_eapol_key *key, struct ieee80211_node *ni) -{ - u_int16_t info; - - if (ic->ic_opmode != IEEE80211_M_HOSTAP && - ic->ic_opmode != IEEE80211_M_IBSS) - return; - - info = BE_READ_2(key->info); - - /* enforce monotonicity of key request replay counter */ - if (ni->ni_reqreplaycnt_ok && - BE_READ_8(key->replaycnt) <= ni->ni_reqreplaycnt) { - ic->ic_stats.is_rx_eapol_replay++; - return; - } - if (!(info & EAPOL_KEY_KEYMIC) || - ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) { - IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); - ic->ic_stats.is_rx_eapol_badmic++; - return; - } - /* update key request replay counter now that MIC is verified */ - ni->ni_reqreplaycnt = BE_READ_8(key->replaycnt); - ni->ni_reqreplaycnt_ok = 1; - - if (info & EAPOL_KEY_ERROR) { /* TKIP MIC failure */ - /* ignore reports from STAs not using TKIP */ - if (ic->ic_bss->ni_rsngroupcipher != IEEE80211_CIPHER_TKIP && - ni->ni_rsncipher != IEEE80211_CIPHER_TKIP) { - IEEE80211_DPRINTF(("%s: MIC failure report from " - "STA not using TKIP: %s\n", __func__, - ether_sprintf(ni->ni_macaddr))); - return; - } - ic->ic_stats.is_rx_remmicfail++; - ieee80211_michael_mic_failure(ic, LE_READ_6(key->rsc)); - - } else if (info & EAPOL_KEY_PAIRWISE) { - /* initiate a 4-Way Handshake */ - - } else { - /* - * Should change the GTK, initiate the 4-Way Handshake and - * then execute a Group Key Handshake with all supplicants. - */ - } -} - -/* - * Process an incoming EAPOL frame. Notice that we are only interested in - * EAPOL-Key frames with an IEEE 802.11 or WPA descriptor type. - */ -void -ieee80211_recv_eapol(struct ieee80211com *ic, struct mbuf *m0, - struct ieee80211_node *ni) -{ - struct ifnet *ifp = &ic->ic_if; - struct ether_header *eh; - struct ieee80211_eapol_key *key; - u_int16_t info, desc; - - ifp->if_ibytes += m0->m_pkthdr.len; - - if (m0->m_len < sizeof(*eh) + sizeof(*key)) - return; - eh = mtod(m0, struct ether_header *); - if (IEEE80211_IS_MULTICAST(eh->ether_dhost)) { - ifp->if_imcasts++; - return; - } - m_adj(m0, sizeof(*eh)); - key = mtod(m0, struct ieee80211_eapol_key *); - - if (key->type != EAPOL_KEY) - return; - ic->ic_stats.is_rx_eapol_key++; - - if ((ni->ni_rsnprotos == IEEE80211_PROTO_RSN && - key->desc != EAPOL_KEY_DESC_IEEE80211) || - (ni->ni_rsnprotos == IEEE80211_PROTO_WPA && - key->desc != EAPOL_KEY_DESC_WPA)) - return; - - /* check packet body length */ - if (m0->m_len < 4 + BE_READ_2(key->len)) - return; - - /* check key data length */ - if (m0->m_len < sizeof(*key) + BE_READ_2(key->paylen)) - return; - - info = BE_READ_2(key->info); - - /* discard EAPOL-Key frames with an unknown descriptor version */ - desc = info & EAPOL_KEY_VERSION_MASK; - if (desc != EAPOL_KEY_DESC_V1 && desc != EAPOL_KEY_DESC_V2) - return; - - if ((ni->ni_rsncipher == IEEE80211_CIPHER_CCMP || - ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP) && - desc != EAPOL_KEY_DESC_V2) - return; - - /* determine message type (see 8.5.3.7) */ - if (info & EAPOL_KEY_REQUEST) { - /* EAPOL-Key Request frame */ - ieee80211_recv_eapol_key_req(ic, key, ni); - - } else if (info & EAPOL_KEY_PAIRWISE) { - /* 4-Way Handshake */ - if (info & EAPOL_KEY_KEYMIC) { - if (info & EAPOL_KEY_KEYACK) - ieee80211_recv_4way_msg3(ic, key, ni); - else - ieee80211_recv_4way_msg2or4(ic, key, ni); - } else if (info & EAPOL_KEY_KEYACK) - ieee80211_recv_4way_msg1(ic, key, ni); - } else { - /* Group Key Handshake */ - if (!(info & EAPOL_KEY_KEYMIC)) - return; - if (info & EAPOL_KEY_KEYACK) { - if (key->desc == EAPOL_KEY_DESC_WPA) - ieee80211_recv_wpa_group_msg1(ic, key, ni); - else - ieee80211_recv_rsn_group_msg1(ic, key, ni); - } else - ieee80211_recv_group_msg2(ic, key, ni); - } -} - void ieee80211_recv_pspoll(struct ieee80211com *ic, struct mbuf *m0) { diff --git a/sys/net80211/ieee80211_output.c b/sys/net80211/ieee80211_output.c index 33654eb2d32..b18bcd351aa 100644 --- a/sys/net80211/ieee80211_output.c +++ b/sys/net80211/ieee80211_output.c @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_output.c,v 1.59 2008/04/16 18:32:15 damien Exp $ */ +/* $OpenBSD: ieee80211_output.c,v 1.60 2008/07/21 19:05:21 damien Exp $ */ /* $NetBSD: ieee80211_output.c,v 1.13 2004/05/31 11:02:55 dyoung Exp $ */ /*- @@ -86,13 +86,6 @@ struct mbuf *ieee80211_get_assoc_resp(struct ieee80211com *, struct ieee80211_node *, u_int16_t); struct mbuf *ieee80211_get_disassoc(struct ieee80211com *, struct ieee80211_node *, u_int16_t); -int ieee80211_send_eapol_key(struct ieee80211com *, struct mbuf *, - struct ieee80211_node *, const struct ieee80211_ptk *); -u_int8_t *ieee80211_add_gtk_kde(u_int8_t *, struct ieee80211_node *, - const struct ieee80211_key *); -u_int8_t *ieee80211_add_pmkid_kde(u_int8_t *, const u_int8_t *); -struct mbuf *ieee80211_get_eapol_key(int, int, u_int); - /* * IEEE 802.11 output routine. Normally this will directly call the @@ -1501,563 +1494,6 @@ ieee80211_beacon_alloc(struct ieee80211com *ic, struct ieee80211_node *ni) return m; } -/* unaligned big endian access */ -#define BE_READ_2(p) \ - ((u_int16_t)(p)[0] << 8 | (u_int16_t)(p)[1]) - -#define BE_WRITE_2(p, v) do { \ - (p)[0] = (v) >> 8; (p)[1] = (v); \ -} while (0) - -#define BE_WRITE_8(p, v) do { \ - (p)[0] = (v) >> 56; (p)[1] = (v) >> 48; \ - (p)[2] = (v) >> 40; (p)[3] = (v) >> 32; \ - (p)[4] = (v) >> 24; (p)[5] = (v) >> 16; \ - (p)[6] = (v) >> 8; (p)[7] = (v); \ -} while (0) - -/* unaligned little endian access */ -#define LE_WRITE_6(p, v) do { \ - (p)[5] = (v) >> 40; (p)[4] = (v) >> 32; \ - (p)[3] = (v) >> 24; (p)[2] = (v) >> 16; \ - (p)[1] = (v) >> 8; (p)[0] = (v); \ -} while (0) - -/* - * Handle EAPOL-Key timeouts (no answer from supplicant). - */ -void -ieee80211_eapol_timeout(void *arg) -{ - struct ieee80211_node *ni = arg; - struct ieee80211com *ic = ni->ni_ic; - int s; - - IEEE80211_DPRINTF(("%s: no answer from station %s in state %d\n", - __func__, ether_sprintf(ni->ni_macaddr), ni->ni_rsn_state)); - - s = splnet(); - - switch (ni->ni_rsn_state) { - case RSNA_PTKSTART: - case RSNA_PTKCALCNEGOTIATING: - (void)ieee80211_send_4way_msg1(ic, ni); - break; - case RSNA_PTKINITNEGOTIATING: - (void)ieee80211_send_4way_msg3(ic, ni); - break; - } - - switch (ni->ni_rsn_gstate) { - case RSNA_REKEYNEGOTIATING: - (void)ieee80211_send_group_msg1(ic, ni); - break; - } - - splx(s); -} - -/* - * Send an EAPOL-Key frame to node `ni'. If MIC or encryption is required, - * the PTK must be passed (otherwise it can be set to NULL.) - */ -int -ieee80211_send_eapol_key(struct ieee80211com *ic, struct mbuf *m, - struct ieee80211_node *ni, const struct ieee80211_ptk *ptk) -{ - struct ifnet *ifp = &ic->ic_if; - struct ether_header *eh; - struct ieee80211_eapol_key *key; - u_int16_t len, info; - int s, error; - - M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT); - if (m == NULL) - return ENOMEM; - /* no need to m_pullup here (ok by construction) */ - eh = mtod(m, struct ether_header *); - eh->ether_type = htons(ETHERTYPE_PAE); - IEEE80211_ADDR_COPY(eh->ether_shost, ic->ic_myaddr); - IEEE80211_ADDR_COPY(eh->ether_dhost, ni->ni_macaddr); - - key = (struct ieee80211_eapol_key *)&eh[1]; - key->version = EAPOL_VERSION; - key->type = EAPOL_KEY; - key->desc = (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ? - EAPOL_KEY_DESC_IEEE80211 : EAPOL_KEY_DESC_WPA; - - info = BE_READ_2(key->info); - /* use V2 descriptor if pairwise or group cipher is CCMP */ - if (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP || - ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP) - info |= EAPOL_KEY_DESC_V2; - else - info |= EAPOL_KEY_DESC_V1; - BE_WRITE_2(key->info, info); - - len = m->m_len - sizeof(struct ether_header); - BE_WRITE_2(key->paylen, len - sizeof(*key)); - BE_WRITE_2(key->len, len - 4); - - if (info & EAPOL_KEY_ENCRYPTED) { - if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { - /* clear "Encrypted" bit for WPA */ - info &= ~EAPOL_KEY_ENCRYPTED; - BE_WRITE_2(key->info, info); - } - ieee80211_eapol_key_encrypt(ic, key, ptk->kek); - - if ((info & EAPOL_KEY_VERSION_MASK) == EAPOL_KEY_DESC_V2) { - /* AES Key Wrap adds 8 bytes + padding */ - m->m_pkthdr.len = m->m_len = - sizeof(*eh) + 4 + BE_READ_2(key->len); - } - } - if (info & EAPOL_KEY_KEYMIC) - ieee80211_eapol_key_mic(key, ptk->kck); - - s = splnet(); - /* start a 100ms timeout if an answer is expected from supplicant */ - if (info & EAPOL_KEY_KEYACK) - timeout_add(&ni->ni_rsn_timeout, hz / 10); - IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error); - if (error == 0) { - ifp->if_obytes += m->m_pkthdr.len; - if ((ifp->if_flags & IFF_OACTIVE) == 0) - (*ifp->if_start)(ifp); - } - splx(s); - - return error; -} - -/* - * Add a GTK KDE to an EAPOL-Key frame (see Figure 144). - */ -u_int8_t * -ieee80211_add_gtk_kde(u_int8_t *frm, struct ieee80211_node *ni, - const struct ieee80211_key *k) -{ - KASSERT(k->k_flags & IEEE80211_KEY_GROUP); - - *frm++ = IEEE80211_ELEMID_VENDOR; - *frm++ = 6 + k->k_len; - memcpy(frm, IEEE80211_OUI, 3); frm += 3; - *frm++ = IEEE80211_KDE_GTK; - *frm = k->k_id & 3; - /* - * The TxRx flag for sending a GTK is always the opposite of whether - * the pairwise key is used for data encryption/integrity or not. - */ - if (ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP) - *frm |= 1 << 2; /* set the Tx bit */ - frm++; - *frm++ = 0; /* reserved */ - memcpy(frm, k->k_key, k->k_len); - return frm + k->k_len; -} - -/* - * Add a PMKID KDE to an EAPOL-Key frame (see Figure 146). - */ -u_int8_t * -ieee80211_add_pmkid_kde(u_int8_t *frm, const u_int8_t *pmkid) -{ - *frm++ = IEEE80211_ELEMID_VENDOR; - *frm++ = 20; - memcpy(frm, IEEE80211_OUI, 3); frm += 3; - *frm++ = IEEE80211_KDE_PMKID; - memcpy(frm, pmkid, IEEE80211_PMKID_LEN); - return frm + IEEE80211_PMKID_LEN; -} - -struct mbuf * -ieee80211_get_eapol_key(int flags, int type, u_int pktlen) -{ - struct mbuf *m; - - /* reserve space for 802.11 encapsulation and EAPOL-Key header */ - pktlen += sizeof(struct ieee80211_frame) + sizeof(struct llc) + - sizeof(struct ieee80211_eapol_key); - - if (pktlen > MCLBYTES) - panic("EAPOL-Key frame too large: %u", pktlen); - MGETHDR(m, flags, type); - if (m == NULL) - return NULL; - if (pktlen >= MINCLSIZE) { - MCLGET(m, flags); - if (!(m->m_flags & M_EXT)) - return m_free(m); - } - m->m_data += sizeof(struct ieee80211_frame) + sizeof(struct llc); - return m; -} - -/* - * 4-Way Handshake Message 1 is sent by the authenticator to the supplicant - * (see 8.5.3.1). - */ -int -ieee80211_send_4way_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) -{ - struct ieee80211_eapol_key *key; - struct mbuf *m; - u_int16_t info, keylen; - u_int8_t *frm; - - ni->ni_rsn_state = RSNA_PTKSTART; - if (++ni->ni_rsn_retries > 3) { - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_4WAY_TIMEOUT); - ieee80211_node_leave(ic, ni); - return 0; - } - m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, - (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ? 2 + 20 : 0); - if (m == NULL) - return ENOMEM; - key = mtod(m, struct ieee80211_eapol_key *); - memset(key, 0, sizeof(*key)); - - info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYACK; - BE_WRITE_2(key->info, info); - - /* copy the authenticator's nonce (ANonce) */ - memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN); - - keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); - BE_WRITE_2(key->keylen, keylen); - - frm = (u_int8_t *)&key[1]; - /* WPA does not have PMKID KDE */ - if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN && - ni->ni_rsnakms == IEEE80211_AKM_IEEE8021X) { - /* XXX retrieve PMKID from the PMKSA cache */ - /* frm = ieee80211_add_pmkid_kde(frm, pmkid); */ - } - - m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key; - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: sending msg %d/%d of the %s handshake to %s\n", - ic->ic_if.if_xname, 1, 4, "4-way", - ether_sprintf(ni->ni_macaddr)); - - ni->ni_replaycnt++; - BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); - - return ieee80211_send_eapol_key(ic, m, ni, NULL); -} - -/* - * 4-Way Handshake Message 2 is sent by the supplicant to the authenticator - * (see 8.5.3.2). - */ -int -ieee80211_send_4way_msg2(struct ieee80211com *ic, struct ieee80211_node *ni, - const u_int8_t *replaycnt, const struct ieee80211_ptk *tptk) -{ - struct ieee80211_eapol_key *key; - struct mbuf *m; - u_int16_t info; - u_int8_t *frm; - - m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, - 2 + 48); - if (m == NULL) - return ENOMEM; - key = mtod(m, struct ieee80211_eapol_key *); - memset(key, 0, sizeof(*key)); - - info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYMIC; - BE_WRITE_2(key->info, info); - - /* copy key replay counter from Message 1/4 */ - memcpy(key->replaycnt, replaycnt, 8); - - /* copy the supplicant's nonce (SNonce) */ - memcpy(key->nonce, ic->ic_nonce, EAPOL_KEY_NONCE_LEN); - - frm = (u_int8_t *)&key[1]; - /* add the WPA/RSN IE used in the (Re)Association Request */ - if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { - u_int16_t keylen; - frm = ieee80211_add_wpa(frm, ic, ni); - /* WPA sets the key length field here */ - keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); - BE_WRITE_2(key->keylen, keylen); - } else /* RSN */ - frm = ieee80211_add_rsn(frm, ic, ni); - - m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key; - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: sending msg %d/%d of the %s handshake to %s\n", - ic->ic_if.if_xname, 2, 4, "4-way", - ether_sprintf(ni->ni_macaddr)); - - return ieee80211_send_eapol_key(ic, m, ni, tptk); -} - -/* - * 4-Way Handshake Message 3 is sent by the authenticator to the supplicant - * (see 8.5.3.3). - */ -int -ieee80211_send_4way_msg3(struct ieee80211com *ic, struct ieee80211_node *ni) -{ - struct ieee80211_eapol_key *key; - struct ieee80211_key *k; - struct mbuf *m; - u_int16_t info, keylen; - u_int8_t *frm; - - ni->ni_rsn_state = RSNA_PTKINITNEGOTIATING; - if (++ni->ni_rsn_retries > 3) { - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_4WAY_TIMEOUT); - ieee80211_node_leave(ic, ni); - return 0; - } - if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) - k = &ic->ic_nw_keys[ic->ic_def_txkey]; - - m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, - 2 + 48 + - ((ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ? - 2 + 6 + k->k_len : 0) + - 8); - if (m == NULL) - return ENOMEM; - key = mtod(m, struct ieee80211_eapol_key *); - memset(key, 0, sizeof(*key)); - - info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYACK | EAPOL_KEY_KEYMIC; - if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP) - info |= EAPOL_KEY_INSTALL; - - /* use same nonce as in Message 1 */ - memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN); - - ni->ni_replaycnt++; - BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); - - keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); - BE_WRITE_2(key->keylen, keylen); - - frm = (u_int8_t *)&key[1]; - /* add the WPA/RSN IE included in Beacon/Probe Response */ - if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) { - frm = ieee80211_add_rsn(frm, ic, ic->ic_bss); - /* encapsulate the GTK and ask for encryption */ - frm = ieee80211_add_gtk_kde(frm, ni, k); - LE_WRITE_6(key->rsc, k->k_tsc); - info |= EAPOL_KEY_ENCRYPTED | EAPOL_KEY_SECURE; - } else /* WPA */ - frm = ieee80211_add_wpa(frm, ic, ic->ic_bss); - - /* write the key info field */ - BE_WRITE_2(key->info, info); - - m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key; - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: sending msg %d/%d of the %s handshake to %s\n", - ic->ic_if.if_xname, 3, 4, "4-way", - ether_sprintf(ni->ni_macaddr)); - - return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); -} - -/* - * 4-Way Handshake Message 4 is sent by the supplicant to the authenticator - * (see 8.5.3.4). - */ -int -ieee80211_send_4way_msg4(struct ieee80211com *ic, struct ieee80211_node *ni) -{ - struct ieee80211_eapol_key *key; - struct mbuf *m; - u_int16_t info; - - m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0); - if (m == NULL) - return ENOMEM; - key = mtod(m, struct ieee80211_eapol_key *); - memset(key, 0, sizeof(*key)); - - info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYMIC; - - /* copy key replay counter from authenticator */ - BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); - - if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { - u_int16_t keylen; - /* WPA sets the key length field here */ - keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); - BE_WRITE_2(key->keylen, keylen); - } else - info |= EAPOL_KEY_SECURE; - - /* write the key info field */ - BE_WRITE_2(key->info, info); - - /* empty key data field */ - m->m_pkthdr.len = m->m_len = sizeof(*key); - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: sending msg %d/%d of the %s handshake to %s\n", - ic->ic_if.if_xname, 4, 4, "4-way", - ether_sprintf(ni->ni_macaddr)); - - return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); -} - -/* - * Group Key Handshake Message 1 is sent by the authenticator to the - * supplicant (see 8.5.4.1). - */ -int -ieee80211_send_group_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) -{ - struct ieee80211_eapol_key *key; - const struct ieee80211_key *k; - struct mbuf *m; - u_int16_t info; - u_int8_t *frm; - - ni->ni_rsn_gstate = RSNA_REKEYNEGOTIATING; - if (++ni->ni_rsn_retries > 3) { - IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, - IEEE80211_REASON_GROUP_TIMEOUT); - ieee80211_node_leave(ic, ni); - return 0; - } - k = &ic->ic_nw_keys[ic->ic_def_txkey]; - - m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, - ((ni->ni_rsnprotos == IEEE80211_PROTO_WPA) ? - k->k_len : 2 + 6 + k->k_len) + - 8); - if (m == NULL) - return ENOMEM; - key = mtod(m, struct ieee80211_eapol_key *); - memset(key, 0, sizeof(*key)); - - info = EAPOL_KEY_KEYACK | EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE | - EAPOL_KEY_ENCRYPTED; - - ni->ni_replaycnt++; - BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); - - frm = (u_int8_t *)&key[1]; - if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { - /* WPA does not have GTK KDE */ - BE_WRITE_2(key->keylen, k->k_len); - memcpy(frm, k->k_key, k->k_len); - frm += k->k_len; - info |= (k->k_id & 0x3) << EAPOL_KEY_WPA_KID_SHIFT; - if (ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP) - info |= EAPOL_KEY_WPA_TX; - } else /* RSN */ - frm = ieee80211_add_gtk_kde(frm, ni, k); - - /* RSC = last transmit sequence number for the GTK */ - LE_WRITE_6(key->rsc, k->k_tsc); - - /* write the key info field */ - BE_WRITE_2(key->info, info); - - m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key; - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: sending msg %d/%d of the %s handshake to %s\n", - ic->ic_if.if_xname, 1, 2, "group key", - ether_sprintf(ni->ni_macaddr)); - - return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); -} - -/* - * Group Key Handshake Message 2 is sent by the supplicant to the - * authenticator (see 8.5.4.2). - */ -int -ieee80211_send_group_msg2(struct ieee80211com *ic, struct ieee80211_node *ni, - const struct ieee80211_key *k) -{ - struct ieee80211_eapol_key *key; - u_int16_t info; - struct mbuf *m; - - m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0); - if (m == NULL) - return ENOMEM; - key = mtod(m, struct ieee80211_eapol_key *); - memset(key, 0, sizeof(*key)); - - info = EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE; - - /* copy key replay counter from authenticator */ - BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); - - if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { - /* WPA sets the key length and key id fields here */ - BE_WRITE_2(key->keylen, k->k_len); - info |= (k->k_id & 3) << EAPOL_KEY_WPA_KID_SHIFT; - } - - /* write the key info field */ - BE_WRITE_2(key->info, info); - - /* empty key data field */ - m->m_pkthdr.len = m->m_len = sizeof(*key); - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: sending msg %d/%d of the %s handshake to %s\n", - ic->ic_if.if_xname, 2, 2, "group key", - ether_sprintf(ni->ni_macaddr)); - - return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); -} - -/* - * EAPOL-Key Request frames are sent by the supplicant to request that the - * authenticator initiates either a 4-Way Handshake or Group Key Handshake, - * or to report a MIC failure in a TKIP MSDU. - */ -int -ieee80211_send_eapol_key_req(struct ieee80211com *ic, - struct ieee80211_node *ni, u_int16_t info, u_int64_t tsc) -{ - struct ieee80211_eapol_key *key; - struct mbuf *m; - - m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0); - if (m == NULL) - return ENOMEM; - key = mtod(m, struct ieee80211_eapol_key *); - memset(key, 0, sizeof(*key)); - - info |= EAPOL_KEY_REQUEST; - BE_WRITE_2(key->info, info); - - /* in case of TKIP MIC failure, fill the RSC field */ - if (info & EAPOL_KEY_ERROR) - LE_WRITE_6(key->rsc, tsc); - - /* use our separate key replay counter for key requests */ - BE_WRITE_8(key->replaycnt, ni->ni_reqreplaycnt); - ni->ni_reqreplaycnt++; - - if (ic->ic_if.if_flags & IFF_DEBUG) - printf("%s: sending EAPOL-Key request to %s\n", - ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr)); - - return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); -} - void ieee80211_pwrsave(struct ieee80211com *ic, struct ieee80211_node *ni, struct mbuf *m) diff --git a/sys/net80211/ieee80211_pae_input.c b/sys/net80211/ieee80211_pae_input.c new file mode 100644 index 00000000000..b7338624d54 --- /dev/null +++ b/sys/net80211/ieee80211_pae_input.c @@ -0,0 +1,916 @@ +/*- + * Copyright (c) 2007,2008 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#endif + +#include + +#include + +void ieee80211_recv_4way_msg1(struct ieee80211com *, + struct ieee80211_eapol_key *, struct ieee80211_node *); +void ieee80211_recv_4way_msg2(struct ieee80211com *, + struct ieee80211_eapol_key *, struct ieee80211_node *, + const u_int8_t *); +void ieee80211_recv_4way_msg3(struct ieee80211com *, + struct ieee80211_eapol_key *, struct ieee80211_node *); +void ieee80211_recv_4way_msg4(struct ieee80211com *, + struct ieee80211_eapol_key *, struct ieee80211_node *); +void ieee80211_recv_4way_msg2or4(struct ieee80211com *, + struct ieee80211_eapol_key *, struct ieee80211_node *); +void ieee80211_recv_rsn_group_msg1(struct ieee80211com *, + struct ieee80211_eapol_key *, struct ieee80211_node *); +void ieee80211_recv_wpa_group_msg1(struct ieee80211com *, + struct ieee80211_eapol_key *, struct ieee80211_node *); +void ieee80211_recv_group_msg2(struct ieee80211com *, + struct ieee80211_eapol_key *, struct ieee80211_node *); +void ieee80211_recv_eapol_key_req(struct ieee80211com *, + struct ieee80211_eapol_key *, struct ieee80211_node *); + +/* unaligned big endian access */ +#define BE_READ_2(p) \ + ((u_int16_t)(p)[0] << 8 | (u_int16_t)(p)[1]) + +#define BE_READ_8(p) \ + ((u_int64_t)(p)[0] << 56 | (u_int64_t)(p)[1] << 48 | \ + (u_int64_t)(p)[2] << 40 | (u_int64_t)(p)[3] << 32 | \ + (u_int64_t)(p)[4] << 24 | (u_int64_t)(p)[5] << 16 | \ + (u_int64_t)(p)[6] << 8 | (u_int64_t)(p)[7]) + +/* unaligned little endian access */ +#define LE_READ_6(p) \ + ((u_int64_t)(p)[5] << 40 | (u_int64_t)(p)[4] << 32 | \ + (u_int64_t)(p)[3] << 24 | (u_int64_t)(p)[2] << 16 | \ + (u_int64_t)(p)[1] << 8 | (u_int64_t)(p)[0]) + +/* + * Process an incoming EAPOL frame. Notice that we are only interested in + * EAPOL-Key frames with an IEEE 802.11 or WPA descriptor type. + */ +void +ieee80211_recv_eapol(struct ieee80211com *ic, struct mbuf *m0, + struct ieee80211_node *ni) +{ + struct ifnet *ifp = &ic->ic_if; + struct ether_header *eh; + struct ieee80211_eapol_key *key; + u_int16_t info, desc; + + ifp->if_ibytes += m0->m_pkthdr.len; + + if (m0->m_len < sizeof(*eh) + sizeof(*key)) + return; + eh = mtod(m0, struct ether_header *); + if (IEEE80211_IS_MULTICAST(eh->ether_dhost)) { + ifp->if_imcasts++; + return; + } + m_adj(m0, sizeof(*eh)); + key = mtod(m0, struct ieee80211_eapol_key *); + + if (key->type != EAPOL_KEY) + return; + ic->ic_stats.is_rx_eapol_key++; + + if ((ni->ni_rsnprotos == IEEE80211_PROTO_RSN && + key->desc != EAPOL_KEY_DESC_IEEE80211) || + (ni->ni_rsnprotos == IEEE80211_PROTO_WPA && + key->desc != EAPOL_KEY_DESC_WPA)) + return; + + /* check packet body length */ + if (m0->m_len < 4 + BE_READ_2(key->len)) + return; + + /* check key data length */ + if (m0->m_len < sizeof(*key) + BE_READ_2(key->paylen)) + return; + + info = BE_READ_2(key->info); + + /* discard EAPOL-Key frames with an unknown descriptor version */ + desc = info & EAPOL_KEY_VERSION_MASK; + if (desc != EAPOL_KEY_DESC_V1 && desc != EAPOL_KEY_DESC_V2) + return; + + if ((ni->ni_rsncipher == IEEE80211_CIPHER_CCMP || + ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP) && + desc != EAPOL_KEY_DESC_V2) + return; + + /* determine message type (see 8.5.3.7) */ + if (info & EAPOL_KEY_REQUEST) { + /* EAPOL-Key Request frame */ + ieee80211_recv_eapol_key_req(ic, key, ni); + + } else if (info & EAPOL_KEY_PAIRWISE) { + /* 4-Way Handshake */ + if (info & EAPOL_KEY_KEYMIC) { + if (info & EAPOL_KEY_KEYACK) + ieee80211_recv_4way_msg3(ic, key, ni); + else + ieee80211_recv_4way_msg2or4(ic, key, ni); + } else if (info & EAPOL_KEY_KEYACK) + ieee80211_recv_4way_msg1(ic, key, ni); + } else { + /* Group Key Handshake */ + if (!(info & EAPOL_KEY_KEYMIC)) + return; + if (info & EAPOL_KEY_KEYACK) { + if (key->desc == EAPOL_KEY_DESC_WPA) + ieee80211_recv_wpa_group_msg1(ic, key, ni); + else + ieee80211_recv_rsn_group_msg1(ic, key, ni); + } else + ieee80211_recv_group_msg2(ic, key, ni); + } +} + +/* + * 4-Way Handshake Message 1 is sent by the authenticator to the supplicant + * (see 8.5.3.1). + */ +void +ieee80211_recv_4way_msg1(struct ieee80211com *ic, + struct ieee80211_eapol_key *key, struct ieee80211_node *ni) +{ + struct ieee80211_ptk tptk; + const u_int8_t *frm, *efrm; + const u_int8_t *pmkid; + const u_int8_t *pmk; + + if (ic->ic_opmode != IEEE80211_M_STA && + ic->ic_opmode != IEEE80211_M_IBSS) + return; + + if (ni->ni_replaycnt_ok && + BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { + ic->ic_stats.is_rx_eapol_replay++; + return; + } + /* save authenticator's nonce (ANonce) */ + memcpy(ni->ni_nonce, key->nonce, EAPOL_KEY_NONCE_LEN); + + /* parse key data field (may contain an encapsulated PMKID) */ + frm = (const u_int8_t *)&key[1]; + efrm = frm + BE_READ_2(key->paylen); + + pmkid = NULL; + while (frm + 2 <= efrm) { + if (frm + 2 + frm[1] > efrm) + break; + switch (frm[0]) { + case IEEE80211_ELEMID_VENDOR: + if (frm[1] < 4) + break; + if (memcmp(&frm[2], IEEE80211_OUI, 3) == 0) { + switch (frm[5]) { + case IEEE80211_KDE_PMKID: + pmkid = frm; + break; + } + } + break; + } + frm += 2 + frm[1]; + } + /* check that the PMKID KDE is valid (if present) */ + if (pmkid != NULL && pmkid[1] < 4 + 16) + return; + + /* generate a new supplicant's nonce (SNonce) */ + arc4random_buf(ic->ic_nonce, EAPOL_KEY_NONCE_LEN); + + /* retrieve PMK and derive TPTK */ + if ((pmk = ieee80211_get_pmk(ic, ni, pmkid)) == NULL) { + /* no PMK configured for this STA/PMKID */ + return; + } + ieee80211_derive_ptk(pmk, IEEE80211_PMK_LEN, ni->ni_macaddr, + ic->ic_myaddr, key->nonce, ic->ic_nonce, (u_int8_t *)&tptk, + sizeof(tptk)); + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: received msg %d/%d of the %s handshake from %s\n", + ic->ic_if.if_xname, 1, 4, "4-way", + ether_sprintf(ni->ni_macaddr)); + + /* send message 2 to authenticator using TPTK */ + (void)ieee80211_send_4way_msg2(ic, ni, key->replaycnt, &tptk); +} + +/* + * 4-Way Handshake Message 2 is sent by the supplicant to the authenticator + * (see 8.5.3.2). + */ +void +ieee80211_recv_4way_msg2(struct ieee80211com *ic, + struct ieee80211_eapol_key *key, struct ieee80211_node *ni, + const u_int8_t *rsnie) +{ + struct ieee80211_ptk tptk; + const u_int8_t *pmk; + + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_opmode != IEEE80211_M_IBSS) + return; + + /* discard if we're not expecting this message */ + if (ni->ni_rsn_state != RSNA_PTKSTART && + ni->ni_rsn_state != RSNA_PTKCALCNEGOTIATING) { + IEEE80211_DPRINTF(("%s: unexpected in state: %d\n", + __func__, ni->ni_rsn_state)); + return; + } + ni->ni_rsn_state = RSNA_PTKCALCNEGOTIATING; + + /* replay counter has already been verified by caller */ + + /* retrieve PMK and derive TPTK */ + if ((pmk = ieee80211_get_pmk(ic, ni, NULL)) == NULL) { + /* no PMK configured for this STA */ + return; /* will timeout.. */ + } + ieee80211_derive_ptk(pmk, IEEE80211_PMK_LEN, ic->ic_myaddr, + ni->ni_macaddr, ni->ni_nonce, key->nonce, (u_int8_t *)&tptk, + sizeof(tptk)); + + /* check Key MIC field using KCK */ + if (ieee80211_eapol_key_check_mic(key, tptk.kck) != 0) { + IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); + ic->ic_stats.is_rx_eapol_badmic++; + return; /* will timeout.. */ + } + + timeout_del(&ni->ni_rsn_timeout); + ni->ni_rsn_state = RSNA_PTKCALCNEGOTIATING_2; + ni->ni_rsn_retries = 0; + + /* install TPTK as PTK now that MIC is verified */ + memcpy(&ni->ni_ptk, &tptk, sizeof(tptk)); + + /* + * The RSN IE must match bit-wise with what the STA included in its + * (Re)Association Request. + */ + if (ni->ni_rsnie == NULL || rsnie[1] != ni->ni_rsnie[1] || + memcmp(rsnie, ni->ni_rsnie, 2 + rsnie[1]) != 0) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_RSN_DIFFERENT_IE); + ieee80211_node_leave(ic, ni); + return; + } + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: received msg %d/%d of the %s handshake from %s\n", + ic->ic_if.if_xname, 2, 4, "4-way", + ether_sprintf(ni->ni_macaddr)); + + /* send message 3 to supplicant */ + (void)ieee80211_send_4way_msg3(ic, ni); +} + +/* + * 4-Way Handshake Message 3 is sent by the authenticator to the supplicant + * (see 8.5.3.3). + */ +void +ieee80211_recv_4way_msg3(struct ieee80211com *ic, + struct ieee80211_eapol_key *key, struct ieee80211_node *ni) +{ + struct ieee80211_ptk tptk; + struct ieee80211_key *k; + const u_int8_t *frm, *efrm; + const u_int8_t *rsnie1, *rsnie2, *gtk; + const u_int8_t *pmk; + u_int16_t info, reason = 0; + + if (ic->ic_opmode != IEEE80211_M_STA && + ic->ic_opmode != IEEE80211_M_IBSS) + return; + + if (ni->ni_replaycnt_ok && + BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { + ic->ic_stats.is_rx_eapol_replay++; + return; + } + + /* check that ANonce matches that of message 1 */ + if (memcmp(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN) != 0) { + IEEE80211_DPRINTF(("%s: ANonce does not match msg 1/4\n", + __func__)); + return; + } + /* retrieve PMK and derive TPTK */ + if ((pmk = ieee80211_get_pmk(ic, ni, NULL)) == NULL) { + /* no PMK configured for this STA */ + return; + } + ieee80211_derive_ptk(pmk, IEEE80211_PMK_LEN, ni->ni_macaddr, + ic->ic_myaddr, key->nonce, ic->ic_nonce, (u_int8_t *)&tptk, + sizeof(tptk)); + + info = BE_READ_2(key->info); + + /* check Key MIC field using KCK */ + if (ieee80211_eapol_key_check_mic(key, tptk.kck) != 0) { + IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); + ic->ic_stats.is_rx_eapol_badmic++; + return; + } + /* install TPTK as PTK now that MIC is verified */ + memcpy(&ni->ni_ptk, &tptk, sizeof(tptk)); + + /* if encrypted, decrypt Key Data field using KEK */ + if ((info & EAPOL_KEY_ENCRYPTED) && + ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0) { + IEEE80211_DPRINTF(("%s: decryption failed\n", __func__)); + return; + } + + /* parse key data field */ + frm = (const u_int8_t *)&key[1]; + efrm = frm + BE_READ_2(key->paylen); + + /* + * Some WPA1+WPA2 APs (like hostapd) appear to include both WPA and + * RSN IEs in message 3/4. We only take into account the IE of the + * version of the protocol we negotiated at association time. + */ + rsnie1 = rsnie2 = gtk = NULL; + while (frm + 2 <= efrm) { + if (frm + 2 + frm[1] > efrm) + break; + switch (frm[0]) { + case IEEE80211_ELEMID_RSN: + if (ni->ni_rsnprotos != IEEE80211_PROTO_RSN) + break; + if (rsnie1 == NULL) + rsnie1 = frm; + else if (rsnie2 == NULL) + rsnie2 = frm; + /* ignore others if more than two RSN IEs */ + break; + case IEEE80211_ELEMID_VENDOR: + if (frm[1] < 4) + break; + if (memcmp(&frm[2], IEEE80211_OUI, 3) == 0) { + switch (frm[5]) { + case IEEE80211_KDE_GTK: + gtk = frm; + break; + } + } else if (memcmp(&frm[2], MICROSOFT_OUI, 3) == 0) { + switch (frm[5]) { + case 1: /* WPA */ + if (ni->ni_rsnprotos != + IEEE80211_PROTO_WPA) + break; + rsnie1 = frm; + break; + } + } + break; + } + frm += 2 + frm[1]; + } + /* first WPA/RSN IE is mandatory */ + if (rsnie1 == NULL) { + IEEE80211_DPRINTF(("%s: missing RSN IE\n", __func__)); + return; + } + /* key data must be encrypted if GTK is included */ + if (gtk != NULL && !(info & EAPOL_KEY_ENCRYPTED)) { + IEEE80211_DPRINTF(("%s: GTK not encrypted\n", __func__)); + return; + } + /* + * Check that first WPA/RSN IE is identical to the one received in + * the beacon or probe response frame. + */ + if (ni->ni_rsnie == NULL || rsnie1[1] != ni->ni_rsnie[1] || + memcmp(rsnie1, ni->ni_rsnie, 2 + rsnie1[1]) != 0) { + reason = IEEE80211_REASON_RSN_DIFFERENT_IE; + goto deauth; + } + + /* + * If a second RSN information element is present, use its pairwise + * cipher suite or deauthenticate. + */ + if (rsnie2 != NULL) { + struct ieee80211_rsnparams rsn; + + if (ieee80211_parse_rsn(ic, rsnie2, &rsn) == 0) { + if (rsn.rsn_akms != ni->ni_rsnakms || + rsn.rsn_groupcipher != ni->ni_rsngroupcipher || + rsn.rsn_nciphers != 1 || + !(rsn.rsn_ciphers & ic->ic_rsnciphers)) { + reason = IEEE80211_REASON_BAD_PAIRWISE_CIPHER; + goto deauth; + } + /* use pairwise cipher suite of second RSN IE */ + ni->ni_rsnciphers = rsn.rsn_ciphers; + ni->ni_rsncipher = ni->ni_rsnciphers; + } + } + + /* update the last seen value of the key replay counter field */ + ni->ni_replaycnt = BE_READ_8(key->replaycnt); + ni->ni_replaycnt_ok = 1; + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: received msg %d/%d of the %s handshake from %s\n", + ic->ic_if.if_xname, 3, 4, "4-way", + ether_sprintf(ni->ni_macaddr)); + + /* send message 4 to authenticator */ + if (ieee80211_send_4way_msg4(ic, ni) != 0) + return; /* ..authenticator will retry */ + + if (info & EAPOL_KEY_INSTALL) { + u_int64_t prsc; + + /* check that key length matches that of pairwise cipher */ + if (BE_READ_2(key->keylen) != + ieee80211_cipher_keylen(ni->ni_rsncipher)) { + reason = IEEE80211_REASON_AUTH_LEAVE; + goto deauth; + } + /* install the PTK */ + prsc = (gtk == NULL) ? LE_READ_6(key->rsc) : 0; + k = &ni->ni_pairwise_key; + ieee80211_map_ptk(&ni->ni_ptk, ni->ni_rsncipher, prsc, k); + if ((*ic->ic_set_key)(ic, ni, k) != 0) { + reason = IEEE80211_REASON_AUTH_LEAVE; + goto deauth; + } + } + if (gtk != NULL) { + u_int64_t rsc; + u_int8_t kid; + + /* check that the GTK KDE is valid */ + if (gtk[1] < 4 + 2) { + reason = IEEE80211_REASON_AUTH_LEAVE; + goto deauth; + } + /* check that key length matches that of group cipher */ + if (gtk[1] - 6 != + ieee80211_cipher_keylen(ni->ni_rsngroupcipher)) { + reason = IEEE80211_REASON_AUTH_LEAVE; + goto deauth; + } + /* install the GTK */ + kid = gtk[6] & 3; + rsc = LE_READ_6(key->rsc); + k = &ic->ic_nw_keys[kid]; + ieee80211_map_gtk(>k[8], ni->ni_rsngroupcipher, kid, + gtk[6] & (1 << 2), rsc, k); + if ((*ic->ic_set_key)(ic, ni, k) != 0) { + reason = IEEE80211_REASON_AUTH_LEAVE; + goto deauth; + } + } + if (info & EAPOL_KEY_SECURE) { + if (ic->ic_opmode != IEEE80211_M_IBSS || + ++ni->ni_key_count == 2) { + IEEE80211_DPRINTF(("%s: marking port %s valid\n", + __func__, ether_sprintf(ni->ni_macaddr))); + ni->ni_port_valid = 1; + } + } + deauth: + if (reason != 0) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + reason); + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + } +} + +/* + * 4-Way Handshake Message 4 is sent by the supplicant to the authenticator + * (see 8.5.3.4). + */ +void +ieee80211_recv_4way_msg4(struct ieee80211com *ic, + struct ieee80211_eapol_key *key, struct ieee80211_node *ni) +{ + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_opmode != IEEE80211_M_IBSS) + return; + + /* discard if we're not expecting this message */ + if (ni->ni_rsn_state != RSNA_PTKINITNEGOTIATING) { + IEEE80211_DPRINTF(("%s: unexpected in state: %d\n", + __func__, ni->ni_rsn_state)); + return; + } + + /* replay counter has already been verified by caller */ + + /* check Key MIC field using KCK */ + if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) { + IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); + ic->ic_stats.is_rx_eapol_badmic++; + return; /* will timeout.. */ + } + + timeout_del(&ni->ni_rsn_timeout); + ni->ni_rsn_state = RSNA_PTKINITDONE; + ni->ni_rsn_retries = 0; + + if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP) { + /* install the PTK */ + struct ieee80211_key *k = &ni->ni_pairwise_key; + ieee80211_map_ptk(&ni->ni_ptk, ni->ni_rsncipher, 0, k); + if ((*ic->ic_set_key)(ic, ni, k) != 0) { + IEEE80211_SEND_MGMT(ic, ni, + IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_ASSOC_TOOMANY); + ieee80211_node_leave(ic, ni); + return; + } + } + if (ic->ic_opmode != IEEE80211_M_IBSS || ++ni->ni_key_count == 2) { + IEEE80211_DPRINTF(("%s: marking port %s valid\n", __func__, + ether_sprintf(ni->ni_macaddr))); + ni->ni_port_valid = 1; + } + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: received msg %d/%d of the %s handshake from %s\n", + ic->ic_if.if_xname, 4, 4, "4-way", + ether_sprintf(ni->ni_macaddr)); + + /* initiate a group key handshake for WPA */ + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) + (void)ieee80211_send_group_msg1(ic, ni); + else + ni->ni_rsn_gstate = RSNA_IDLE; +} + +/* + * Differentiate Message 2 from Message 4 of the 4-Way Handshake based on + * the presence of an RSN or WPA Information Element. + */ +void +ieee80211_recv_4way_msg2or4(struct ieee80211com *ic, + struct ieee80211_eapol_key *key, struct ieee80211_node *ni) +{ + const u_int8_t *frm, *efrm; + const u_int8_t *rsnie; + + if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt) { + ic->ic_stats.is_rx_eapol_replay++; + return; + } + + /* parse key data field (check if an RSN IE is present) */ + frm = (const u_int8_t *)&key[1]; + efrm = frm + BE_READ_2(key->paylen); + + rsnie = NULL; + while (frm + 2 <= efrm) { + if (frm + 2 + frm[1] > efrm) + break; + switch (frm[0]) { + case IEEE80211_ELEMID_RSN: + rsnie = frm; + break; + case IEEE80211_ELEMID_VENDOR: + if (frm[1] < 4) + break; + if (memcmp(&frm[2], MICROSOFT_OUI, 3) == 0) { + switch (frm[5]) { + case 1: /* WPA */ + rsnie = frm; + break; + } + } + } + frm += 2 + frm[1]; + } + if (rsnie != NULL) + ieee80211_recv_4way_msg2(ic, key, ni, rsnie); + else + ieee80211_recv_4way_msg4(ic, key, ni); +} + +/* + * Group Key Handshake Message 1 is sent by the authenticator to the + * supplicant (see 8.5.4.1). + */ +void +ieee80211_recv_rsn_group_msg1(struct ieee80211com *ic, + struct ieee80211_eapol_key *key, struct ieee80211_node *ni) +{ + struct ieee80211_key *k; + const u_int8_t *frm, *efrm; + const u_int8_t *gtk; + u_int64_t rsc; + u_int16_t info; + u_int8_t kid; + + if (ic->ic_opmode != IEEE80211_M_STA && + ic->ic_opmode != IEEE80211_M_IBSS) + return; + + if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { + ic->ic_stats.is_rx_eapol_replay++; + return; + } + /* check Key MIC field using KCK */ + if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) { + IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); + ic->ic_stats.is_rx_eapol_badmic++; + return; + } + info = BE_READ_2(key->info); + + /* check that encrypted and decrypt Key Data field using KEK */ + if (!(info & EAPOL_KEY_ENCRYPTED) || + ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0) { + IEEE80211_DPRINTF(("%s: decryption failed\n", __func__)); + return; + } + + /* parse key data field (shall contain a GTK KDE) */ + frm = (const u_int8_t *)&key[1]; + efrm = frm + BE_READ_2(key->paylen); + + gtk = NULL; + while (frm + 2 <= efrm) { + if (frm + 2 + frm[1] > efrm) + break; + switch (frm[0]) { + case IEEE80211_ELEMID_VENDOR: + if (frm[1] < 4) + break; + if (memcmp(&frm[2], IEEE80211_OUI, 3) == 0) { + switch (frm[5]) { + case IEEE80211_KDE_GTK: + gtk = frm; + break; + } + } + break; + } + frm += 2 + frm[1]; + } + /* check that the GTK KDE is present and valid */ + if (gtk == NULL || gtk[1] < 4 + 2) { + IEEE80211_DPRINTF(("%s: missing or invalid GTK KDE\n", + __func__)); + return; + } + + /* check that key length matches that of group cipher */ + if (gtk[1] - 6 != ieee80211_cipher_keylen(ni->ni_rsngroupcipher)) + return; + + /* install the GTK */ + kid = gtk[6] & 3; + rsc = LE_READ_6(key->rsc); + k = &ic->ic_nw_keys[kid]; + ieee80211_map_gtk(>k[8], ni->ni_rsngroupcipher, kid, + gtk[6] & (1 << 2), rsc, k); + if ((*ic->ic_set_key)(ic, ni, k) != 0) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_LEAVE); + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + return; + } + if (info & EAPOL_KEY_SECURE) { + if (ic->ic_opmode != IEEE80211_M_IBSS || + ++ni->ni_key_count == 2) { + IEEE80211_DPRINTF(("%s: marking port %s valid\n", + __func__, ether_sprintf(ni->ni_macaddr))); + ni->ni_port_valid = 1; + } + } + /* update the last seen value of the key replay counter field */ + ni->ni_replaycnt = BE_READ_8(key->replaycnt); + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: received msg %d/%d of the %s handshake from %s\n", + ic->ic_if.if_xname, 1, 2, "group key", + ether_sprintf(ni->ni_macaddr)); + + /* send message 2 to authenticator */ + (void)ieee80211_send_group_msg2(ic, ni, k); +} + +void +ieee80211_recv_wpa_group_msg1(struct ieee80211com *ic, + struct ieee80211_eapol_key *key, struct ieee80211_node *ni) +{ + struct ieee80211_key *k; + const u_int8_t *frm; + u_int64_t rsc; + u_int16_t info; + u_int8_t kid; + int keylen; + + if (ic->ic_opmode != IEEE80211_M_STA && + ic->ic_opmode != IEEE80211_M_IBSS) + return; + + if (BE_READ_8(key->replaycnt) <= ni->ni_replaycnt) { + ic->ic_stats.is_rx_eapol_replay++; + return; + } + /* check Key MIC field using KCK */ + if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) { + IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); + ic->ic_stats.is_rx_eapol_badmic++; + return; + } + /* + * EAPOL-Key data field is encrypted even though WPA doesn't set + * the ENCRYPTED bit in the info field. + */ + if (ieee80211_eapol_key_decrypt(key, ni->ni_ptk.kek) != 0) { + IEEE80211_DPRINTF(("%s: decryption failed\n", __func__)); + return; + } + info = BE_READ_2(key->info); + keylen = ieee80211_cipher_keylen(ni->ni_rsngroupcipher); + + /* check that key length matches that of group cipher */ + if (BE_READ_2(key->keylen) != keylen) + return; + + /* check that the data length is large enough to hold the key */ + if (BE_READ_2(key->paylen) < keylen) + return; + + /* key data field contains the GTK */ + frm = (const u_int8_t *)&key[1]; + + /* install the GTK */ + kid = (info >> EAPOL_KEY_WPA_KID_SHIFT) & 3; + rsc = LE_READ_6(key->rsc); + k = &ic->ic_nw_keys[kid]; + ieee80211_map_gtk(frm, ni->ni_rsngroupcipher, kid, + info & EAPOL_KEY_WPA_TX, rsc, k); + if ((*ic->ic_set_key)(ic, ni, k) != 0) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_AUTH_LEAVE); + ieee80211_new_state(ic, IEEE80211_S_SCAN, -1); + return; + } + if (info & EAPOL_KEY_SECURE) { + if (ic->ic_opmode != IEEE80211_M_IBSS || + ++ni->ni_key_count == 2) { + IEEE80211_DPRINTF(("%s: marking port %s valid\n", + __func__, ether_sprintf(ni->ni_macaddr))); + ni->ni_port_valid = 1; + } + } + /* update the last seen value of the key replay counter field */ + ni->ni_replaycnt = BE_READ_8(key->replaycnt); + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: received msg %d/%d of the %s handshake from %s\n", + ic->ic_if.if_xname, 1, 2, "group key", + ether_sprintf(ni->ni_macaddr)); + + /* send message 2 to authenticator */ + (void)ieee80211_send_group_msg2(ic, ni, k); +} + +/* + * Group Key Handshake Message 2 is sent by the supplicant to the + * authenticator (see 8.5.4.2). + */ +void +ieee80211_recv_group_msg2(struct ieee80211com *ic, + struct ieee80211_eapol_key *key, struct ieee80211_node *ni) +{ + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_opmode != IEEE80211_M_IBSS) + return; + + /* discard if we're not expecting this message */ + if (ni->ni_rsn_gstate != RSNA_REKEYNEGOTIATING) { + IEEE80211_DPRINTF(("%s: unexpected in state: %d\n", + __func__, ni->ni_rsn_state)); + return; + } + if (BE_READ_8(key->replaycnt) != ni->ni_replaycnt) { + ic->ic_stats.is_rx_eapol_replay++; + return; + } + /* check Key MIC field using KCK */ + if (ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) { + IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); + ic->ic_stats.is_rx_eapol_badmic++; + return; + } + + timeout_del(&ni->ni_rsn_timeout); + ni->ni_rsn_gstate = RSNA_REKEYESTABLISHED; + + 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; + ni->ni_rsn_retries = 0; + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: received msg %d/%d of the %s handshake from %s\n", + ic->ic_if.if_xname, 2, 2, "group key", + ether_sprintf(ni->ni_macaddr)); +} + +/* + * EAPOL-Key Request frames are sent by the supplicant to request that the + * authenticator initiates either a 4-Way Handshake or Group Key Handshake, + * or to report a MIC failure in a TKIP MSDU. + */ +void +ieee80211_recv_eapol_key_req(struct ieee80211com *ic, + struct ieee80211_eapol_key *key, struct ieee80211_node *ni) +{ + u_int16_t info; + + if (ic->ic_opmode != IEEE80211_M_HOSTAP && + ic->ic_opmode != IEEE80211_M_IBSS) + return; + + info = BE_READ_2(key->info); + + /* enforce monotonicity of key request replay counter */ + if (ni->ni_reqreplaycnt_ok && + BE_READ_8(key->replaycnt) <= ni->ni_reqreplaycnt) { + ic->ic_stats.is_rx_eapol_replay++; + return; + } + if (!(info & EAPOL_KEY_KEYMIC) || + ieee80211_eapol_key_check_mic(key, ni->ni_ptk.kck) != 0) { + IEEE80211_DPRINTF(("%s: key MIC failed\n", __func__)); + ic->ic_stats.is_rx_eapol_badmic++; + return; + } + /* update key request replay counter now that MIC is verified */ + ni->ni_reqreplaycnt = BE_READ_8(key->replaycnt); + ni->ni_reqreplaycnt_ok = 1; + + if (info & EAPOL_KEY_ERROR) { /* TKIP MIC failure */ + /* ignore reports from STAs not using TKIP */ + if (ic->ic_bss->ni_rsngroupcipher != IEEE80211_CIPHER_TKIP && + ni->ni_rsncipher != IEEE80211_CIPHER_TKIP) { + IEEE80211_DPRINTF(("%s: MIC failure report from " + "STA not using TKIP: %s\n", __func__, + ether_sprintf(ni->ni_macaddr))); + return; + } + ic->ic_stats.is_rx_remmicfail++; + ieee80211_michael_mic_failure(ic, LE_READ_6(key->rsc)); + + } else if (info & EAPOL_KEY_PAIRWISE) { + /* initiate a 4-Way Handshake */ + + } else { + /* + * Should change the GTK, initiate the 4-Way Handshake and + * then execute a Group Key Handshake with all supplicants. + */ + } +} diff --git a/sys/net80211/ieee80211_pae_output.c b/sys/net80211/ieee80211_pae_output.c new file mode 100644 index 00000000000..30dc8b981fa --- /dev/null +++ b/sys/net80211/ieee80211_pae_output.c @@ -0,0 +1,607 @@ +/* $OpenBSD: ieee80211_pae_output.c,v 1.1 2008/07/21 19:05:21 damien Exp $ */ + +/*- + * Copyright (c) 2007,2008 Damien Bergamini + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#endif + +#include + +int ieee80211_send_eapol_key(struct ieee80211com *, struct mbuf *, + struct ieee80211_node *, const struct ieee80211_ptk *); +u_int8_t *ieee80211_add_gtk_kde(u_int8_t *, struct ieee80211_node *, + const struct ieee80211_key *); +u_int8_t *ieee80211_add_pmkid_kde(u_int8_t *, const u_int8_t *); +struct mbuf *ieee80211_get_eapol_key(int, int, u_int); + +/* unaligned big endian access */ +#define BE_READ_2(p) \ + ((u_int16_t)(p)[0] << 8 | (u_int16_t)(p)[1]) + +#define BE_WRITE_2(p, v) do { \ + (p)[0] = (v) >> 8; (p)[1] = (v); \ +} while (0) + +#define BE_WRITE_8(p, v) do { \ + (p)[0] = (v) >> 56; (p)[1] = (v) >> 48; \ + (p)[2] = (v) >> 40; (p)[3] = (v) >> 32; \ + (p)[4] = (v) >> 24; (p)[5] = (v) >> 16; \ + (p)[6] = (v) >> 8; (p)[7] = (v); \ +} while (0) + +/* unaligned little endian access */ +#define LE_WRITE_6(p, v) do { \ + (p)[5] = (v) >> 40; (p)[4] = (v) >> 32; \ + (p)[3] = (v) >> 24; (p)[2] = (v) >> 16; \ + (p)[1] = (v) >> 8; (p)[0] = (v); \ +} while (0) + +/* + * Send an EAPOL-Key frame to node `ni'. If MIC or encryption is required, + * the PTK must be passed (otherwise it can be set to NULL.) + */ +int +ieee80211_send_eapol_key(struct ieee80211com *ic, struct mbuf *m, + struct ieee80211_node *ni, const struct ieee80211_ptk *ptk) +{ + struct ifnet *ifp = &ic->ic_if; + struct ether_header *eh; + struct ieee80211_eapol_key *key; + u_int16_t len, info; + int s, error; + + M_PREPEND(m, sizeof(struct ether_header), M_DONTWAIT); + if (m == NULL) + return ENOMEM; + /* no need to m_pullup here (ok by construction) */ + eh = mtod(m, struct ether_header *); + eh->ether_type = htons(ETHERTYPE_PAE); + IEEE80211_ADDR_COPY(eh->ether_shost, ic->ic_myaddr); + IEEE80211_ADDR_COPY(eh->ether_dhost, ni->ni_macaddr); + + key = (struct ieee80211_eapol_key *)&eh[1]; + key->version = EAPOL_VERSION; + key->type = EAPOL_KEY; + key->desc = (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ? + EAPOL_KEY_DESC_IEEE80211 : EAPOL_KEY_DESC_WPA; + + info = BE_READ_2(key->info); + /* use V2 descriptor if pairwise or group cipher is CCMP */ + if (ni->ni_rsncipher == IEEE80211_CIPHER_CCMP || + ni->ni_rsngroupcipher == IEEE80211_CIPHER_CCMP) + info |= EAPOL_KEY_DESC_V2; + else + info |= EAPOL_KEY_DESC_V1; + BE_WRITE_2(key->info, info); + + len = m->m_len - sizeof(struct ether_header); + BE_WRITE_2(key->paylen, len - sizeof(*key)); + BE_WRITE_2(key->len, len - 4); + + if (info & EAPOL_KEY_ENCRYPTED) { + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { + /* clear "Encrypted" bit for WPA */ + info &= ~EAPOL_KEY_ENCRYPTED; + BE_WRITE_2(key->info, info); + } + ieee80211_eapol_key_encrypt(ic, key, ptk->kek); + + if ((info & EAPOL_KEY_VERSION_MASK) == EAPOL_KEY_DESC_V2) { + /* AES Key Wrap adds 8 bytes + padding */ + m->m_pkthdr.len = m->m_len = + sizeof(*eh) + 4 + BE_READ_2(key->len); + } + } + if (info & EAPOL_KEY_KEYMIC) + ieee80211_eapol_key_mic(key, ptk->kck); + + s = splnet(); + /* start a 100ms timeout if an answer is expected from supplicant */ + if (info & EAPOL_KEY_KEYACK) + timeout_add(&ni->ni_rsn_timeout, hz / 10); + IFQ_ENQUEUE(&ifp->if_snd, m, NULL, error); + if (error == 0) { + ifp->if_obytes += m->m_pkthdr.len; + if ((ifp->if_flags & IFF_OACTIVE) == 0) + (*ifp->if_start)(ifp); + } + splx(s); + + return error; +} + +/* + * Handle EAPOL-Key timeouts (no answer from supplicant). + */ +void +ieee80211_eapol_timeout(void *arg) +{ + struct ieee80211_node *ni = arg; + struct ieee80211com *ic = ni->ni_ic; + int s; + + IEEE80211_DPRINTF(("%s: no answer from station %s in state %d\n", + __func__, ether_sprintf(ni->ni_macaddr), ni->ni_rsn_state)); + + s = splnet(); + + switch (ni->ni_rsn_state) { + case RSNA_PTKSTART: + case RSNA_PTKCALCNEGOTIATING: + (void)ieee80211_send_4way_msg1(ic, ni); + break; + case RSNA_PTKINITNEGOTIATING: + (void)ieee80211_send_4way_msg3(ic, ni); + break; + } + + switch (ni->ni_rsn_gstate) { + case RSNA_REKEYNEGOTIATING: + (void)ieee80211_send_group_msg1(ic, ni); + break; + } + + splx(s); +} + +/* + * Add a GTK KDE to an EAPOL-Key frame (see Figure 144). + */ +u_int8_t * +ieee80211_add_gtk_kde(u_int8_t *frm, struct ieee80211_node *ni, + const struct ieee80211_key *k) +{ + KASSERT(k->k_flags & IEEE80211_KEY_GROUP); + + *frm++ = IEEE80211_ELEMID_VENDOR; + *frm++ = 6 + k->k_len; + memcpy(frm, IEEE80211_OUI, 3); frm += 3; + *frm++ = IEEE80211_KDE_GTK; + *frm = k->k_id & 3; + /* + * The TxRx flag for sending a GTK is always the opposite of whether + * the pairwise key is used for data encryption/integrity or not. + */ + if (ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP) + *frm |= 1 << 2; /* set the Tx bit */ + frm++; + *frm++ = 0; /* reserved */ + memcpy(frm, k->k_key, k->k_len); + return frm + k->k_len; +} + +/* + * Add a PMKID KDE to an EAPOL-Key frame (see Figure 146). + */ +u_int8_t * +ieee80211_add_pmkid_kde(u_int8_t *frm, const u_int8_t *pmkid) +{ + *frm++ = IEEE80211_ELEMID_VENDOR; + *frm++ = 20; + memcpy(frm, IEEE80211_OUI, 3); frm += 3; + *frm++ = IEEE80211_KDE_PMKID; + memcpy(frm, pmkid, IEEE80211_PMKID_LEN); + return frm + IEEE80211_PMKID_LEN; +} + +struct mbuf * +ieee80211_get_eapol_key(int flags, int type, u_int pktlen) +{ + struct mbuf *m; + + /* reserve space for 802.11 encapsulation and EAPOL-Key header */ + pktlen += sizeof(struct ieee80211_frame) + sizeof(struct llc) + + sizeof(struct ieee80211_eapol_key); + + if (pktlen > MCLBYTES) + panic("EAPOL-Key frame too large: %u", pktlen); + MGETHDR(m, flags, type); + if (m == NULL) + return NULL; + if (pktlen >= MINCLSIZE) { + MCLGET(m, flags); + if (!(m->m_flags & M_EXT)) + return m_free(m); + } + m->m_data += sizeof(struct ieee80211_frame) + sizeof(struct llc); + return m; +} + +/* + * 4-Way Handshake Message 1 is sent by the authenticator to the supplicant + * (see 8.5.3.1). + */ +int +ieee80211_send_4way_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct ieee80211_eapol_key *key; + struct mbuf *m; + u_int16_t info, keylen; + u_int8_t *frm; + + ni->ni_rsn_state = RSNA_PTKSTART; + if (++ni->ni_rsn_retries > 3) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_4WAY_TIMEOUT); + ieee80211_node_leave(ic, ni); + return 0; + } + m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, + (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ? 2 + 20 : 0); + if (m == NULL) + return ENOMEM; + key = mtod(m, struct ieee80211_eapol_key *); + memset(key, 0, sizeof(*key)); + + info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYACK; + BE_WRITE_2(key->info, info); + + /* copy the authenticator's nonce (ANonce) */ + memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN); + + keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); + BE_WRITE_2(key->keylen, keylen); + + frm = (u_int8_t *)&key[1]; + /* WPA does not have PMKID KDE */ + if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN && + ni->ni_rsnakms == IEEE80211_AKM_IEEE8021X) { + /* XXX retrieve PMKID from the PMKSA cache */ + /* frm = ieee80211_add_pmkid_kde(frm, pmkid); */ + } + + m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key; + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: sending msg %d/%d of the %s handshake to %s\n", + ic->ic_if.if_xname, 1, 4, "4-way", + ether_sprintf(ni->ni_macaddr)); + + ni->ni_replaycnt++; + BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); + + return ieee80211_send_eapol_key(ic, m, ni, NULL); +} + +/* + * 4-Way Handshake Message 2 is sent by the supplicant to the authenticator + * (see 8.5.3.2). + */ +int +ieee80211_send_4way_msg2(struct ieee80211com *ic, struct ieee80211_node *ni, + const u_int8_t *replaycnt, const struct ieee80211_ptk *tptk) +{ + struct ieee80211_eapol_key *key; + struct mbuf *m; + u_int16_t info; + u_int8_t *frm; + + m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, + 2 + 48); + if (m == NULL) + return ENOMEM; + key = mtod(m, struct ieee80211_eapol_key *); + memset(key, 0, sizeof(*key)); + + info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYMIC; + BE_WRITE_2(key->info, info); + + /* copy key replay counter from Message 1/4 */ + memcpy(key->replaycnt, replaycnt, 8); + + /* copy the supplicant's nonce (SNonce) */ + memcpy(key->nonce, ic->ic_nonce, EAPOL_KEY_NONCE_LEN); + + frm = (u_int8_t *)&key[1]; + /* add the WPA/RSN IE used in the (Re)Association Request */ + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { + u_int16_t keylen; + frm = ieee80211_add_wpa(frm, ic, ni); + /* WPA sets the key length field here */ + keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); + BE_WRITE_2(key->keylen, keylen); + } else /* RSN */ + frm = ieee80211_add_rsn(frm, ic, ni); + + m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key; + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: sending msg %d/%d of the %s handshake to %s\n", + ic->ic_if.if_xname, 2, 4, "4-way", + ether_sprintf(ni->ni_macaddr)); + + return ieee80211_send_eapol_key(ic, m, ni, tptk); +} + +/* + * 4-Way Handshake Message 3 is sent by the authenticator to the supplicant + * (see 8.5.3.3). + */ +int +ieee80211_send_4way_msg3(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct ieee80211_eapol_key *key; + struct ieee80211_key *k; + struct mbuf *m; + u_int16_t info, keylen; + u_int8_t *frm; + + ni->ni_rsn_state = RSNA_PTKINITNEGOTIATING; + if (++ni->ni_rsn_retries > 3) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_4WAY_TIMEOUT); + ieee80211_node_leave(ic, ni); + return 0; + } + if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) + k = &ic->ic_nw_keys[ic->ic_def_txkey]; + + m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, + 2 + 48 + + ((ni->ni_rsnprotos == IEEE80211_PROTO_RSN) ? + 2 + 6 + k->k_len : 0) + + 8); + if (m == NULL) + return ENOMEM; + key = mtod(m, struct ieee80211_eapol_key *); + memset(key, 0, sizeof(*key)); + + info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYACK | EAPOL_KEY_KEYMIC; + if (ni->ni_rsncipher != IEEE80211_CIPHER_USEGROUP) + info |= EAPOL_KEY_INSTALL; + + /* use same nonce as in Message 1 */ + memcpy(key->nonce, ni->ni_nonce, EAPOL_KEY_NONCE_LEN); + + ni->ni_replaycnt++; + BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); + + keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); + BE_WRITE_2(key->keylen, keylen); + + frm = (u_int8_t *)&key[1]; + /* add the WPA/RSN IE included in Beacon/Probe Response */ + if (ni->ni_rsnprotos == IEEE80211_PROTO_RSN) { + frm = ieee80211_add_rsn(frm, ic, ic->ic_bss); + /* encapsulate the GTK and ask for encryption */ + frm = ieee80211_add_gtk_kde(frm, ni, k); + LE_WRITE_6(key->rsc, k->k_tsc); + info |= EAPOL_KEY_ENCRYPTED | EAPOL_KEY_SECURE; + } else /* WPA */ + frm = ieee80211_add_wpa(frm, ic, ic->ic_bss); + + /* write the key info field */ + BE_WRITE_2(key->info, info); + + m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key; + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: sending msg %d/%d of the %s handshake to %s\n", + ic->ic_if.if_xname, 3, 4, "4-way", + ether_sprintf(ni->ni_macaddr)); + + return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); +} + +/* + * 4-Way Handshake Message 4 is sent by the supplicant to the authenticator + * (see 8.5.3.4). + */ +int +ieee80211_send_4way_msg4(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct ieee80211_eapol_key *key; + struct mbuf *m; + u_int16_t info; + + m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0); + if (m == NULL) + return ENOMEM; + key = mtod(m, struct ieee80211_eapol_key *); + memset(key, 0, sizeof(*key)); + + info = EAPOL_KEY_PAIRWISE | EAPOL_KEY_KEYMIC; + + /* copy key replay counter from authenticator */ + BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); + + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { + u_int16_t keylen; + /* WPA sets the key length field here */ + keylen = ieee80211_cipher_keylen(ni->ni_rsncipher); + BE_WRITE_2(key->keylen, keylen); + } else + info |= EAPOL_KEY_SECURE; + + /* write the key info field */ + BE_WRITE_2(key->info, info); + + /* empty key data field */ + m->m_pkthdr.len = m->m_len = sizeof(*key); + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: sending msg %d/%d of the %s handshake to %s\n", + ic->ic_if.if_xname, 4, 4, "4-way", + ether_sprintf(ni->ni_macaddr)); + + return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); +} + +/* + * Group Key Handshake Message 1 is sent by the authenticator to the + * supplicant (see 8.5.4.1). + */ +int +ieee80211_send_group_msg1(struct ieee80211com *ic, struct ieee80211_node *ni) +{ + struct ieee80211_eapol_key *key; + const struct ieee80211_key *k; + struct mbuf *m; + u_int16_t info; + u_int8_t *frm; + + ni->ni_rsn_gstate = RSNA_REKEYNEGOTIATING; + if (++ni->ni_rsn_retries > 3) { + IEEE80211_SEND_MGMT(ic, ni, IEEE80211_FC0_SUBTYPE_DEAUTH, + IEEE80211_REASON_GROUP_TIMEOUT); + ieee80211_node_leave(ic, ni); + return 0; + } + k = &ic->ic_nw_keys[ic->ic_def_txkey]; + + m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, + ((ni->ni_rsnprotos == IEEE80211_PROTO_WPA) ? + k->k_len : 2 + 6 + k->k_len) + + 8); + if (m == NULL) + return ENOMEM; + key = mtod(m, struct ieee80211_eapol_key *); + memset(key, 0, sizeof(*key)); + + info = EAPOL_KEY_KEYACK | EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE | + EAPOL_KEY_ENCRYPTED; + + ni->ni_replaycnt++; + BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); + + frm = (u_int8_t *)&key[1]; + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { + /* WPA does not have GTK KDE */ + BE_WRITE_2(key->keylen, k->k_len); + memcpy(frm, k->k_key, k->k_len); + frm += k->k_len; + info |= (k->k_id & 0x3) << EAPOL_KEY_WPA_KID_SHIFT; + if (ni->ni_rsncipher == IEEE80211_CIPHER_USEGROUP) + info |= EAPOL_KEY_WPA_TX; + } else /* RSN */ + frm = ieee80211_add_gtk_kde(frm, ni, k); + + /* RSC = last transmit sequence number for the GTK */ + LE_WRITE_6(key->rsc, k->k_tsc); + + /* write the key info field */ + BE_WRITE_2(key->info, info); + + m->m_pkthdr.len = m->m_len = frm - (u_int8_t *)key; + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: sending msg %d/%d of the %s handshake to %s\n", + ic->ic_if.if_xname, 1, 2, "group key", + ether_sprintf(ni->ni_macaddr)); + + return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); +} + +/* + * Group Key Handshake Message 2 is sent by the supplicant to the + * authenticator (see 8.5.4.2). + */ +int +ieee80211_send_group_msg2(struct ieee80211com *ic, struct ieee80211_node *ni, + const struct ieee80211_key *k) +{ + struct ieee80211_eapol_key *key; + u_int16_t info; + struct mbuf *m; + + m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0); + if (m == NULL) + return ENOMEM; + key = mtod(m, struct ieee80211_eapol_key *); + memset(key, 0, sizeof(*key)); + + info = EAPOL_KEY_KEYMIC | EAPOL_KEY_SECURE; + + /* copy key replay counter from authenticator */ + BE_WRITE_8(key->replaycnt, ni->ni_replaycnt); + + if (ni->ni_rsnprotos == IEEE80211_PROTO_WPA) { + /* WPA sets the key length and key id fields here */ + BE_WRITE_2(key->keylen, k->k_len); + info |= (k->k_id & 3) << EAPOL_KEY_WPA_KID_SHIFT; + } + + /* write the key info field */ + BE_WRITE_2(key->info, info); + + /* empty key data field */ + m->m_pkthdr.len = m->m_len = sizeof(*key); + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: sending msg %d/%d of the %s handshake to %s\n", + ic->ic_if.if_xname, 2, 2, "group key", + ether_sprintf(ni->ni_macaddr)); + + return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); +} + +/* + * EAPOL-Key Request frames are sent by the supplicant to request that the + * authenticator initiates either a 4-Way Handshake or Group Key Handshake, + * or to report a MIC failure in a TKIP MSDU. + */ +int +ieee80211_send_eapol_key_req(struct ieee80211com *ic, + struct ieee80211_node *ni, u_int16_t info, u_int64_t tsc) +{ + struct ieee80211_eapol_key *key; + struct mbuf *m; + + m = ieee80211_get_eapol_key(M_DONTWAIT, MT_DATA, 0); + if (m == NULL) + return ENOMEM; + key = mtod(m, struct ieee80211_eapol_key *); + memset(key, 0, sizeof(*key)); + + info |= EAPOL_KEY_REQUEST; + BE_WRITE_2(key->info, info); + + /* in case of TKIP MIC failure, fill the RSC field */ + if (info & EAPOL_KEY_ERROR) + LE_WRITE_6(key->rsc, tsc); + + /* use our separate key replay counter for key requests */ + BE_WRITE_8(key->replaycnt, ni->ni_reqreplaycnt); + ni->ni_reqreplaycnt++; + + if (ic->ic_if.if_flags & IFF_DEBUG) + printf("%s: sending EAPOL-Key request to %s\n", + ic->ic_if.if_xname, ether_sprintf(ni->ni_macaddr)); + + return ieee80211_send_eapol_key(ic, m, ni, &ni->ni_ptk); +} diff --git a/sys/net80211/ieee80211_proto.h b/sys/net80211/ieee80211_proto.h index 27c0b95bc8f..1a5698a612e 100644 --- a/sys/net80211/ieee80211_proto.h +++ b/sys/net80211/ieee80211_proto.h @@ -1,4 +1,4 @@ -/* $OpenBSD: ieee80211_proto.h,v 1.30 2008/07/21 18:43:18 damien Exp $ */ +/* $OpenBSD: ieee80211_proto.h,v 1.31 2008/07/21 19:05:21 damien Exp $ */ /* $NetBSD: ieee80211_proto.h,v 1.3 2003/10/13 04:23:56 dyoung Exp $ */ /*- @@ -58,6 +58,7 @@ extern void ieee80211_proto_detach(struct ifnet *); struct ieee80211_node; struct ieee80211_rxinfo; +struct ieee80211_rsnparams; extern u_int ieee80211_get_hdrlen(const void *); extern void ieee80211_input(struct ifnet *, struct mbuf *, struct ieee80211_node *, struct ieee80211_rxinfo *); @@ -121,6 +122,10 @@ extern u_int8_t *ieee80211_add_wpa(u_int8_t *, struct ieee80211com *, const struct ieee80211_node *); extern u_int8_t *ieee80211_add_xrates(u_int8_t *, const struct ieee80211_rateset *); +extern int ieee80211_parse_rsn(struct ieee80211com *, const u_int8_t *, + struct ieee80211_rsnparams *); +extern int ieee80211_parse_wpa(struct ieee80211com *, const u_int8_t *, + struct ieee80211_rsnparams *); extern void ieee80211_print_essid(const u_int8_t *, int); extern void ieee80211_dump_pkt(const u_int8_t *, int, int, int); extern int ieee80211_ibss_merge(struct ieee80211com *,