]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
hostapd: Support Multi-AP backhaul STA onboarding with WPS
authorDavina Lu <ylu@quantenna.com>
Tue, 12 Feb 2019 14:35:26 +0000 (15:35 +0100)
committerJouni Malinen <j@w1.fi>
Mon, 18 Feb 2019 20:35:41 +0000 (22:35 +0200)
The Wi-Fi Alliance Multi-AP Specification v1.0 allows onboarding of a
backhaul STA through WPS. To enable this, the WPS Registrar offers a
different set of credentials (backhaul credentials instead of fronthaul
credentials) when the Multi-AP subelement is present in the WFA vendor
extension element of the WSC M1 message.

Add new configuration options to specify the backhaul credentials for
the hostapd internal registrar: multi_ap_backhaul_ssid,
multi_ap_backhaul_wpa_psk, multi_ap_backhaul_wpa_passphrase. These are
only relevant for a fronthaul SSID, i.e., where multi_ap is set to 2 or
3. When these options are set, pass the backhaul credentials instead of
the normal credentials when the Multi-AP subelement is present.

Ignore the Multi-AP subelement if the backhaul config options are not
set. Note that for an SSID which is fronthaul and backhaul at the same
time (i.e., multi_ap == 3), this results in the correct credentials
being sent anyway.

The security to be used for the backaul BSS is fixed to WPA2PSK. The
Multi-AP Specification only allows Open and WPA2PSK networks to be
configured. Although not stated explicitly, the backhaul link is
intended to be always encrypted, hence WPA2PSK.

To build the credentials, the credential-building code is essentially
copied and simplified. Indeed, the backhaul credentials are always
WPA2PSK and never use per-device PSK. All the options set for the
fronthaul BSS WPS are simply ignored.

Signed-off-by: Davina Lu <ylu@quantenna.com>
Signed-off-by: Igor Mitsyanko <igor.mitsyanko.os@quantenna.com>
Signed-off-by: Arnout Vandecappelle (Essensium/Mind) <arnout@mind.be>
Cc: Marianna Carrera <marianna.carrera.so@quantenna.com>
hostapd/config_file.c
hostapd/hostapd.conf
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/wps_hostapd.c
src/wps/wps.h
src/wps/wps_attr_parse.c
src/wps/wps_attr_parse.h
src/wps/wps_dev_attr.c
src/wps/wps_dev_attr.h
src/wps/wps_registrar.c

index c22731e327e36351a961bb5281e77f13bd27b83a..70cad76d45faf4f103f3c82a1740c38d6bb4e3f4 100644 (file)
@@ -3621,6 +3621,56 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                                   line, pos);
                        return 1;
                }
