diff mbox series

[v2] WPS: Make it possible to use PSKs loaded from PSK file

Message ID 20200210114933.19641-1-tomasz.jankowski.mail@gmail.com
State Accepted
Headers show
Series [v2] WPS: Make it possible to use PSKs loaded from PSK file | expand

Commit Message

Tomasz Jankowski Feb. 10, 2020, 11:49 a.m. UTC
From: Tomasz Jankowski <tomasz.jankowski@plume.com>

By default, when conf file defines wpa_psk_file, hostapd will generate a random
PSK for each device connecting using WPS and append that PSK to wpa_psk_file.

This patch changes that behavior by adding new step. WPS will first try to
use PSK from wpa_psk_file. It will only try PSKs with wps=1 tag. Additionally
it'll try to match enrollee's MAC address (if provided). If it fails to find
appropriate PSK code fallbacks to generating new PSK.

Signed-off-by: Tomasz Jankowski <tomasz.jankowski@plume.com>
---
 hostapd/hostapd.wpa_psk |  4 ++++
 src/ap/ap_config.c      |  5 +++++
 src/ap/ap_config.h      |  1 +
 src/ap/wps_hostapd.c    | 39 +++++++++++++++++++++++++++++++++++++++
 src/wps/wps.h           |  8 ++++++++
 src/wps/wps_registrar.c | 20 ++++++++++++++++++++
 6 files changed, 77 insertions(+)

Comments

Jouni Malinen Feb. 15, 2020, 5:36 p.m. UTC | #1
On Mon, Feb 10, 2020 at 12:49:33PM +0100, tomasz.jankowski.mail@gmail.com wrote:
> By default, when conf file defines wpa_psk_file, hostapd will generate a random
> PSK for each device connecting using WPS and append that PSK to wpa_psk_file.
> 
> This patch changes that behavior by adding new step. WPS will first try to
> use PSK from wpa_psk_file. It will only try PSKs with wps=1 tag. Additionally
> it'll try to match enrollee's MAC address (if provided). If it fails to find
> appropriate PSK code fallbacks to generating new PSK.

Thanks, applied with some fixes.
diff mbox series

Patch

diff --git a/hostapd/hostapd.wpa_psk b/hostapd/hostapd.wpa_psk
index 166e59e9c..9a63d93f7 100644
--- a/hostapd/hostapd.wpa_psk
+++ b/hostapd/hostapd.wpa_psk
@@ -7,9 +7,13 @@ 
 # keyid=<keyid_string>
 # An optional VLAN ID can be specified by prefixing the line with
 # vlanid=<VLAN ID>.
+# An optional WPS tag can be added by prefixing the line with
+# wps=[0/1] (default: 0)
 00:00:00:00:00:00 secret passphrase
 00:11:22:33:44:55 another passphrase
 00:22:33:44:55:66 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
 keyid=example_id 00:11:22:33:44:77 passphrase with keyid
 vlanid=3 00:00:00:00:00:00 passphrase with vlanid
