WPS: Add MAC address to validation error message for Probe Request
[libeap.git] / src / ap / wps_hostapd.c
index 86ce10f..f1ad1df 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / WPS integration
- * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2010, 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
@@ -42,6 +42,7 @@ static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
 
 static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
                                    const u8 *ie, size_t ie_len);
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
 
 
 static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
@@ -100,8 +101,7 @@ static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie,
        wpabuf_free(hapd->wps_probe_resp_ie);
        hapd->wps_probe_resp_ie = probe_resp_ie;
        ieee802_11_set_beacon(hapd);
-       return hapd->drv.set_ap_wps_ie(hapd, hapd->wps_beacon_ie,
-                                      hapd->wps_probe_resp_ie);
+       return hapd->drv.set_ap_wps_ie(hapd);
 }
 
 
@@ -153,6 +153,9 @@ static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
                return;
        wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
                MAC2STR(mac_addr), uuid);
+       if (hapd->wps_reg_success_cb)
+               hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
+                                        mac_addr, uuid_e);
 }
 
 
@@ -419,20 +422,35 @@ static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred)
 }
 
 
+static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
+{
+       struct hostapd_data *hapd = eloop_data;
+
+       if (hapd->conf->ap_setup_locked)
+               return;
+
+       wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
+       wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+       hapd->wps->ap_setup_locked = 0;
+       wps_registrar_update_ie(hapd->wps->registrar);
+}
+
+
 static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
                                  struct wps_event_pwd_auth_fail *data)
 {
-       FILE *f;
-
-       if (!data->enrollee)
+       if (!data->enrollee || hapd->conf->ap_pin == NULL)
                return;
 
        /*
         * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup
-        * if this happens multiple times.
+        * for some time if this happens multiple times to slow down brute
+        * force attacks.
         */
        hapd->ap_pin_failures++;
-       if (hapd->ap_pin_failures < 4)
+       wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
+                  hapd->ap_pin_failures);
+       if (hapd->ap_pin_failures < 3)
                return;
 
        wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED);
@@ -440,23 +458,22 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
 
        wps_registrar_update_ie(hapd->wps->registrar);
 
-       if (hapd->conf->wps_cred_processing == 1)
-               return;
-
-       f = fopen(hapd->iface->config_fname, "a");
-       if (f == NULL) {
-               wpa_printf(MSG_WARNING, "WPS: Could not append to the current "
-                          "configuration file");
-               return;
+       if (!hapd->conf->ap_setup_locked) {
+               if (hapd->ap_pin_lockout_time == 0)
+                       hapd->ap_pin_lockout_time = 60;
+               else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 &&
+                        (hapd->ap_pin_failures % 3) == 0)
+                       hapd->ap_pin_lockout_time *= 2;
+
+               wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds",
+                          hapd->ap_pin_lockout_time);
+               eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+               eloop_register_timeout(hapd->ap_pin_lockout_time, 0,
+                                      hostapd_wps_reenable_ap_pin, hapd,
+                                      NULL);
        }
 
-       fprintf(f, "# WPS AP Setup Locked based on possible attack\n");
-       fprintf(f, "ap_setup_locked=1\n");
-       fclose(f);
-
-       /* TODO: dualband AP may need to update multiple configuration files */
-
-       wpa_printf(MSG_DEBUG, "WPS: AP configuration updated");
+       /* TODO: dualband AP may need to update other interfaces */
 }
 
 
@@ -478,7 +495,54 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
        wpabuf_free(hapd->wps_probe_resp_ie);
        hapd->wps_probe_resp_ie = NULL;
 