+       } else if (os_strcmp(buf, "multi_ap_backhaul_ssid") == 0) {
+               size_t slen;
+               char *str = wpa_config_parse_string(pos, &slen);
+
+               if (!str || slen < 1 || slen > SSID_MAX_LEN) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+                                  line, pos);
+                       os_free(str);
+                       return 1;
+               }
+               os_memcpy(bss->multi_ap_backhaul_ssid.ssid, str, slen);
+               bss->multi_ap_backhaul_ssid.ssid_len = slen;
+               bss->multi_ap_backhaul_ssid.ssid_set = 1;
+               os_free(str);
+       } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_passphrase") == 0) {
+               int len = os_strlen(pos);
+
+               if (len < 8 || len > 63) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid WPA passphrase length %d (expected 8..63)",
+                                  line, len);
+                       return 1;
+               }
+               os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase);
+               bss->multi_ap_backhaul_ssid.wpa_passphrase = os_strdup(pos);
+               if (bss->multi_ap_backhaul_ssid.wpa_passphrase) {
+                       hostapd_config_clear_wpa_psk(
+                               &bss->multi_ap_backhaul_ssid.wpa_psk);
+                       bss->multi_ap_backhaul_ssid.wpa_passphrase_set = 1;
+               }
+       } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_psk") == 0) {
+               hostapd_config_clear_wpa_psk(
+                       &bss->multi_ap_backhaul_ssid.wpa_psk);
+               bss->multi_ap_backhaul_ssid.wpa_psk =
+                       os_zalloc(sizeof(struct hostapd_wpa_psk));
+               if (!bss->multi_ap_backhaul_ssid.wpa_psk)
+                       return 1;
+               if (hexstr2bin(pos, bss->multi_ap_backhaul_ssid.wpa_psk->psk,
+                              PMK_LEN) ||
+                   pos[PMK_LEN * 2] != '\0') {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
+                                  line, pos);
+                       hostapd_config_clear_wpa_psk(
+                               &bss->multi_ap_backhaul_ssid.wpa_psk);
+                       return 1;
+               }
+               bss->multi_ap_backhaul_ssid.wpa_psk->group = 1;
+               os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase);
+               bss->multi_ap_backhaul_ssid.wpa_passphrase = NULL;
+               bss->multi_ap_backhaul_ssid.wpa_psk_set = 1;
        } else if (os_strcmp(buf, "upnp_iface") == 0) {
                os_free(bss->upnp_iface);
                bss->upnp_iface = os_strdup(pos);
index c7e23ffe1b8bdf1c6814d772e71c8c1ecce7d6a0..57f0af7a014cd6158599a247fd9c92f677b06b12 100644 (file)
@@ -1946,6 +1946,15 @@ own_ip_addr=127.0.0.1
 # attribute.
 #ap_settings=hostapd.ap_settings
 
+# Multi-AP backhaul BSS config
+# Used in WPS when multi_ap=2 or 3. Defines "backhaul BSS" credentials.
+# These are passed in WPS M8 instead of the normal (fronthaul) credentials
+# if the Enrollee has the Multi-AP subelement set. Backhaul SSID is formatted
+# like ssid2. The key is set like wpa_psk or wpa_passphrase.
+#multi_ap_backhaul_ssid="backhaul"
+#multi_ap_backhaul_wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+#multi_ap_backhaul_wpa_passphrase=secret passphrase
+
 # WPS UPnP interface
 # If set, support for external Registrars is enabled.
 #upnp_iface=br0
index 11475b6e0d1410440d7d4afe1b381d6acfa311aa..cd55b574e02d1e763b6f8f49f1ad8482096db7d8 100644 (file)
@@ -644,6 +644,8 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
        os_free(conf->ap_pin);
        os_free(conf->extra_cred);
        os_free(conf->ap_settings);
+       hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk);
+       str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase);
        os_free(conf->upnp_iface);
        os_free(conf->friendly_name);
        os_free(conf->manufacturer_url);
