diff mbox

[RFC,1/3] Client station blacklist support

Message ID CACdfkSJ7ZZLbPkrd=BhkEs79wMz6pgkHKR6qE53KxL7yHX0DNw@mail.gmail.com
State RFC
Headers show

Commit Message

David Weidenkopf July 20, 2016, 12:33 a.m. UTC
This patch implements hostapd-based client station blacklisting. It
includes new CLI commands to add, remove, clear, and show the blacklist.
Client stations in the blacklist will be denied authentication,
association, and re-association with the AP. The AP will not respond to
probe requests from clients in the blacklist.
diff mbox

Patch

From 709f5fdc2cf5bcb4660389fbe291192c349e9c9d Mon Sep 17 00:00:00 2001
From: David Weidenkopf <dweidenkopf@cococorp.com>
Date: Thu, 7 Jul 2016 14:11:43 -0700
Subject: [PATCH] WNM: hostapd client station blacklist support. New CLI
 commands to add, remove, clear, and show the blacklist.

Signed-off-by: Henry M. Bennett <hbennett@cococorp.com>
Signed-off-by: Alexis Green <agreen@cococorp.com>
Signed-off-by: David Weidenkopf <dweidenkopf@cococorp.com>
---
 hostapd/Makefile       |   2 +-
 hostapd/ctrl_iface.c   |  18 +++++++
 hostapd/hostapd_cli.c  |  72 ++++++++++++++++++++++++-
 src/ap/beacon.c        |   6 +++
 src/ap/ctrl_iface_ap.c |  65 ++++++++++++++++++++++-
 src/ap/ctrl_iface_ap.h |   8 ++-
 src/ap/hostapd.c       |   1 +
 src/ap/hostapd.h       |   3 ++
 src/ap/ieee802_11.c    |  12 ++++-
 src/ap/sta_blacklist.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/ap/sta_blacklist.h |  14 +++++
 11 files changed, 335 insertions(+), 6 deletions(-)
 create mode 100644 src/ap/sta_blacklist.c
 create mode 100644 src/ap/sta_blacklist.h

diff --git a/hostapd/Makefile b/hostapd/Makefile
index baa7819..2d0bdc2 100644
--- a/hostapd/Makefile
+++ b/hostapd/Makefile
@@ -63,7 +63,6 @@  endif
 
 OBJS += main.o
 OBJS += config_file.o
-
 OBJS += ../src/ap/hostapd.o
 OBJS += ../src/ap/wpa_auth_glue.o
 OBJS += ../src/ap/drv_callbacks.o
@@ -86,6 +85,7 @@  OBJS += ../src/ap/beacon.o
 OBJS += ../src/ap/bss_load.o
 OBJS += ../src/ap/neighbor_db.o
 OBJS += ../src/ap/rrm.o
+OBJS += ../src/ap/sta_blacklist.o
 
 OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o
 
diff --git a/hostapd/ctrl_iface.c b/hostapd/ctrl_iface.c
index 62bef18..d037d3d 100644
--- a/hostapd/ctrl_iface.c
+++ b/hostapd/ctrl_iface.c
@@ -2488,6 +2488,24 @@  static int hostapd_ctrl_iface_receive_process(struct hostapd_data *hapd,
 	} else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
 		reply_len = hostapd_ctrl_iface_log_level(
 			hapd, buf + 9, reply, reply_size);
