wpa_supplicant: Initial Revision 0.8.X

Based on:
commit 0725cc7b7efc434910e89865c42eda7ce61bbf08
Author: Jouni Malinen <j@w1.fi>
Date:   Thu Apr 21 20:41:01 2011 +0300

    Enable CONFIG_DRIVER_NL80211=y in the default configuration

    nl80211 should be preferred over WEXT with any recent Linux
    kernel version.

Change-Id: I26aec5afbbd4f4a1f5fd900912545b6f5050de64
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/src/ap/preauth_auth.c b/src/ap/preauth_auth.c
new file mode 100644
index 0000000..8e13315
--- /dev/null
+++ b/src/ap/preauth_auth.c
@@ -0,0 +1,279 @@
+/*
+ * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication
+ * Copyright (c) 2004-2007, 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.
+ */
+
+#include "utils/includes.h"
+
+#ifdef CONFIG_RSN_PREAUTH
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "l2_packet/l2_packet.h"
+#include "common/wpa_common.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
+#include "hostapd.h"
+#include "ap_config.h"
+#include "ieee802_1x.h"
+#include "sta_info.h"
+#include "wpa_auth.h"
+#include "preauth_auth.h"
+
+#ifndef ETH_P_PREAUTH
+#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */
+#endif /* ETH_P_PREAUTH */
+
+static const int dot11RSNAConfigPMKLifetime = 43200;
+
+struct rsn_preauth_interface {
+	struct rsn_preauth_interface *next;
+	struct hostapd_data *hapd;
+	struct l2_packet_data *l2;
+	char *ifname;
+	int ifindex;
+};
+
+
+static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
+				const u8 *buf, size_t len)
+{
+	struct rsn_preauth_interface *piface = ctx;
+	struct hostapd_data *hapd = piface->hapd;
+	struct ieee802_1x_hdr *hdr;
+	struct sta_info *sta;
+	struct l2_ethhdr *ethhdr;
+
+	wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet "
+		   "from interface '%s'", piface->ifname);
+	if (len < sizeof(*ethhdr) + sizeof(*hdr)) {
+		wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet "
+			   "(len=%lu)", (unsigned long) len);
+		return;
+	}
+
+	ethhdr = (struct l2_ethhdr *) buf;
+	hdr = (struct ieee802_1x_hdr *) (ethhdr + 1);
+
+	if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) {
+		wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address "
+			   MACSTR, MAC2STR(ethhdr->h_dest));
+		return;
+	}
+
+	sta = ap_get_sta(hapd, ethhdr->h_source);
+	if (sta && (sta->flags & WLAN_STA_ASSOC)) {
+		wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association "
+			   "STA " MACSTR, MAC2STR(sta->addr));
+		return;
+	}
+	if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) {
+		sta = ap_sta_add(hapd, ethhdr->h_source);
+		if (sta == NULL)
+			return;
+		sta->flags = WLAN_STA_PREAUTH;
+
+		ieee802_1x_new_station(hapd, sta);
+		if (sta->eapol_sm == NULL) {
+			ap_free_sta(hapd, sta);
+			sta = NULL;
+		} else {
+			sta->eapol_sm->radius_identifier = -1;
+			sta->eapol_sm->portValid = TRUE;
+			sta->eapol_sm->flags |= EAPOL_SM_PREAUTH;
+		}
+	}
+	if (sta == NULL)
+		return;
+	sta->preauth_iface = piface;
+	ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1),
+			   len - sizeof(*ethhdr));
+}
+
+
+static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname)
+{
+	struct rsn_preauth_interface *piface;
+
+	wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname);
+
+	piface = os_zalloc(sizeof(*piface));
+	if (piface == NULL)
+		return -1;
+	piface->hapd = hapd;
+
+	piface->ifname = os_strdup(ifname);
+	if (piface->ifname == NULL) {
+		goto fail1;
+	}
+
+	piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH,
+				    rsn_preauth_receive, piface, 1);
+	if (piface->l2 == NULL) {
+		wpa_printf(MSG_ERROR, "Failed to open register layer 2 access "
+			   "to ETH_P_PREAUTH");
+		goto fail2;
+	}
+
+	piface->next = hapd->preauth_iface;
+	hapd->preauth_iface = piface;
+	return 0;
+
+fail2:
+	os_free(piface->ifname);
+fail1:
+	os_free(piface);
+	return -1;
+}
+
+
+void rsn_preauth_iface_deinit(struct hostapd_data *hapd)
+{
+	struct rsn_preauth_interface *piface, *prev;
+
+	piface = hapd->preauth_iface;
+	hapd->preauth_iface = NULL;
+	while (piface) {
+		prev = piface;
+		piface = piface->next;
+		l2_packet_deinit(prev->l2);
+		os_free(prev->ifname);
+		os_free(prev);
+	}
+}
+
+
+int rsn_preauth_iface_init(struct hostapd_data *hapd)
+{
+	char *tmp, *start, *end;
+
+	if (hapd->conf->rsn_preauth_interfaces == NULL)
+		return 0;
+
+	tmp = os_strdup(hapd->conf->rsn_preauth_interfaces);
+	if (tmp == NULL)
+		return -1;
+	start = tmp;
+	for (;;) {
+		while (*start == ' ')
+			start++;
+		if (*start == '\0')
+			break;
+		end = os_strchr(start, ' ');
+		if (end)
+			*end = '\0';
+
+		if (rsn_preauth_iface_add(hapd, start)) {
+			rsn_preauth_iface_deinit(hapd);
+			os_free(tmp);
+			return -1;
+		}
+
+		if (end)
+			start = end + 1;
+		else
+			break;
+	}
+	os_free(tmp);
+	return 0;
+}
+
+
+static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx)
+{
+	struct hostapd_data *hapd = eloop_ctx;
+	struct sta_info *sta = timeout_ctx;
+	wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for "
+		   MACSTR, MAC2STR(sta->addr));
+	ap_free_sta(hapd, sta);
+}
+
+
+void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta,
+			  int success)
+{
+	const u8 *key;
+	size_t len;
+	hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+		       HOSTAPD_LEVEL_INFO, "pre-authentication %s",
+		       success ? "succeeded" : "failed");
+
+	key = ieee802_1x_get_key(sta->eapol_sm, &len);
+	if (len > PMK_LEN)
+		len = PMK_LEN;
+	if (success && key) {
+		if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len,
+					       sta->addr,
+					       dot11RSNAConfigPMKLifetime,
+					       sta->eapol_sm) == 0) {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "added PMKSA cache entry (pre-auth)");
+		} else {
+			hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
+				       HOSTAPD_LEVEL_DEBUG,
+				       "failed to add PMKSA cache entry "
+				       "(pre-auth)");
+		}
+	}
+
+	/*
+	 * Finish STA entry removal from timeout in order to avoid freeing
+	 * STA data before the caller has finished processing.
+	 */
+	eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta);
+}
+
+
+void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta,
+		      u8 *buf, size_t len)
+{
+	struct rsn_preauth_interface *piface;
+	struct l2_ethhdr *ethhdr;
+
+	piface = hapd->preauth_iface;
+	while (piface) {
+		if (piface == sta->preauth_iface)
+			break;
+		piface = piface->next;
+	}
+
+	if (piface == NULL) {
+		wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication "
+			   "interface for " MACSTR, MAC2STR(sta->addr));
+		return;
+	}
+
+	ethhdr = os_malloc(sizeof(*ethhdr) + len);
+	if (ethhdr == NULL)
+		return;
+
+	os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN);
+	os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN);
+	ethhdr->h_proto = host_to_be16(ETH_P_PREAUTH);
+	os_memcpy(ethhdr + 1, buf, len);
+
+	if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr,
+			   sizeof(*ethhdr) + len) < 0) {
+		wpa_printf(MSG_ERROR, "Failed to send preauth packet using "
+			   "l2_packet_send\n");
+	}
+	os_free(ethhdr);
+}
+
+
+void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta)
+{
+	eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta);
+}
+
+#endif /* CONFIG_RSN_PREAUTH */