index 897af5019e52970bc616cd8d1944b0898b2916b2..1edd072b3476ec26fea5b410fce4a8f4c722bfe2 100644 (file)
@@ -467,6 +467,7 @@ struct hostapd_bss_config {
        int force_per_enrollee_psk;
        u8 *ap_settings;
        size_t ap_settings_len;
+       struct hostapd_ssid multi_ap_backhaul_ssid;
        char *upnp_iface;
        char *friendly_name;
        char *manufacturer_url;
index 5ec019971f3725b5b767bcb737fa042e7ccc1636..6c6e9b7ce090087c8c5b3fd78cbe41ade9a8ddb0 100644 (file)
@@ -975,6 +975,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
 {
        struct wps_context *wps;
        struct wps_registrar_config cfg;
+       u8 *multi_ap_netw_key = NULL;
 
        if (conf->wps_state == 0) {
                hostapd_wps_clear_ies(hapd, 0);
@@ -1133,6 +1134,31 @@ int hostapd_init_wps(struct hostapd_data *hapd,
                wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP;
        }
 
+       if ((hapd->conf->multi_ap & FRONTHAUL_BSS) &&
+           hapd->conf->multi_ap_backhaul_ssid.ssid_len) {
+               cfg.multi_ap_backhaul_ssid_len =
+                       hapd->conf->multi_ap_backhaul_ssid.ssid_len;
+               cfg.multi_ap_backhaul_ssid =
+                       hapd->conf->multi_ap_backhaul_ssid.ssid;
+
+               if (conf->multi_ap_backhaul_ssid.wpa_passphrase) {
+                       cfg.multi_ap_backhaul_network_key = (const u8 *)
+                               conf->multi_ap_backhaul_ssid.wpa_passphrase;
+                       cfg.multi_ap_backhaul_network_key_len =
+                               os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+               } else if (conf->multi_ap_backhaul_ssid.wpa_psk) {
+                       multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1);
+                       if (!multi_ap_netw_key)
+                               goto fail;
+                       wpa_snprintf_hex((char *) multi_ap_netw_key,
+                                        2 * PMK_LEN + 1,
+                                        conf->multi_ap_backhaul_ssid.wpa_psk->psk,
+                                        PMK_LEN);
+                       cfg.multi_ap_backhaul_network_key = multi_ap_netw_key;
+                       cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN;
+               }
+       }
+
        wps->ap_settings = conf->ap_settings;
        wps->ap_settings_len = conf->ap_settings_len;
 
@@ -1174,10 +1200,12 @@ int hostapd_init_wps(struct hostapd_data *hapd,
        hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
 
        hapd->wps = wps;
+       bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
 
        return 0;
 
 fail:
+       bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
        hostapd_free_wps(wps);
        return -1;
 }
index 8752647eef9e40c5fdec965b5683386668840d6d..14ce863269f6c2e8a214fafd1b87e3cae3eca831 100644 (file)
@@ -100,6 +100,7 @@ struct wps_device_data {
        struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
 
        int p2p;
+       u8 multi_ap_ext;
 };
 
 /**
@@ -401,6 +402,37 @@ struct wps_registrar_config {
         * PSK is set for a network.
         */
        int force_per_enrollee_psk;
+
+       /**
+        * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul
+        * enrollee
+        *
+        * This SSID is used by the Registrar to fill in information for
+        * Credentials when the enrollee advertises it is a Multi-AP backhaul
+        * STA.
+        */
+       const u8 *multi_ap_backhaul_ssid;
+
+       /**
+        * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in
+        * octets
+        */
+       size_t multi_ap_backhaul_ssid_len;
+
+       /**
+        * multi_ap_backhaul_network_key - The Network Key (PSK) for the
+        * Multi-AP backhaul enrollee.
+        *
+        * This key can be either the ASCII passphrase (8..63 characters) or the
+        * 32-octet PSK (64 hex characters).
+        */
+       const u8 *multi_ap_backhaul_network_key;
+
+       /**
+        * multi_ap_backhaul_network_key_len - Length of
+        * multi_ap_backhaul_network_key in octets
+        */
+       size_t multi_ap_backhaul_network_key_len;
 };
 
 
index 756d57e876c55186fc3c5097854f220c0f2046b8..fd51635158ac5c646bf8772e0d83200972e9c595 100644 (file)
@@ -67,6 +67,17 @@ static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
                }
                attr->registrar_configuration_methods = pos;
                break;
+       case WFA_ELEM_MULTI_AP:
+               if (len != 1) {
+                       wpa_printf(MSG_DEBUG,
+                                  "WPS: Invalid Multi-AP Extension length %u",
+                                  len);
+                       return -1;
+               }
+               attr->multi_ap_ext = *pos;
+               wpa_printf(MSG_DEBUG, "WPS: Multi-AP Extension 0x%02x",
+                          attr->multi_ap_ext);
+               break;
        default:
                wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
                           "Extension subelement %u", id);
index 8188fe9173d44aa86f74cf67ba740fb89ae6bc82..4de27b26d4e26b1a20b9b8a1cbdceae57a568d98 100644 (file)
@@ -97,6 +97,7 @@ struct wps_parse_attr {
        const u8 *cred[MAX_CRED_COUNT];
        const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
        const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+       u8 multi_ap_ext;
 };
 
 int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
index 0d01211a261cbe2ed3908f9a4a6af3e95b7e7d47..b209fea8a4f2045de68814bcdb6176475ecc4a16 100644 (file)
@@ -390,6 +390,14 @@ int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
 }
 
 
