From 01a57fe420f49588ecc1d59d5a5faf18cf551521 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 1 Sep 2013 13:40:33 +0300 Subject: [PATCH] P2P: Maintain list of per-client PSKs for persistent groups Record all generated per-client PSKs in the persistent group network block and configure these for the GO Authenticator whenever re-starting the persistent group. This completes per-client PSK support for persistent groups. Signed-hostap: Jouni Malinen --- src/ap/hostapd.h | 5 ++ src/ap/wps_hostapd.c | 5 ++ wpa_supplicant/ap.c | 15 ++++ wpa_supplicant/config.c | 63 ++++++++++++++++ wpa_supplicant/config_file.c | 17 +++++ wpa_supplicant/config_ssid.h | 15 +++- wpa_supplicant/config_winreg.c | 1 + wpa_supplicant/p2p_supplicant.c | 152 +++++++++++++++++++++++++++++++++++++- wpa_supplicant/p2p_supplicant.h | 3 + wpa_supplicant/wpa_supplicant.c | 1 + wpa_supplicant/wpa_supplicant_i.h | 2 + 11 files changed, 277 insertions(+), 2 deletions(-) diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h index cae4756..dbf1b52 100644 --- a/src/ap/hostapd.h +++ b/src/ap/hostapd.h @@ -204,6 +204,11 @@ struct hostapd_data { void (*setup_complete_cb)(void *ctx); void *setup_complete_cb_ctx; + void (*new_psk_cb)(void *ctx, const u8 *mac_addr, + const u8 *p2p_dev_addr, const u8 *psk, + size_t psk_len); + void *new_psk_cb_ctx; + #ifdef CONFIG_P2P struct p2p_data *p2p; struct p2p_group *p2p_group; diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c index 85eae35..eb300f1 100644 --- a/src/ap/wps_hostapd.c +++ b/src/ap/wps_hostapd.c @@ -125,6 +125,11 @@ static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN); os_memcpy(p->psk, psk, PMK_LEN); + if (hapd->new_psk_cb) { + hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr, + psk, psk_len); + } + p->next = ssid->wpa_psk; ssid->wpa_psk = p; diff --git a/wpa_supplicant/ap.c b/wpa_supplicant/ap.c index 99c6d8a..c2fb215 100644 --- a/wpa_supplicant/ap.c +++ b/wpa_supplicant/ap.c @@ -367,6 +367,19 @@ static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr, } +#ifdef CONFIG_P2P +static void ap_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr, + const u8 *psk, size_t psk_len) +{ + + struct wpa_supplicant *wpa_s = ctx; + if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL) + return; + wpas_p2p_new_psk_cb(wpa_s, mac_addr, p2p_dev_addr, psk, psk_len); +} +#endif /* CONFIG_P2P */ + + static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq) { #ifdef CONFIG_P2P @@ -571,6 +584,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s, hapd_iface->bss[i]->sta_authorized_cb = ap_sta_authorized_cb; hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s; #ifdef CONFIG_P2P + hapd_iface->bss[i]->new_psk_cb = ap_new_psk_cb; + hapd_iface->bss[i]->new_psk_cb_ctx = wpa_s; hapd_iface->bss[i]->p2p = wpa_s->global->p2p; hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(wpa_s, ssid); diff --git a/wpa_supplicant/config.c b/wpa_supplicant/config.c index a35be51..d666c91 100644 --- a/wpa_supplicant/config.c +++ b/wpa_supplicant/config.c @@ -1372,6 +1372,60 @@ static char * wpa_config_write_p2p_client_list(const struct parse_data *data, } #endif /* NO_CONFIG_WRITE */ + +static int wpa_config_parse_psk_list(const struct parse_data *data, + struct wpa_ssid *ssid, int line, + const char *value) +{ + struct psk_list_entry *p; + const char *pos; + + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return -1; + + pos = value; + if (os_strncmp(pos, "P2P-", 4) == 0) { + p->p2p = 1; + pos += 4; + } + + if (hwaddr_aton(pos, p->addr)) { + wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list address '%s'", + line, pos); + os_free(p); + return -1; + } + pos += 17; + if (*pos != '-') { + wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list '%s'", + line, pos); + os_free(p); + return -1; + } + pos++; + + if (hexstr2bin(pos, p->psk, PMK_LEN) || pos[PMK_LEN * 2] != '\0') { + wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list PSK '%s'", + line, pos); + os_free(p); + return -1; + } + + dl_list_add(&ssid->psk_list, &p->list); + + return 0; +} + + +#ifndef NO_CONFIG_WRITE +static char * wpa_config_write_psk_list(const struct parse_data *data, + struct wpa_ssid *ssid) +{ + return NULL; +} +#endif /* NO_CONFIG_WRITE */ + #endif /* CONFIG_P2P */ /* Helper macros for network block parser */ @@ -1539,6 +1593,7 @@ static const struct parse_data ssid_fields[] = { { INT_RANGE(ignore_broadcast_ssid, 0, 2) }, #ifdef CONFIG_P2P { FUNC(p2p_client_list) }, + { FUNC(psk_list) }, #endif /* CONFIG_P2P */ #ifdef CONFIG_HT_OVERRIDES { INT_RANGE(disable_ht, 0, 1) }, @@ -1731,6 +1786,8 @@ static void eap_peer_config_free(struct eap_peer_config *eap) */ void wpa_config_free_ssid(struct wpa_ssid *ssid) { + struct psk_list_entry *psk; + os_free(ssid->ssid); os_free(ssid->passphrase); os_free(ssid->ext_psk); @@ -1745,6 +1802,11 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid) #ifdef CONFIG_HT_OVERRIDES os_free(ssid->ht_mcs); #endif /* CONFIG_HT_OVERRIDES */ + while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry, + list))) { + dl_list_del(&psk->list); + os_free(psk); + } os_free(ssid); } @@ -1908,6 +1970,7 @@ struct wpa_ssid * wpa_config_add_network(struct wpa_config *config) if (ssid == NULL) return NULL; ssid->id = id; + dl_list_init(&ssid->psk_list); if (last) last->next = ssid; else diff --git a/wpa_supplicant/config_file.c b/wpa_supplicant/config_file.c index d03de0b..a2791eb 100644 --- a/wpa_supplicant/config_file.c +++ b/wpa_supplicant/config_file.c @@ -158,6 +158,7 @@ static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id) ssid = os_zalloc(sizeof(*ssid)); if (ssid == NULL) return NULL; + dl_list_init(&ssid->psk_list); ssid->id = id; wpa_config_set_network_defaults(ssid); @@ -604,6 +605,7 @@ static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid) #ifdef CONFIG_P2P + static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid) { char *value = wpa_config_get(ssid, "p2p_client_list"); @@ -612,6 +614,20 @@ static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid) fprintf(f, "\tp2p_client_list=%s\n", value); os_free(value); } + + +static void write_psk_list(FILE *f, struct wpa_ssid *ssid) +{ + struct psk_list_entry *psk; + char hex[32 * 2 + 1]; + + dl_list_for_each(psk, &ssid->psk_list, struct psk_list_entry, list) { + wpa_snprintf_hex(hex, sizeof(hex), psk->psk, sizeof(psk->psk)); + fprintf(f, "\tpsk_list=%s" MACSTR "-%s\n", + psk->p2p ? "P2P-" : "", MAC2STR(psk->addr), hex); + } +} + #endif /* CONFIG_P2P */ @@ -696,6 +712,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid) STR(id_str); #ifdef CONFIG_P2P write_p2p_client_list(f, ssid); + write_psk_list(f, ssid); #endif /* CONFIG_P2P */ INT(dtim_period); INT(beacon_int); diff --git a/wpa_supplicant/config_ssid.h b/wpa_supplicant/config_ssid.h index 1340dce..3a44272 100644 --- a/wpa_supplicant/config_ssid.h +++ b/wpa_supplicant/config_ssid.h @@ -1,6 +1,6 @@ /* * WPA Supplicant / Network configuration structures - * Copyright (c) 2003-2008, Jouni Malinen + * Copyright (c) 2003-2013, Jouni Malinen * * This software may be distributed under the terms of the BSD license. * See README for more details. @@ -10,6 +10,7 @@ #define CONFIG_SSID_H #include "common/defs.h" +#include "utils/list.h" #include "eap_peer/eap_config.h" #define MAX_SSID_LEN 32 @@ -33,6 +34,13 @@ #define DEFAULT_AMPDU_FACTOR -1 /* no change */ #define DEFAULT_AMPDU_DENSITY -1 /* no change */ +struct psk_list_entry { + struct dl_list list; + u8 addr[ETH_ALEN]; + u8 psk[32]; + u8 p2p; +}; + /** * struct wpa_ssid - Network configuration data * @@ -456,6 +464,11 @@ struct wpa_ssid { #endif /* P2P_MAX_STORED_CLIENTS */ /** + * psk_list - Per-client PSKs (struct psk_list_entry) + */ + struct dl_list psk_list; + + /** * p2p_group - Network generated as a P2P group (used internally) */ int p2p_group; diff --git a/wpa_supplicant/config_winreg.c b/wpa_supplicant/config_winreg.c index 3cf3a91..1b5e237 100644 --- a/wpa_supplicant/config_winreg.c +++ b/wpa_supplicant/config_winreg.c @@ -302,6 +302,7 @@ static struct wpa_ssid * wpa_config_read_network(HKEY hk, const TCHAR *netw, RegCloseKey(nhk); return NULL; } + dl_list_init(&ssid->psk_list); ssid->id = id; wpa_config_set_network_defaults(ssid); diff --git a/wpa_supplicant/p2p_supplicant.c b/wpa_supplicant/p2p_supplicant.c index f8c8b0a..962236b 100644 --- a/wpa_supplicant/p2p_supplicant.c +++ b/wpa_supplicant/p2p_supplicant.c @@ -600,6 +600,11 @@ static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s, s->ssid_len = ssid->ssid_len; os_memcpy(s->ssid, ssid->ssid, s->ssid_len); } + if (ssid->mode == WPAS_MODE_P2P_GO && wpa_s->global->add_psk) { + dl_list_add(&s->psk_list, &wpa_s->global->add_psk->list); + wpa_s->global->add_psk = NULL; + changed = 1; + } #ifndef CONFIG_NO_CONFIG_WRITE if (changed && wpa_s->conf->update_config && @@ -775,6 +780,10 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s, if (persistent) network_id = wpas_p2p_store_persistent_group(wpa_s->parent, ssid, go_dev_addr); + else { + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + } if (network_id < 0 && ssid) network_id = ssid->id; if (!client) { @@ -879,6 +888,44 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s, } +static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s, + struct wpa_ssid *ssid) +{ + struct wpa_ssid *persistent; + struct psk_list_entry *psk; + struct hostapd_data *hapd; + + if (!wpa_s->ap_iface) + return; + + persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid, + ssid->ssid_len); + if (persistent == NULL) + return; + + hapd = wpa_s->ap_iface->bss[0]; + + dl_list_for_each(psk, &persistent->psk_list, struct psk_list_entry, + list) { + struct hostapd_wpa_psk *hpsk; + + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add persistent group PSK entry for " + MACSTR " psk=%d", + MAC2STR(psk->addr), psk->p2p); + hpsk = os_zalloc(sizeof(*hpsk)); + if (hpsk == NULL) + break; + os_memcpy(hpsk->psk, psk->psk, PMK_LEN); + if (psk->p2p) + os_memcpy(hpsk->p2p_dev_addr, psk->addr, ETH_ALEN); + else + os_memcpy(hpsk->addr, psk->addr, ETH_ALEN); + hpsk->next = hapd->conf->ssid.wpa_psk; + hapd->conf->ssid.wpa_psk = hpsk; + } +} + + static void p2p_go_configured(void *ctx, void *data) { struct wpa_supplicant *wpa_s = ctx; @@ -918,10 +965,12 @@ static void p2p_go_configured(void *ctx, void *data) " [PERSISTENT]" : ""); } - if (params->persistent_group) + if (params->persistent_group) { network_id = wpas_p2p_store_persistent_group( wpa_s->parent, ssid, wpa_s->global->p2p_dev_addr); + wpas_p2p_add_psk_list(wpa_s, ssid); + } if (network_id < 0) network_id = ssid->id; wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0); @@ -3970,6 +4019,9 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr, return -1; } + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + if (go_intent < 0) go_intent = wpa_s->conf->p2p_go_intent; @@ -4424,6 +4476,9 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group, if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) return -1; + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + /* Make sure we are not running find during connection establishment */ wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND"); wpas_p2p_stop_find_oper(wpa_s); @@ -4515,6 +4570,9 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s, return 0; } + os_free(wpa_s->global->add_psk); + wpa_s->global->add_psk = NULL; + /* Make sure we are not running find during connection establishment */ wpas_p2p_stop_find_oper(wpa_s); @@ -5843,6 +5901,11 @@ struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s, (ssid_len != s->ssid_len || os_memcmp(ssid, s->ssid, ssid_len) != 0)) continue; + if (addr == NULL) { + if (s->mode == WPAS_MODE_P2P_GO) + return s; + continue; + } if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0) return s; /* peer is GO in the persistent group */ if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL) @@ -5966,3 +6029,90 @@ void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s) wpas_p2p_search_delay(wpa_s)); } } + + +void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, + const u8 *p2p_dev_addr, + const u8 *psk, size_t psk_len) +{ + struct wpa_ssid *ssid = wpa_s->current_ssid; + struct wpa_ssid *persistent; + struct psk_list_entry *p; + + if (psk_len != sizeof(p->psk)) + return; + + if (p2p_dev_addr) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR + " p2p_dev_addr=" MACSTR, + MAC2STR(mac_addr), MAC2STR(p2p_dev_addr)); + if (is_zero_ether_addr(p2p_dev_addr)) + p2p_dev_addr = NULL; + } else { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR, + MAC2STR(mac_addr)); + } + + if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: new_psk_cb during group formation"); + /* To be added to persistent group once created */ + if (wpa_s->global->add_psk == NULL) { + wpa_s->global->add_psk = os_zalloc(sizeof(*p)); + if (wpa_s->global->add_psk == NULL) + return; + } + p = wpa_s->global->add_psk; + if (p2p_dev_addr) { + p->p2p = 1; + os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN); + } else { + p->p2p = 0; + os_memcpy(p->addr, mac_addr, ETH_ALEN); + } + os_memcpy(p->psk, psk, psk_len); + return; + } + + if (ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Ignore new_psk_cb on not-persistent GO"); + return; + } + + persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid, + ssid->ssid_len); + if (!persistent) { + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not find persistent group information to store the new PSK"); + return; + } + + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return; + if (p2p_dev_addr) { + p->p2p = 1; + os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN); + } else { + p->p2p = 0; + os_memcpy(p->addr, mac_addr, ETH_ALEN); + } + os_memcpy(p->psk, psk, psk_len); + + if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS) { + struct psk_list_entry *last; + last = dl_list_last(&persistent->psk_list, + struct psk_list_entry, list); + wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove oldest PSK entry for " + MACSTR " (p2p=%u) to make room for a new one", + MAC2STR(last->addr), last->p2p); + dl_list_del(&last->list); + os_free(last); + } + + dl_list_add(&persistent->psk_list, &p->list); + +#ifndef CONFIG_NO_CONFIG_WRITE + if (wpa_s->parent->conf->update_config && + wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf)) + wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration"); +#endif /* CONFIG_NO_CONFIG_WRITE */ +} diff --git a/wpa_supplicant/p2p_supplicant.h b/wpa_supplicant/p2p_supplicant.h index e7ddb85..f72d563 100644 --- a/wpa_supplicant/p2p_supplicant.h +++ b/wpa_supplicant/p2p_supplicant.h @@ -152,6 +152,9 @@ int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s); int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s, struct hostapd_hw_modes *mode, u8 channel); unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s); +void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr, + const u8 *p2p_dev_addr, + const u8 *psk, size_t psk_len); #ifdef CONFIG_P2P void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s); diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c index 7bfbc0e..c274f3c 100644 --- a/wpa_supplicant/wpa_supplicant.c +++ b/wpa_supplicant/wpa_supplicant.c @@ -3480,6 +3480,7 @@ void wpa_supplicant_deinit(struct wpa_global *global) os_free(global->params.override_ctrl_interface); os_free(global->p2p_disallow_freq); + os_free(global->add_psk); os_free(global); wpa_debug_close_syslog(); diff --git a/wpa_supplicant/wpa_supplicant_i.h b/wpa_supplicant/wpa_supplicant_i.h index f808f49..ac22c31 100644 --- a/wpa_supplicant/wpa_supplicant_i.h +++ b/wpa_supplicant/wpa_supplicant_i.h @@ -272,6 +272,8 @@ struct wpa_global { #define MAX_WFD_SUBELEMS 10 struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS]; #endif /* CONFIG_WIFI_DISPLAY */ + + struct psk_list_entry *add_psk; /* From group formation */ }; -- 2.1.4