-       hapd->drv.set_ap_wps_ie(hapd, NULL, NULL);
+       hapd->drv.set_ap_wps_ie(hapd);
+}
+
+
+static int get_uuid_cb(struct hostapd_iface *iface, void *ctx)
+{
+       const u8 **uuid = ctx;
+       size_t j;
+
+       if (iface == NULL)
+               return 0;
+       for (j = 0; j < iface->num_bss; j++) {
+               struct hostapd_data *hapd = iface->bss[j];
+               if (hapd->wps && !is_nil_uuid(hapd->wps->uuid)) {
+                       *uuid = hapd->wps->uuid;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+
+static const u8 * get_own_uuid(struct hostapd_iface *iface)
+{
+       const u8 *uuid;
+       if (iface->for_each_interface == NULL)
+               return NULL;
+       uuid = NULL;
+       iface->for_each_interface(iface->interfaces, get_uuid_cb, &uuid);
+       return uuid;
+}
+
+
+static int count_interface_cb(struct hostapd_iface *iface, void *ctx)
+{
+       int *count= ctx;
+       (*count)++;
+       return 0;
+}
+
+
+static int interface_count(struct hostapd_iface *iface)
+{
+       int count = 0;
+       iface->for_each_interface(iface->interfaces, count_interface_cb,
+                                 &count);
+       return count;
 }
 
 
@@ -505,11 +569,22 @@ int hostapd_init_wps(struct hostapd_data *hapd,
        wps->wps_state = hapd->conf->wps_state;
        wps->ap_setup_locked = hapd->conf->ap_setup_locked;
        if (is_nil_uuid(hapd->conf->uuid)) {
-               uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
-               wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address",
-                           wps->uuid, UUID_LEN);
-       } else
+               const u8 *uuid;
+               uuid = get_own_uuid(hapd->iface);
+               if (uuid) {
+                       os_memcpy(wps->uuid, uuid, UUID_LEN);
+                       wpa_hexdump(MSG_DEBUG, "WPS: Clone UUID from another "
+                                   "interface", wps->uuid, UUID_LEN);
+               } else {
+                       uuid_gen_mac_addr(hapd->own_addr, wps->uuid);
+                       wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
+                                   "address", wps->uuid, UUID_LEN);
+               }
+       } else {
                os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN);
+               wpa_hexdump(MSG_DEBUG, "WPS: Use configured UUID",
+                           wps->uuid, UUID_LEN);
+       }
        wps->ssid_len = hapd->conf->ssid.ssid_len;
        os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len);
        wps->ap = 1;
@@ -630,10 +705,13 @@ int hostapd_init_wps(struct hostapd_data *hapd,
                conf->skip_cred_build;
        if (conf->ssid.security_policy == SECURITY_STATIC_WEP)
                cfg.static_wep_only = 1;
+       cfg.dualband = interface_count(hapd->iface) > 1;
+       if (cfg.dualband)
+               wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
 
        wps->registrar = wps_registrar_init(wps, &cfg);
        if (wps->registrar == NULL) {
-               printf("Failed to initialize WPS Registrar\n");
+               wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
                os_free(wps->network_key);
                os_free(wps);
                return -1;
@@ -665,6 +743,8 @@ int hostapd_init_wps(struct hostapd_data *hapd,
 
 void hostapd_deinit_wps(struct hostapd_data *hapd)
 {
+       eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+       eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
        if (hapd->wps == NULL)
                return;
 #ifdef CONFIG_WPS_UPNP
@@ -684,8 +764,19 @@ void hostapd_deinit_wps(struct hostapd_data *hapd)
 }
 
 
-int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
-                       const char *pin, int timeout)
+void hostapd_update_wps(struct hostapd_data *hapd)
+{
+       if (hapd->wps == NULL)
+               return;
+       if (hapd->conf->wps_state)
+               wps_registrar_update_ie(hapd->wps->registrar);
+       else
+               hostapd_deinit_wps(hapd);
+}
+
+
+int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
+                       const char *uuid, const char *pin, int timeout)
 {
        u8 u[UUID_LEN];
        int any = 0;
@@ -696,7 +787,8 @@ int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid,
                any = 1;
        else if (uuid_str2bin(uuid, u))
                return -1;
-       return wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u,
+       return wps_registrar_add_pin(hapd->wps->registrar, addr,
+                                    any ? NULL : u,
                                     (const u8 *) pin, os_strlen(pin),
                                     timeout);
 }
@@ -747,7 +839,7 @@ int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
 
        if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ||
             wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) &&
-           hostapd_wps_add_pin(hapd, "any",
+           hostapd_wps_add_pin(hapd, NULL, "any",
                                wpabuf_head(wps->oob_conf.dev_password), 0) <
            0)
                goto error;
@@ -769,22 +861,48 @@ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr,
 {
        struct hostapd_data *hapd = ctx;
        struct wpabuf *wps_ie;
+       struct ieee802_11_elems elems;
 
        if (hapd->wps == NULL)
                return 0;
 
+       if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
+               wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from "
+                          MACSTR, MAC2STR(addr));
+               return 0;
+       }
+
+       if (elems.ssid && elems.ssid_len > 0 &&
+           (elems.ssid_len != hapd->conf->ssid.ssid_len ||
+            os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) !=
+            0))
+               return 0; /* Not for us */
+
        wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA);
        if (wps_ie == NULL)
                return 0;