+void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext)
+{
+       dev->multi_ap_ext = ext;
+       wpa_printf(MSG_DEBUG, "WPS: Multi-AP extension value %02x",
+                  dev->multi_ap_ext);
+}
+
+
 int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
 {
        if (bands == NULL) {
index c9034addbcc6495d5716900aa95aefe3ba81d2ab..a4b4173cdbaf4aa8c4aa76402a23d9167a0d99bd 100644 (file)
@@ -29,6 +29,7 @@ int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_process_device_attrs(struct wps_device_data *dev,
                             struct wps_parse_attr *attr);
 int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
+void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext);
 int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
 void wps_device_data_free(struct wps_device_data *dev);
 int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
index b5ac29bea621651b95f3ab034f61479fa6668b57..2669650775111140f7e2b5e1be17ab0e260089a7 100644 (file)
@@ -188,6 +188,37 @@ struct wps_registrar {
 #ifdef WPS_WORKAROUNDS
        struct os_reltime pbc_ignore_start;
 #endif /* WPS_WORKAROUNDS */
+
+       /**
+        * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul
+        * enrollee
+        *
+        * This SSID is used by the Registrar to fill in information for
+        * Credentials when the enrollee advertises it is a Multi-AP backhaul
+        * STA.
+        */
+       u8 multi_ap_backhaul_ssid[SSID_MAX_LEN];
+
+       /**
+        * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in
+        * octets
+        */
+       size_t multi_ap_backhaul_ssid_len;
+
+       /**
+        * multi_ap_backhaul_network_key - The Network Key (PSK) for the
+        * Multi-AP backhaul enrollee.
+        *
+        * This key can be either the ASCII passphrase (8..63 characters) or the
+        * 32-octet PSK (64 hex characters).
+        */
+       u8 *multi_ap_backhaul_network_key;
+
+       /**
+        * multi_ap_backhaul_network_key_len - Length of
+        * multi_ap_backhaul_network_key in octets
+        */
+       size_t multi_ap_backhaul_network_key_len;
 };
 
 
@@ -667,6 +698,18 @@ wps_registrar_init(struct wps_context *wps,
        reg->dualband = cfg->dualband;
        reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk;
 
+       os_memcpy(reg->multi_ap_backhaul_ssid, cfg->multi_ap_backhaul_ssid,
+                 cfg->multi_ap_backhaul_ssid_len);
+       reg->multi_ap_backhaul_ssid_len = cfg->multi_ap_backhaul_ssid_len;
+       if (cfg->multi_ap_backhaul_network_key) {
+               reg->multi_ap_backhaul_network_key =
+                       os_memdup(cfg->multi_ap_backhaul_network_key,
+                                 cfg->multi_ap_backhaul_network_key_len);
+               if (reg->multi_ap_backhaul_network_key)
+                       reg->multi_ap_backhaul_network_key_len =
+                               cfg->multi_ap_backhaul_network_key_len;
+       }
+
        if (wps_set_ie(reg)) {
                wps_registrar_deinit(reg);
                return NULL;
@@ -704,6 +747,8 @@ void wps_registrar_deinit(struct wps_registrar *reg)
        eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
        wps_registrar_flush(reg);
        wpabuf_clear_free(reg->extra_cred);
+       bin_clear_free(reg->multi_ap_backhaul_network_key,
+                      reg->multi_ap_backhaul_network_key_len);
        os_free(reg);
 }
 
@@ -1592,6 +1637,7 @@ int wps_build_credential_wrap(struct wpabuf *msg,
 int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
 {
        struct wpabuf *cred;
+       struct wps_registrar *reg = wps->wps->registrar;
 
        if (wps->wps->registrar->skip_cred_build)
                goto skip_cred_build;
@@ -1603,6 +1649,29 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
        }
        os_memset(&wps->cred, 0, sizeof(wps->cred));
 
+       if (wps->peer_dev.multi_ap_ext == MULTI_AP_BACKHAUL_STA &&
+           reg->multi_ap_backhaul_ssid_len) {
+               wpa_printf(MSG_DEBUG, "WPS: Use backhaul STA credentials");
+               os_memcpy(wps->cred.ssid, reg->multi_ap_backhaul_ssid,
+                         reg->multi_ap_backhaul_ssid_len);
+               wps->cred.ssid_len = reg->multi_ap_backhaul_ssid_len;
+               /* Backhaul is always WPA2PSK */
+               wps->cred.auth_type = WPS_AUTH_WPA2PSK;
+               wps->cred.encr_type = WPS_ENCR_AES;
+               /* Set MAC address in the Credential to be the Enrollee's MAC
+                * address
+                */
+               os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
+               if (reg->multi_ap_backhaul_network_key) {
+                       os_memcpy(wps->cred.key,
+                                 reg->multi_ap_backhaul_network_key,
+                                 reg->multi_ap_backhaul_network_key_len);
+                       wps->cred.key_len =
+                               reg->multi_ap_backhaul_network_key_len;
+               }
+               goto use_provided;
+       }
+
        os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
        wps->cred.ssid_len = wps->wps->ssid_len;
 
@@ -2705,6 +2774,7 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
                wps->use_psk_key = 1;
        }
 #endif /* WPS_WORKAROUNDS */
+       wps_process_vendor_ext_m1(&wps->peer_dev, attr->multi_ap_ext);
 
        wps->state = SEND_M2;
        return WPS_CONTINUE;