Updated through tag hostap_2_5 from git://w1.fi/hostap.git
[mech_eap.git] / libeap / wpa_supplicant / ibss_rsn.c
index 0e33253..d9d0ae7 100644 (file)
@@ -1,31 +1,44 @@
 /*
  * wpa_supplicant - IBSS RSN
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2013, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "includes.h"
 
 #include "common.h"
+#include "common/wpa_ctrl.h"
+#include "utils/eloop.h"
 #include "l2_packet/l2_packet.h"
 #include "rsn_supp/wpa.h"
 #include "rsn_supp/wpa_ie.h"
 #include "ap/wpa_auth.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
+#include "common/ieee802_11_defs.h"
 #include "ibss_rsn.h"
 
 
+static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
+static struct ibss_rsn_peer * ibss_rsn_get_peer(struct ibss_rsn *ibss_rsn,
+                                               const u8 *addr)
+{
+       struct ibss_rsn_peer *peer;
+
+       for (peer = ibss_rsn->peers; peer; peer = peer->next)
+               if (os_memcmp(addr, peer->addr, ETH_ALEN) == 0)
+                       break;
+       return peer;
+}
+
+
 static void ibss_rsn_free(struct ibss_rsn_peer *peer)
 {
+       eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL);
        wpa_auth_sta_deinit(peer->auth);
        wpa_sm_deinit(peer->supp);
        os_free(peer);
@@ -39,6 +52,13 @@ static void supp_set_state(void *ctx, enum wpa_states state)
 }
 
 
+static enum wpa_states supp_get_state(void *ctx)
+{
+       struct ibss_rsn_peer *peer = ctx;
+       return peer->supp_state;
+}
+
+
 static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
                           size_t len)
 {
@@ -52,7 +72,7 @@ static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
        if (wpa_s->l2)
                return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
 
-       return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len);
+       return -1;
 }
 
 
@@ -100,6 +120,22 @@ static int supp_get_beacon_ie(void *ctx)
 }
 
 
+static void ibss_check_rsn_completed(struct ibss_rsn_peer *peer)
+{
+       struct wpa_supplicant *wpa_s = peer->ibss_rsn->wpa_s;
+
+       if ((peer->authentication_status &
+            (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH)) !=
+           (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH))
+               return;
+       if (peer->authentication_status & IBSS_RSN_REPORTED_PTK)
+               return;
+       peer->authentication_status |= IBSS_RSN_REPORTED_PTK;
+       wpa_msg(wpa_s, MSG_INFO, IBSS_RSN_COMPLETED MACSTR,
+               MAC2STR(peer->addr));
+}
+
+
 static int supp_set_key(void *ctx, enum wpa_alg alg,
                        const u8 *addr, int key_idx, int set_tx,
                        const u8 *seq, size_t seq_len,
@@ -114,6 +150,8 @@ static int supp_set_key(void *ctx, enum wpa_alg alg,
        wpa_hexdump_key(MSG_DEBUG, "SUPP: set_key - key", key, key_len);
 
        if (key_idx == 0) {
+               peer->authentication_status |= IBSS_RSN_SET_PTK_SUPP;
+               ibss_check_rsn_completed(peer);
                /*
                 * In IBSS RSN, the pairwise key from the 4-way handshake
                 * initiated by the peer with highest MAC address is used.
@@ -125,6 +163,8 @@ static int supp_set_key(void *ctx, enum wpa_alg alg,
                }
        }
 
+       if (is_broadcast_ether_addr(addr))
+               addr = peer->addr;
        return wpa_drv_set_key(peer->ibss_rsn->wpa_s, alg, addr, key_idx,
                               set_tx, seq, seq_len, key, key_len);
 }
@@ -153,8 +193,14 @@ static void supp_cancel_auth_timeout(void *ctx)
 }
 
 
-int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
-                      const u8 *psk)
+static void supp_deauthenticate(void * ctx, int reason_code)
+{
+       wpa_printf(MSG_DEBUG, "SUPP: %s (TODO)", __func__);
+}
+
+
+static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
+                             const u8 *psk)
 {
        struct wpa_sm_ctx *ctx = os_zalloc(sizeof(*ctx));
        if (ctx == NULL)
@@ -163,6 +209,7 @@ int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
        ctx->ctx = peer;
        ctx->msg_ctx = peer->ibss_rsn->wpa_s;
        ctx->set_state = supp_set_state;
+       ctx->get_state = supp_get_state;
        ctx->ether_send = supp_ether_send;
        ctx->get_beacon_ie = supp_get_beacon_ie;
        ctx->alloc_eapol = supp_alloc_eapol;
@@ -170,6 +217,7 @@ int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
        ctx->get_network_ctx = supp_get_network_ctx;
        ctx->mlme_setprotection = supp_mlme_setprotection;
        ctx->cancel_auth_timeout = supp_cancel_auth_timeout;
+       ctx->deauthenticate = supp_deauthenticate;
        peer->supp = wpa_sm_init(ctx);
        if (peer->supp == NULL) {
                wpa_printf(MSG_DEBUG, "SUPP: wpa_sm_init() failed");
@@ -182,7 +230,7 @@ int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
        wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP);
        wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
        wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK);
-       wpa_sm_set_pmk(peer->supp, psk, PMK_LEN);
+       wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL);
 
        peer->supp_ie_len = sizeof(peer->supp_ie);
        if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie,
@@ -209,7 +257,8 @@ static void auth_logger(void *ctx, const u8 *addr, logger_level level,
 }
 
 
-static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk)
+static const u8 * auth_get_psk(void *ctx, const u8 *addr,
+                              const u8 *p2p_dev_addr, const u8 *prev_psk)
 {
        struct ibss_rsn *ibss_rsn = ctx;
        wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
@@ -234,7 +283,7 @@ static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
                return l2_packet_send(wpa_s->l2, addr, ETH_P_EAPOL, data,
                                      data_len);
 
-       return wpa_drv_send_eapol(wpa_s, addr, ETH_P_EAPOL, data, data_len);
+       return -1;
 }
 
 
@@ -257,6 +306,15 @@ static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
        wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
 
        if (idx == 0) {
+               if (addr) {
+                       struct ibss_rsn_peer *peer;
+                       peer = ibss_rsn_get_peer(ibss_rsn, addr);
+                       if (peer) {
+                               peer->authentication_status |=
+                                       IBSS_RSN_SET_PTK_AUTH;
+                               ibss_check_rsn_completed(peer);
+                       }
+               }
                /*
                 * In IBSS RSN, the pairwise key from the 4-way handshake
                 * initiated by the peer with highest MAC address is used.
@@ -273,6 +331,78 @@ static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
 }
 
 
+static void ibss_rsn_disconnect(void *ctx, const u8 *addr, u16 reason)
+{
+       struct ibss_rsn *ibss_rsn = ctx;
+       wpa_drv_sta_deauth(ibss_rsn->wpa_s, addr, reason);
+}
+
+
+static int auth_for_each_sta(void *ctx, int (*cb)(struct wpa_state_machine *sm,
+                                                 void *ctx),
+                            void *cb_ctx)
+{
+       struct ibss_rsn *ibss_rsn = ctx;
+       struct ibss_rsn_peer *peer;
+
+       wpa_printf(MSG_DEBUG, "AUTH: for_each_sta");
+
+       for (peer = ibss_rsn->peers; peer; peer = peer->next) {
+               if (peer->auth && cb(peer->auth, cb_ctx))
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static void ibss_set_sta_authorized(struct ibss_rsn *ibss_rsn,
+                                   struct ibss_rsn_peer *peer, int authorized)
+{
+       int res;
+
+       if (authorized) {
+               res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr,
+                                           WPA_STA_AUTHORIZED,
+                                           WPA_STA_AUTHORIZED, ~0);
+               wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " authorizing port",
+                          MAC2STR(peer->addr));
+       } else {
+               res = wpa_drv_sta_set_flags(ibss_rsn->wpa_s, peer->addr,
+                                           0, 0, ~WPA_STA_AUTHORIZED);
+               wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " unauthorizing port",
+                          MAC2STR(peer->addr));
+       }
+
+       if (res && errno != ENOENT) {
+               wpa_printf(MSG_DEBUG, "Could not set station " MACSTR " flags "
+                          "for kernel driver (errno=%d)",
+                          MAC2STR(peer->addr), errno);
+       }
+}
+
+
+static void auth_set_eapol(void *ctx, const u8 *addr,
+                                      wpa_eapol_variable var, int value)
+{
+       struct ibss_rsn *ibss_rsn = ctx;
+       struct ibss_rsn_peer *peer = ibss_rsn_get_peer(ibss_rsn, addr);
+
+       if (peer == NULL)
+               return;
+
+       switch (var) {
+       case WPA_EAPOL_authorized:
+               ibss_set_sta_authorized(ibss_rsn, peer, value);
+               break;
+       default:
+               /* do not handle any other event */
+               wpa_printf(MSG_DEBUG, "AUTH: eapol event not handled %d", var);
+               break;
+       }
+}
+
+
 static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
                                    const u8 *own_addr)
 {
@@ -288,13 +418,17 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
        conf.rsn_pairwise = WPA_CIPHER_CCMP;
        conf.wpa_group = WPA_CIPHER_CCMP;
        conf.eapol_version = 2;
+       conf.wpa_group_rekey = 600;
 
        os_memset(&cb, 0, sizeof(cb));
        cb.ctx = ibss_rsn;
        cb.logger = auth_logger;
+       cb.set_eapol = auth_set_eapol;
        cb.send_eapol = auth_send_eapol;
        cb.get_psk = auth_get_psk;
        cb.set_key = auth_set_key;
+       cb.for_each_sta = auth_for_each_sta;
+       cb.disconnect = ibss_rsn_disconnect;
 
        ibss_rsn->auth_group = wpa_init(own_addr, &conf, &cb);
        if (ibss_rsn->auth_group == NULL) {
@@ -302,6 +436,8 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
                return -1;
        }
 
+       wpa_init_keys(ibss_rsn->auth_group);
+
        return 0;
 }
 