+	/* BEGIN HOSTAPD BLACKLIST SUPPORT */
+	} else if (os_strncmp(buf, "BLACKLIST_ADD ", 14) == 0) {
+			if (hostapd_ctrl_iface_blacklist_add(hapd, buf + 14))
+				reply_len = -1;
+	} else if (os_strncmp(buf, "BLACKLIST_RM ", 13) == 0) {
+			if (hostapd_ctrl_iface_blacklist_rm(hapd, buf + 13))
+				reply_len = -1;
+	} else if (os_strcmp(buf, "BLACKLIST_SHOW") == 0) {
+		reply_len = hostapd_ctrl_iface_blacklist_show(hapd, reply,
+						reply_size);
+		if(reply_len == 0) {
+			os_memcpy(reply, "EMPTY\n", 6);
+			reply_len = 6;
+		}
+	} else if (os_strcmp(buf, "BLACKLIST_CLR") == 0) {
+		if(hostapd_ctrl_iface_blacklist_clr(hapd))
+			reply_len = -1;
+	/* END HOSTAPD BLACKLIST SUPPORT */
 #ifdef NEED_AP_MLME
 	} else if (os_strcmp(buf, "TRACK_STA_LIST") == 0) {
 		reply_len = hostapd_ctrl_iface_track_sta_list(
diff --git a/hostapd/hostapd_cli.c b/hostapd/hostapd_cli.c
index ff133f6..ce6aecf 100644
--- a/hostapd/hostapd_cli.c
+++ b/hostapd/hostapd_cli.c
@@ -65,6 +65,11 @@  static const char *const commands_help =
 "   all_sta              get MIB variables for all stations\n"
 "   new_sta <addr>       add a new station\n"
 "   deauthenticate <addr>  deauthenticate a station\n"
+"   blacklist_add <addr>  blacklist a station from an AP\n"
+"   blacklist_rm <addr>   remove a station from the blacklist\n"
+"   blacklist_clr         clear all stations from the blacklist\n"
+"   blacklist_show        show entire blacklist\n"
+"   bss_transition <addr> <beacon timer count> <AP addr> <AP channel> disassociate a WNM station telling the station which AP to connect to\n"
 "   disassociate <addr>  disassociate a station\n"
 #ifdef CONFIG_IEEE80211W
 "   sa_query <addr>      send SA Query to a station\n"
@@ -351,7 +356,6 @@  static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
 	return wpa_ctrl_command(ctrl, buf);
 }
 
-
 static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
 				   char *argv[])
 {
@@ -366,6 +370,65 @@  static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+/* BEGIN HOSTAPD BLACKLIST SUPPORT */
+static int hostapd_cli_cmd_blacklist_add_hostapd(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	char buf[64];
+
+	if (argc != 1) {
+		printf("Invalid 'blacklist_add_hostapd' command - exactly one argument, STA address, is required.\n");
+		return -1;
+	}
+
+	os_snprintf(buf, sizeof(buf), "BLACKLIST_ADD %s", argv[0]);
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+static int hostapd_cli_cmd_blacklist_rm_hostapd(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	char buf[64];
+
+	if (argc != 1) {
+		printf("Invalid 'blacklist_rm_hostapd' command - exactly one argument, STA address, is required.\n");
+		return -1;
+	}
+
+	os_snprintf(buf, sizeof(buf), "BLACKLIST_RM %s", argv[0]);
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+static int hostapd_cli_cmd_blacklist_show(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	char buf[64];
+
+	if (argc > 0) {
+		printf("Invalid 'blacklist_show' command - this command takes no arguments.\n");
+		return -1;
+	}
+
+	os_snprintf(buf, sizeof(buf), "BLACKLIST_SHOW");
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+static int hostapd_cli_cmd_blacklist_clr_hostapd(struct wpa_ctrl *ctrl, int argc,
+					char *argv[])
+{
+	char buf[64];
+
+	if (argc != 0) {
+		printf("Invalid 'blacklist_add_hostapd' command - exactly zero arguments is required.\n");
+		return -1;
+	}
+
+	os_snprintf(buf, sizeof(buf), "BLACKLIST_CLR");
+	return wpa_ctrl_command(ctrl, buf);
+}
+
+/* END HOSTAPD BLACKLIST SUPPORT */
+
 static int hostapd_cli_cmd_deauthenticate(struct wpa_ctrl *ctrl, int argc,
 					  char *argv[])
 {
@@ -1102,7 +1165,6 @@  static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc,
 	return wpa_ctrl_command(ctrl, "ERP_FLUSH");
 }
 
-
 static int hostapd_cli_cmd_log_level(struct wpa_ctrl *ctrl, int argc,
 				     char *argv[])
 {
@@ -1233,6 +1295,12 @@  static const struct hostapd_cli_cmd hostapd_cli_commands[] = {
 	{ "new_sta", hostapd_cli_cmd_new_sta },
 	{ "deauthenticate", hostapd_cli_cmd_deauthenticate },
 	{ "disassociate", hostapd_cli_cmd_disassociate },
+
+	{ "blacklist_add", hostapd_cli_cmd_blacklist_add_hostapd },
+	{ "blacklist_rm", hostapd_cli_cmd_blacklist_rm_hostapd },
+	{ "blacklist_show", hostapd_cli_cmd_blacklist_show },
+	{ "blacklist_clr", hostapd_cli_cmd_blacklist_clr_hostapd },
+
 #ifdef CONFIG_IEEE80211W
 	{ "sa_query", hostapd_cli_cmd_sa_query },
 #endif /* CONFIG_IEEE80211W */
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 0570ab7..c4a84a4 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -29,6 +29,7 @@ 
 #include "beacon.h"
 #include "hs20.h"
 #include "dfs.h"
+#include "sta_blacklist.h"
 
 
 #ifdef NEED_AP_MLME
@@ -838,6 +839,11 @@  void handle_probe_req(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_P2P */
 
+	if(sta_blacklist_present(hapd, mgmt->sa)) {
+		wpa_printf(MSG_DEBUG, "ignoring probe request from " MACSTR " due to blacklist", MAC2STR(mgmt->sa));
+		return;
+	}
+
 	/* TODO: verify that supp_rates contains at least one matching rate
 	 * with AP configuration */
 
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 14c154f..d340269 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -23,7 +23,7 @@ 
 #include "ctrl_iface_ap.h"
 #include "ap_drv_ops.h"
 #include "mbo_ap.h"
-
+#include "sta_blacklist.h"
 
 static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
 				 struct sta_info *sta,
@@ -550,6 +550,69 @@  int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
 	return len;
 }
 
+int hostapd_ctrl_iface_blacklist_add(struct hostapd_data *hapd,
+					const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	int ret = -1;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE BLACKLIST_ADD %s",
+		txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	ret = sta_blacklist_add(hapd, addr);
+
+	return ret;
+}
+
+int hostapd_ctrl_iface_blacklist_rm(struct hostapd_data *hapd,
+					const char *txtaddr)
+{
+	u8 addr[ETH_ALEN];
+	int ret = -1;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE BLACKLIST_RM %s",
+		txtaddr);
+
+	if (hwaddr_aton(txtaddr, addr))
+		return -1;
+
+	ret = sta_blacklist_rm(hapd, addr);
+
+	return ret;
+}
+
+int hostapd_ctrl_iface_blacklist_show(struct hostapd_data *hapd, char *buf,
+		size_t buflen)
+{
+	int len = 0, ret = 0;
+	struct sta_blacklist *e = hapd->blacklist;
+
+	while (e) {
+		ret = os_snprintf(buf + len, buflen - len,
+				  MACSTR "\n",
+				  MAC2STR(e->sta));
+		if (ret < 0 || (size_t) ret >= buflen - len)
+			return len;
+		len += ret;
+		e = e->next;
+	}
+
+	return ret;
+}
+
+int hostapd_ctrl_iface_blacklist_clr(struct hostapd_data *hapd)
+{
+	int ret = -1;
+
+	wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "CTRL_IFACE BLACKLIST_CLR");
+
+	ret = sta_blacklist_clear(hapd);
+
+	return ret;
+}
 
 int hostapd_parse_csa_settings(const char *pos,
 			       struct csa_settings *settings)
diff --git a/src/ap/ctrl_iface_ap.h b/src/ap/ctrl_iface_ap.h
index 6095d7d..346c6f3 100644
--- a/src/ap/ctrl_iface_ap.h
+++ b/src/ap/ctrl_iface_ap.h
@@ -29,5 +29,11 @@  int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
 int hostapd_ctrl_iface_pmksa_list(struct hostapd_data *hapd, char *buf,
 				  size_t len);
 void hostapd_ctrl_iface_pmksa_flush(struct hostapd_data *hapd);
-
+int hostapd_ctrl_iface_blacklist_add(struct hostapd_data *hapd,
+					const char *txtaddr);
+int hostapd_ctrl_iface_blacklist_rm(struct hostapd_data *hapd,
+					const char *txtaddr);
+int hostapd_ctrl_iface_blacklist_show(struct hostapd_data *hapd, char *buf,
+		size_t buflen);
+int hostapd_ctrl_iface_blacklist_clr(struct hostapd_data *hapd);
 #endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 30f57f4..6bb3897 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1839,6 +1839,7 @@  dfs_offload:
 	for (j = 0; j < iface->num_bss; j++)
 		hostapd_set_own_neighbor_report(iface->bss[j]);
 
+	hapd->blacklist = NULL;
 	return 0;
 
 fail:
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index 195679e..ab7635c 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -19,6 +19,7 @@  struct radius_server_data;
 struct upnp_wps_device_sm;
 struct hostapd_data;
 struct sta_info;
+struct sta_blacklist;
 struct ieee80211_ht_capabilities;
 struct full_dynamic_vlan;
 enum wps_event;
@@ -120,6 +121,8 @@  struct hostapd_data {
 	struct hostapd_bss_config *conf;
 	int interface_added; /* virtual interface added for this BSS */
 	unsigned int started:1;
+	/* Client station blacklist support */
+	struct sta_blacklist *blacklist;
 	unsigned int disabled:1;
 	unsigned int reenable_beacon:1;
 
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index 11f12f9..70d6005 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -28,6 +28,7 @@ 
 #include "beacon.h"
 #include "ieee802_11_auth.h"
 #include "sta_info.h"
+#include "sta_blacklist.h"
 #include "ieee802_1x.h"
 #include "wpa_auth.h"
 #include "pmksa_cache_auth.h"
@@ -944,7 +945,6 @@  remove_sta:
 	wpabuf_free(data);
 }
 
-
 /**
  * auth_sae_init_committed - Send COMMIT and start SAE in committed state
  * @hapd: BSS data for the device initiating the authentication
@@ -1045,6 +1045,16 @@  static void handle_auth(struct hostapd_data *hapd,
 	}
 #endif /* CONFIG_NO_RC4 */
 
+	// Check for existence in blacklist
+	if(sta_blacklist_present(hapd, mgmt->sa)) {
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "auth failed from " MACSTR " due to blacklist", MAC2STR(mgmt->sa));
+
+		resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+		goto fail;
+	}
+
+
 	if (hapd->tkip_countermeasures) {
 		resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
 		goto fail;
diff --git a/src/ap/sta_blacklist.c b/src/ap/sta_blacklist.c
new file mode 100644
index 0000000..40838cd
--- /dev/null
+++ b/src/ap/sta_blacklist.c
@@ -0,0 +1,140 @@ 
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "hostapd.h"
+#include "ieee802_1x.h"
+#include "wpa_auth.h"
+#include "ieee802_11.h"
+#include "sta_info.h"
+#include "sta_blacklist.h"
+
+/**
+ * sta_blacklist_get - Get the blacklist entry for a BSSID
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID
+ * Returns: Matching blacklist entry for the BSSID or %NULL if not found
+ */
+struct sta_blacklist * sta_blacklist_get(struct hostapd_data *hapd, const u8 *sta) {
+	struct sta_blacklist *e;
+
+	if (hapd == NULL || sta == NULL)
+		return NULL;
+
+	e = hapd->blacklist;
+	while (e) {
+		if (os_memcmp(e->sta, sta, ETH_ALEN) == 0)
+			return e;
+		e = e->next;
+	}
+
+	return NULL;
+}
+
+int sta_blacklist_present(struct hostapd_data *hapd, const u8 *sta)
+{
+	if(sta_blacklist_get(hapd, sta) != NULL)
+		return 1;
+
+	return 0;
+}
+
+
+
+/**
+ * wpa_blacklist_add - Add an BSSID to the blacklist
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be added to the blacklist
+ * Returns: Current blacklist count on success, -1 on failure
+ *
+ * This function adds the specified BSSID to the blacklist or increases the
+ * blacklist count if the BSSID was already listed. It should be called when
+ * an association attempt fails either due to the selected BSS rejecting
+ * association or due to timeout.
+ *
+ * This blacklist is used to force %wpa_supplicant to go through all available
+ * BSSes before retrying to associate with an BSS that rejected or timed out
+ * association. It does not prevent the listed BSS from being used; it only
+ * changes the order in which they are tried.
+ */
+int sta_blacklist_add(struct hostapd_data *hapd, const u8 *sta) {
+	struct sta_blacklist *e;
+
+	if (hapd == NULL || sta == NULL)
+		return -1;
+
+	e = sta_blacklist_get(hapd, sta);
+	if (e) {
+		return 0;
+	}
+
+	e = os_zalloc(sizeof(*e));
+	if (e == NULL)
+		return -1;
+	os_memcpy(e->sta, sta, ETH_ALEN);
+
+
+	e->next = hapd->blacklist;
+	hapd->blacklist = e;
+
+	hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+		       HOSTAPD_LEVEL_INFO, "Added BSSID " MACSTR " into blacklist", MAC2STR(sta));
+
+	return 0;
+}
+
+/**
+ * wpa_blacklist_del - Remove an BSSID from the blacklist
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bssid: BSSID to be removed from the blacklist
+ * Returns: 0 on success, -1 on failure
+ */
+int sta_blacklist_rm(struct hostapd_data *hapd, const u8 *sta) {
+	struct sta_blacklist *e, *prev = NULL;
+
+	if (hapd == NULL || sta == NULL)
+		return -1;
+
+	e = hapd->blacklist;
+	while (e) {
+		if (os_memcmp(e->sta, sta, ETH_ALEN) == 0) {
+			if (prev == NULL) {
+				hapd->blacklist = e->next;
+			} else {
+				prev->next = e->next;
+			}
+
+			hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+				       HOSTAPD_LEVEL_INFO, "Removed BSSID " MACSTR " from blacklist", MAC2STR(sta));
+
+			os_free(e);
+
+			return 0;
+		}
+		prev = e;
+		e = e->next;
+	}
+	return -1;
+}
+
+/**
+ * wpa_blacklist_clear - Clear the blacklist of all entries
+ * @wpa_s: Pointer to wpa_supplicant data
+ */
+int sta_blacklist_clear(struct hostapd_data *hapd) {
+	struct sta_blacklist *e, *prev;
+
+	e = hapd->blacklist;
+	hapd->blacklist = NULL;
+	while (e) {
+		prev = e;
+		e = e->next;
+		hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+			       HOSTAPD_LEVEL_INFO, "Removed BSSID " MACSTR " from blacklist", MAC2STR(prev->sta));
+		//wpa_printf(MSG_ERROR, "Removed BSSID " MACSTR " from blacklist (clear)", MAC2STR(prev->sta));
+		os_free(prev);
+	}
+
+	return 0;
+}
diff --git a/src/ap/sta_blacklist.h b/src/ap/sta_blacklist.h
new file mode 100644
index 0000000..243a277
--- /dev/null
+++ b/src/ap/sta_blacklist.h
@@ -0,0 +1,14 @@ 
+#ifndef STA_BLACKLIST_H
+#define STA_BLACKLIST_H
+
+struct sta_blacklist {
+	struct sta_blacklist *next;
+	u8 sta[ETH_ALEN];
+};
+
+struct sta_blacklist * sta_blacklist_get(struct hostapd_data *hapd, const u8 *sta);
+int sta_blacklist_present(struct hostapd_data *hapd, const u8 *sta);
+int sta_blacklist_add(struct hostapd_data *hapd, const u8 *sta);
+int sta_blacklist_rm(struct hostapd_data *hapd, const u8 *sta);
+int sta_blacklist_clear(struct hostapd_data *hapd);
+#endif /* STA_BLACKLIST_H */
-- 
1.9.1