move processing of EAPOL frames away from ieee80211_{input,output}.c
authordamien <damien@openbsd.org>
Mon, 21 Jul 2008 19:05:21 +0000 (19:05 +0000)
committerdamien <damien@openbsd.org>
Mon, 21 Jul 2008 19:05:21 +0000 (19:05 +0000)
sys/conf/files
sys/net80211/ieee80211_input.c
sys/net80211/ieee80211_output.c
sys/net80211/ieee80211_pae_input.c [new file with mode: 0644]
sys/net80211/ieee80211_pae_output.c [new file with mode: 0644]
sys/net80211/ieee80211_proto.h

index d599c5d..95a4f23 100644 (file)
@@ -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
index de5744f..0fa629c 100644 (file)
@@ -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 <net80211/ieee80211_var.h>
 
-#include <dev/rndvar.h>
-
 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(&gtk[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(&gtk[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)
 {
index 33654eb..b18bcd3 100644 (file)
@@ -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 (file)
index 0000000..b733862
--- /dev/null
@@ -0,0 +1,916 @@
+/*-
+ * Copyright (c) 2007,2008 Damien Bergamini <damien.bergamini@free.fr>
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+
+#include <dev/rndvar.h>
+
+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(&gtk[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(&gtk[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 (file)
index 0000000..30dc8b9
--- /dev/null
@@ -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 <damien.bergamini@free.fr>
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/endian.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_arp.h>
+#include <net/if_llc.h>
+#include <net/bpf.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#endif
+
+#include <net80211/ieee80211_var.h>
+
+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);
+}
index 27c0b95..1a5698a 100644 (file)
@@ -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 *,