@@ -309,7 +445,7 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
 static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
                              struct ibss_rsn_peer *peer)
 {
-       peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr);
+       peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr, NULL);
        if (peer->auth == NULL) {
                wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
                return -1;
@@ -337,35 +473,195 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
 }
 
 
-int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
+static int ibss_rsn_send_auth(struct ibss_rsn *ibss_rsn, const u8 *da, int seq)
+{
+       struct ieee80211_mgmt auth;
+       const size_t auth_length = IEEE80211_HDRLEN + sizeof(auth.u.auth);
+       struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s;
+
+       if (wpa_s->driver->send_frame == NULL)
+               return -1;
+
+       os_memset(&auth, 0, sizeof(auth));
+
+       auth.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_AUTH);
+       os_memcpy(auth.da, da, ETH_ALEN);
+       os_memcpy(auth.sa, wpa_s->own_addr, ETH_ALEN);
+       os_memcpy(auth.bssid, wpa_s->bssid, ETH_ALEN);
+
+       auth.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN);
+       auth.u.auth.auth_transaction = host_to_le16(seq);
+       auth.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+
+       wpa_printf(MSG_DEBUG, "RSN: IBSS TX Auth frame (SEQ %d) to " MACSTR,
+                  seq, MAC2STR(da));
+
+       return wpa_s->driver->send_frame(wpa_s->drv_priv, (u8 *) &auth,
+                                        auth_length, 0);
+}
+
+
+static int ibss_rsn_is_auth_started(struct ibss_rsn_peer * peer)
+{
+       return peer->authentication_status &
+              (IBSS_RSN_AUTH_BY_US | IBSS_RSN_AUTH_EAPOL_BY_US);
+}
+
+
+static struct ibss_rsn_peer *
+ibss_rsn_peer_init(struct ibss_rsn *ibss_rsn, const u8 *addr)
 {
        struct ibss_rsn_peer *peer;
+       if (ibss_rsn == NULL)
+               return NULL;
+
+       peer = ibss_rsn_get_peer(ibss_rsn, addr);
+       if (peer) {
+               wpa_printf(MSG_DEBUG, "RSN: IBSS Supplicant for peer "MACSTR
+                          " already running", MAC2STR(addr));
+               return peer;
+       }
 
-       wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and "
-                  "Supplicant for peer " MACSTR, MAC2STR(addr));
+       wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Supplicant for peer "MACSTR,
+                  MAC2STR(addr));
 
        peer = os_zalloc(sizeof(*peer));