+       if (wps_validate_probe_req(wps_ie, addr) < 0) {
+               wpabuf_free(wps_ie);
+               return 0;
+       }
 
        if (wpabuf_len(wps_ie) > 0) {
-               wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie);
+               int p2p_wildcard = 0;
+#ifdef CONFIG_P2P
+               if (elems.ssid && elems.ssid_len == P2P_WILDCARD_SSID_LEN &&
+                   os_memcmp(elems.ssid, P2P_WILDCARD_SSID,
+                             P2P_WILDCARD_SSID_LEN) == 0)
+                       p2p_wildcard = 1;
+#endif /* CONFIG_P2P */
+               wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie,
+                                          p2p_wildcard);
 #ifdef CONFIG_WPS_UPNP
                /* FIX: what exactly should be included in the WLANEvent?
                 * WPS attributes? Full ProbeReq frame? */
-               upnp_wps_device_send_wlan_event(hapd->wps_upnp, addr,
-                                               UPNP_WPS_WLANEVENT_TYPE_PROBE,
-                                               wps_ie);
+               if (!p2p_wildcard)
+                       upnp_wps_device_send_wlan_event(
+                               hapd->wps_upnp, addr,
+                               UPNP_WPS_WLANEVENT_TYPE_PROBE, wps_ie);
 #endif /* CONFIG_WPS_UPNP */
        }
 
@@ -821,6 +939,7 @@ static int hostapd_rx_req_put_wlan_response(
         */
 
        sta = ap_get_sta(hapd, mac_addr);
+#ifndef CONFIG_WPS_STRICT
        if (!sta) {
                /*
                 * Workaround - Intel wsccmd uses bogus NewWLANEventMAC:
@@ -834,6 +953,7 @@ static int hostapd_rx_req_put_wlan_response(
                                break;
                }
        }
+#endif /* CONFIG_WPS_STRICT */
 
        if (!sta) {
                wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found");
@@ -900,3 +1020,78 @@ int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr,
                return 0;
        return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen);
 }
+
+
+static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx)
+{
+       struct hostapd_data *hapd = eloop_data;
+       wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out");
+       hostapd_wps_ap_pin_disable(hapd);
+}
+
+
+static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout);
+       hapd->ap_pin_failures = 0;
+       hapd->conf->ap_setup_locked = 0;
+       if (hapd->wps->ap_setup_locked) {
+               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
+               hapd->wps->ap_setup_locked = 0;
+               wps_registrar_update_ie(hapd->wps->registrar);
+       }
+       eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+       if (timeout > 0)
+               eloop_register_timeout(timeout, 0,
+                                      hostapd_wps_ap_pin_timeout, hapd, NULL);
+}
+
+
+void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd)
+{
+       wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN");
+       os_free(hapd->conf->ap_pin);
+       hapd->conf->ap_pin = NULL;
+#ifdef CONFIG_WPS_UPNP
+       upnp_wps_set_ap_pin(hapd->wps_upnp, NULL);
+#endif /* CONFIG_WPS_UPNP */
+       eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
+}
+
+
+const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
+{
+       unsigned int pin;
+       char pin_txt[9];
+
+       pin = wps_generate_pin();
+       os_snprintf(pin_txt, sizeof(pin_txt), "%u", pin);
+       os_free(hapd->conf->ap_pin);
+       hapd->conf->ap_pin = os_strdup(pin_txt);
+#ifdef CONFIG_WPS_UPNP
+       upnp_wps_set_ap_pin(hapd->wps_upnp, pin_txt);
+#endif /* CONFIG_WPS_UPNP */
+       hostapd_wps_ap_pin_enable(hapd, timeout);
+       return hapd->conf->ap_pin;
+}
+
+
+const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd)
+{
+       return hapd->conf->ap_pin;
+}
+
+
+int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
+                          int timeout)
+{
+       os_free(hapd->conf->ap_pin);
+       hapd->conf->ap_pin = os_strdup(pin);
+       if (hapd->conf->ap_pin == NULL)
+               return -1;
+#ifdef CONFIG_WPS_UPNP
+       upnp_wps_set_ap_pin(hapd->wps_upnp, hapd->conf->ap_pin);
+#endif /* CONFIG_WPS_UPNP */
+       hostapd_wps_ap_pin_enable(hapd, timeout);
+       return 0;
+}