+wps=1 00:00:00:00:00:00 passphrase for WPS
+wps=1 11:22:33:44:55:00 dev-specific passphrase for WPS
 00:00:00:00:00:00 another passphrase for all STAs
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index 27c4b48e4..290d5fa39 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -301,6 +301,7 @@  static int hostapd_config_read_wpa_psk(const char *fname,
 
 	while (fgets(buf, sizeof(buf), f)) {
 		int vlan_id = 0;
+		int wps = 0;
 
 		line++;
 
@@ -331,6 +332,8 @@  static int hostapd_config_read_wpa_psk(const char *fname,
 				value = "";
 			if (!os_strcmp(name, "keyid")) {
 				keyid = value;
+			} else if (!os_strcmp(name, "wps")) {
+				wps = atoi(value);
 			} else if (!os_strcmp(name, "vlanid")) {
 				vlan_id = atoi(value);
 			} else {
@@ -404,6 +407,8 @@  static int hostapd_config_read_wpa_psk(const char *fname,
 			}
 		}
 
+		psk->wps = wps;
+
 		psk->next = ssid->wpa_psk;
 		ssid->wpa_psk = psk;
 	}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 017e60aa4..f321017e8 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -152,6 +152,7 @@  struct hostapd_wpa_psk {
 	struct hostapd_wpa_psk *next;
 	int group;
 	char keyid[KEYID_LEN];
+	int wps;
 	u8 psk[PMK_LEN];
 	u8 addr[ETH_ALEN];
 	u8 p2p_dev_addr[ETH_ALEN];
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 3ea2228a9..ea60ae76c 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -268,6 +268,44 @@  static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr,
 		     config_methods, dev_password_id, request_type, dev_name);
 }
 
+static int hostapd_wps_lookup_pskfile_cb(void *ctx, const u8 *mac_addr,
+			    const u8 **psk)
+{
+	const struct hostapd_data *hapd = ctx;
+	const struct hostapd_wpa_psk *wpa_psk;
+	const u8 *any_psk = NULL;
+	const u8 *dev_psk = NULL;
+	u8 any_mac_addr[ETH_ALEN];
+
+	os_memset(any_mac_addr, 0, ETH_ALEN);
+
+	for (wpa_psk = hapd->conf->ssid.wpa_psk; wpa_psk->next;
+		wpa_psk = wpa_psk->next)
+	{
+		if (wpa_psk->wps == 0)
+			continue;
+
+		if (!any_psk && os_memcmp(any_mac_addr, wpa_psk->addr, ETH_ALEN) == 0)
+			any_psk = wpa_psk->psk;
+
+		if (mac_addr && !dev_psk &&
+			os_memcmp(mac_addr, wpa_psk->addr, ETH_ALEN) == 0) {
+			dev_psk = wpa_psk->psk;
+		}
+	}
+
+	if (dev_psk)
+		*psk = dev_psk;
+	else if (any_psk)
+		*psk = any_psk;
+	else {
+		*psk = NULL;
+		wpa_printf(MSG_DEBUG,
+				"WPS: Failed to find appropriate PSK in wpa_psk_file");
+	}
+
+	return *psk ? 1 : 0;
+}
 
 static void wps_reload_config(void *eloop_data, void *user_ctx)
 {
@@ -1213,6 +1251,7 @@  int hostapd_init_wps(struct hostapd_data *hapd,
 	cfg.pin_needed_cb = hostapd_wps_pin_needed_cb;
 	cfg.reg_success_cb = hostapd_wps_reg_success_cb;
 	cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb;
+	cfg.lookup_pskfile_cb = hostapd_wps_lookup_pskfile_cb;
 	cfg.cb_ctx = hapd;
 	cfg.skip_cred_build = conf->skip_cred_build;
 	cfg.extra_cred = conf->extra_cred;
diff --git a/src/wps/wps.h b/src/wps/wps.h
index f42045e93..3b56da74d 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -344,6 +344,14 @@  struct wps_registrar_config {
 				 u16 dev_password_id, u8 request_type,
 				 const char *dev_name);
 
+	/**
+	 * lookup_pskfile_cb - Callback for searching for PSK in wpa_psk_file
+	 * @ctx: Higher layer context data (cb_ctx)
+	 * @addr: Enrollee's MAC address
+	 * @psk: Pointer to found PSK (output arg)
+	 */
+	int (*lookup_pskfile_cb)(void *ctx, const u8 *mac_addr, const u8 **psk);
+
 	/**
 	 * cb_ctx: Higher layer context data for Registrar callbacks
 	 */
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index da81d1adc..91b35b6f2 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -159,6 +159,9 @@  struct wps_registrar {
 				 const u8 *pri_dev_type, u16 config_methods,
 				 u16 dev_password_id, u8 request_type,
 				 const char *dev_name);
+
+	int (*lookup_pskfile_cb)(void *ctx, const u8 *mac_addr, const u8 **psk);
+
 	void *cb_ctx;
 
 	struct dl_list pins;
@@ -681,6 +684,7 @@  wps_registrar_init(struct wps_context *wps,
 	reg->reg_success_cb = cfg->reg_success_cb;
 	reg->set_sel_reg_cb = cfg->set_sel_reg_cb;
 	reg->enrollee_seen_cb = cfg->enrollee_seen_cb;
+	reg->lookup_pskfile_cb = cfg->lookup_pskfile_cb;
 	reg->cb_ctx = cfg->cb_ctx;
 	reg->skip_cred_build = cfg->skip_cred_build;
 	if (cfg->extra_cred) {
@@ -1289,6 +1293,14 @@  static void wps_cb_set_sel_reg(struct wps_registrar *reg)
 			    methods);
 }
 
+static int wps_cp_lookup_pskfile(struct wps_registrar *reg, const u8 *mac_addr,
+                const u8** psk)
+{
+	if (reg->lookup_pskfile_cb == NULL)
+		return 0;
+
+	return reg->lookup_pskfile_cb(reg->cb_ctx, mac_addr, psk);
+}
 
 static int wps_set_ie(struct wps_registrar *reg)
 {
@@ -1644,6 +1656,7 @@  int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
 {
 	struct wpabuf *cred;
 	struct wps_registrar *reg = wps->wps->registrar;
+	const u8 *pskfile_psk;
 
 	if (wps->wps->registrar->skip_cred_build)
 		goto skip_cred_build;
@@ -1759,6 +1772,13 @@  int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
 				      wps->new_psk, wps->new_psk_len);
 		os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len);
 		wps->cred.key_len = wps->new_psk_len;
+	} else if (wps_cp_lookup_pskfile(reg, wps->mac_addr_e, &pskfile_psk)) {
+		char hex[65];
+		wpa_hexdump_key(MSG_DEBUG, "WPS: Use PSK from wpa_psk_file",
+				pskfile_psk, 32);
+		wpa_snprintf_hex(hex, sizeof(hex), pskfile_psk, 32);
+		os_memcpy(wps->cred.key, hex, 32 * 2);
+		wps->cred.key_len = 32 * 2;
 	} else if (!wps->wps->registrar->force_per_enrollee_psk &&
 		   wps->use_psk_key && wps->wps->psk_set) {
 		char hex[65];