+
+
+static int ibss_rsn_eapol_dst_supp(const u8 *buf, size_t len)
+{
+ const struct ieee802_1x_hdr *hdr;
+ const struct wpa_eapol_key *key;
+ u16 key_info;
+ size_t plen;
+
+ /* TODO: Support other EAPOL packets than just EAPOL-Key */
+
+ if (len < sizeof(*hdr) + sizeof(*key))
+ return -1;
+
+ hdr = (const struct ieee802_1x_hdr *) buf;
+ key = (const struct wpa_eapol_key *) (hdr + 1);
+ plen = be_to_host16(hdr->length);
+
+ if (hdr->version < EAPOL_VERSION) {
+ /* TODO: backwards compatibility */
+ }
+ if (hdr->type != IEEE802_1X_TYPE_EAPOL_KEY) {
+ wpa_printf(MSG_DEBUG, "RSN: EAPOL frame (type %u) discarded, "
+ "not a Key frame", hdr->type);
+ return -1;
+ }
+ if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
+ wpa_printf(MSG_DEBUG, "RSN: EAPOL frame payload size %lu "
+ "invalid (frame size %lu)",
+ (unsigned long) plen, (unsigned long) len);
+ return -1;
+ }
+
+ if (key->type != EAPOL_KEY_TYPE_RSN) {
+ wpa_printf(MSG_DEBUG, "RSN: EAPOL-Key type (%d) unknown, "
+ "discarded", key->type);
+ return -1;
+ }
+
+ key_info = WPA_GET_BE16(key->key_info);
+
+ return !!(key_info & WPA_KEY_INFO_ACK);
+}
+
+
+static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn,
+ struct ibss_rsn_peer *peer,
+ const u8 *buf, size_t len)
+{
+ int supp;
+ u8 *tmp;
+
+ supp = ibss_rsn_eapol_dst_supp(buf, len);
+ if (supp < 0)
+ return -1;
+
+ tmp = os_malloc(len);
+ if (tmp == NULL)
+ return -1;
+ os_memcpy(tmp, buf, len);
+ if (supp) {
+ peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER;
+ wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from "
+ MACSTR, MAC2STR(peer->addr));
+ wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len);
+ } else {
+ if (ibss_rsn_is_auth_started(peer) == 0) {
+ wpa_printf(MSG_DEBUG, "RSN: IBSS EAPOL for "
+ "Authenticator dropped as " MACSTR " is not "
+ "authenticated", MAC2STR(peer->addr));
+ os_free(tmp);
+ return -1;
+ }
+
+ wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator "
+ "from "MACSTR, MAC2STR(peer->addr));
+ wpa_receive(ibss_rsn->auth_group, peer->auth, tmp, len);
+ }
+ os_free(tmp);
+
+ return 1;
+}
+
+
+int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
+ const u8 *buf, size_t len)
+{
+ struct ibss_rsn_peer *peer;
+
+ if (ibss_rsn == NULL)
+ return -1;
+
+ peer = ibss_rsn_get_peer(ibss_rsn, src_addr);
+ if (peer)
+ return ibss_rsn_process_rx_eapol(ibss_rsn, peer, buf, len);
+
+ if (ibss_rsn_eapol_dst_supp(buf, len) > 0) {
+ /*
+ * Create new IBSS peer based on an EAPOL message from the peer
+ * Authenticator.
+ */
+ peer = ibss_rsn_peer_init(ibss_rsn, src_addr);
+ if (peer == NULL)
+ return -1;
+
+ /* assume the peer is authenticated already */
+ wpa_printf(MSG_DEBUG, "RSN: IBSS Not using IBSS Auth for peer "
+ MACSTR, MAC2STR(src_addr));
+ ibss_rsn_peer_authenticated(ibss_rsn, peer,
+ IBSS_RSN_AUTH_EAPOL_BY_US);
+
+ return ibss_rsn_process_rx_eapol(ibss_rsn, ibss_rsn->peers,
+ buf, len);
+ }
+
+ return 0;
+}
+
+void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk)
+{
+ if (ibss_rsn == NULL)
+ return;
+ os_memcpy(ibss_rsn->psk, psk, PMK_LEN);
+}
+
+
+static void ibss_rsn_handle_auth_1_of_2(struct ibss_rsn *ibss_rsn,
+ struct ibss_rsn_peer *peer,
+ const u8* addr)
+{
+ wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 1) from " MACSTR,
+ MAC2STR(addr));
+
+ if (peer &&
+ peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) {
+ if (peer->own_auth_tx.sec) {
+ struct os_reltime now, diff;
+ os_get_reltime(&now);
+ os_reltime_sub(&now, &peer->own_auth_tx, &diff);
+ if (diff.sec == 0 && diff.usec < 500000) {
+ wpa_printf(MSG_DEBUG, "RSN: Skip IBSS reinit since only %u usec from own Auth frame TX",
+ (int) diff.usec);
+ goto skip_reinit;
+ }
+ }
+ /*
+ * A peer sent us an Authentication frame even though it already
+ * started an EAPOL session. We should reinit state machines
+ * here, but it's much more complicated than just deleting and
+ * recreating the state machine
+ */
+ wpa_printf(MSG_DEBUG, "RSN: IBSS Reinitializing station "
+ MACSTR, MAC2STR(addr));
+
+ ibss_rsn_stop(ibss_rsn, addr);
+ peer = NULL;
+ }
+
+ if (!peer) {
+ peer = ibss_rsn_peer_init(ibss_rsn, addr);
+ if (!peer)
+ return;
+
+ wpa_printf(MSG_DEBUG, "RSN: IBSS Auth started by peer " MACSTR,
+ MAC2STR(addr));
+ }
+
+skip_reinit:
+ /* reply with an Authentication frame now, before sending an EAPOL */
+ ibss_rsn_send_auth(ibss_rsn, addr, 2);
+ /* no need to start another AUTH challenge in the other way.. */
+ ibss_rsn_peer_authenticated(ibss_rsn, peer, IBSS_RSN_AUTH_EAPOL_BY_US);
+}
+
+
+void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame,
+ size_t len)
+{
+ const struct ieee80211_mgmt *header;
+ struct ibss_rsn_peer *peer;
+ size_t auth_length;
+
+ header = (const struct ieee80211_mgmt *) auth_frame;
+ auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth);
+
+ if (ibss_rsn == NULL || len < auth_length)
+ return;
+
+ if (le_to_host16(header->u.auth.auth_alg) != WLAN_AUTH_OPEN ||
+ le_to_host16(header->u.auth.status_code) != WLAN_STATUS_SUCCESS)
+ return;
+
+ peer = ibss_rsn_get_peer(ibss_rsn, header->sa);
+
+ switch (le_to_host16(header->u.auth.auth_transaction)) {
+ case 1:
+ ibss_rsn_handle_auth_1_of_2(ibss_rsn, peer, header->sa);
+ break;
+ case 2:
+ wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 2) from "
+ MACSTR, MAC2STR(header->sa));
+ if (!peer) {
+ wpa_printf(MSG_DEBUG, "RSN: Received Auth seq 2 from "
+ "unknown STA " MACSTR, MAC2STR(header->sa));
+ break;
+ }
+
+ /* authentication has been completed */
+ eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL);
+ wpa_printf(MSG_DEBUG, "RSN: IBSS Auth completed with " MACSTR,
+ MAC2STR(header->sa));
+ ibss_rsn_peer_authenticated(ibss_rsn, peer,
+ IBSS_RSN_AUTH_BY_US);
+ break;
+ }
+}