-       if (peer == NULL)
-               return -1;
+       if (peer == NULL) {
+               wpa_printf(MSG_DEBUG, "RSN: Could not allocate memory.");
+               return NULL;
+       }
 
        peer->ibss_rsn = ibss_rsn;
        os_memcpy(peer->addr, addr, ETH_ALEN);
+       peer->authentication_status = IBSS_RSN_AUTH_NOT_AUTHENTICATED;
 
-       if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr, ibss_rsn->psk)
-           < 0) {
+       if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr,
+                              ibss_rsn->psk) < 0) {
                ibss_rsn_free(peer);
+               return NULL;
+       }
+
+       peer->next = ibss_rsn->peers;
+       ibss_rsn->peers = peer;
+
+       return peer;
+}
+
+
+static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct ibss_rsn_peer *peer = eloop_ctx;
+
+       /*
+        * Assume peer does not support Authentication exchange or the frame was
+        * lost somewhere - start EAPOL Authenticator.
+        */
+       wpa_printf(MSG_DEBUG,
+                  "RSN: Timeout on waiting Authentication frame response from "
+                  MACSTR " - start authenticator", MAC2STR(peer->addr));
+
+       peer->authentication_status |= IBSS_RSN_AUTH_BY_US;
+       ibss_rsn_auth_init(peer->ibss_rsn, peer);
+}
+
+
+int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
+{
+       struct ibss_rsn_peer *peer;
+       int res;
+
+       if (!ibss_rsn)
                return -1;
+
+       /* if the peer already exists, exit immediately */
+       peer = ibss_rsn_get_peer(ibss_rsn, addr);
+       if (peer)
+               return 0;
+
+       peer = ibss_rsn_peer_init(ibss_rsn, addr);
+       if (peer == NULL)
+               return -1;
+
+       /* Open Authentication: send first Authentication frame */
+       res = ibss_rsn_send_auth(ibss_rsn, addr, 1);
+       if (res) {
+               /*
+                * The driver may not support Authentication frame exchange in
+                * IBSS. Ignore authentication and go through EAPOL exchange.
+                */
+               peer->authentication_status |= IBSS_RSN_AUTH_BY_US;
+               return ibss_rsn_auth_init(ibss_rsn, peer);
+       } else {
+               os_get_reltime(&peer->own_auth_tx);
+               eloop_register_timeout(1, 0, ibss_rsn_auth_timeout, peer, NULL);
        }
 
-       if (ibss_rsn_auth_init(ibss_rsn, peer) < 0) {
-               ibss_rsn_free(peer);
+       return 0;
+}
+
+
+static int ibss_rsn_peer_authenticated(struct ibss_rsn *ibss_rsn,
+                                      struct ibss_rsn_peer *peer, int reason)
+{
+       int already_started;
+
+       if (ibss_rsn == NULL || peer == NULL)
                return -1;
+
+       already_started = ibss_rsn_is_auth_started(peer);
+       peer->authentication_status |= reason;
+
+       if (already_started) {
+               wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator already "
+                          "started for peer " MACSTR, MAC2STR(peer->addr));
+               return 0;
        }
 
-       peer->next = ibss_rsn->peers;
-       ibss_rsn->peers = peer;
+       wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator "
+                  "for now-authenticated peer " MACSTR, MAC2STR(peer->addr));
 
-       return 0;
+       return ibss_rsn_auth_init(ibss_rsn, peer);
+}
+
+
+void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac)
+{
+       struct ibss_rsn_peer *peer, *prev;
+
+       if (ibss_rsn == NULL)
+               return;
+
+       if (peermac == NULL) {
+               /* remove all peers */
+               wpa_printf(MSG_DEBUG, "%s: Remove all peers", __func__);
+               peer = ibss_rsn->peers;
+               while (peer) {
+                       prev = peer;
+                       peer = peer->next;
+                       ibss_rsn_free(prev);
+                       ibss_rsn->peers = peer;
+               }
+       } else {
+               /* remove specific peer */
+               wpa_printf(MSG_DEBUG, "%s: Remove specific peer " MACSTR,
+                          __func__, MAC2STR(peermac));
+
+               for (prev = NULL, peer = ibss_rsn->peers; peer != NULL;
+                    prev = peer, peer = peer->next) {
+                       if (os_memcmp(peermac, peer->addr, ETH_ALEN) == 0) {
+                               if (prev == NULL)
+                                       ibss_rsn->peers = peer->next;
+                               else
+                                       prev->next = peer->next;
+                               ibss_rsn_free(peer);
+                               wpa_printf(MSG_DEBUG, "%s: Successfully "
+                                          "removed a specific peer",
+                                          __func__);
+                               break;
+                       }
+               }
+       }
 }
 
 
@@ -401,7 +697,8 @@ void ibss_rsn_deinit(struct ibss_rsn *ibss_rsn)
                ibss_rsn_free(prev);
        }
 
-       wpa_deinit(ibss_rsn->auth_group);
+       if (ibss_rsn->auth_group)
+               wpa_deinit(ibss_rsn->auth_group);
        os_free(ibss_rsn);
 
 }
@@ -466,10 +763,21 @@ static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn,
                return -1;
        os_memcpy(tmp, buf, len);
        if (supp) {
-               wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant");
+               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 {
-               wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator");
+               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);
@@ -483,19 +791,28 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
 {
        struct ibss_rsn_peer *peer;
 
-       for (peer = ibss_rsn->peers; peer; peer = peer->next) {
-               if (os_memcmp(src_addr, peer->addr, ETH_ALEN) == 0)
-                       return ibss_rsn_process_rx_eapol(ibss_rsn, peer,
-                                                        buf, len);
-       }
+       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.
                 */
-               if (ibss_rsn_start(ibss_rsn, src_addr) < 0)
+               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);
        }
@@ -503,8 +820,101 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
        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;
+       }
+}