WPS: Share a single function for generating NFS password tokens
[mech_eap.git] / src / ap / wps_hostapd.c
index 671bc67..07ce06c 100644 (file)
@@ -1,15 +1,9 @@
 /*
  * hostapd / WPS integration
- * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2012, 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
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
@@ -18,6 +12,8 @@
 #include "utils/eloop.h"
 #include "utils/uuid.h"
 #include "crypto/dh_groups.h"
+#include "crypto/dh_group5.h"
+#include "crypto/random.h"
 #include "common/wpa_ctrl.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
@@ -26,6 +22,7 @@
 #include "wps/wps.h"
 #include "wps/wps_defs.h"
 #include "wps/wps_dev_attr.h"
+#include "wps/wps_attr_parse.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
@@ -43,7 +40,8 @@ static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd);
 
 static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
                                    const u8 *bssid,
-                                   const u8 *ie, size_t ie_len);
+                                   const u8 *ie, size_t ie_len,
+                                   int ssi_signal);
 static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
 
 
@@ -186,11 +184,30 @@ static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
 }
 
 
+struct wps_stop_reg_data {
+       struct hostapd_data *current_hapd;
+       const u8 *uuid_e;
+       const u8 *dev_pw;
+       size_t dev_pw_len;
+};
+
+static int wps_stop_registrar(struct hostapd_data *hapd, void *ctx)
+{
+       struct wps_stop_reg_data *data = ctx;
+       if (hapd != data->current_hapd && hapd->wps != NULL)
+               wps_registrar_complete(hapd->wps->registrar, data->uuid_e,
+                                      data->dev_pw, data->dev_pw_len);
+       return 0;
+}
+
+
 static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
-                                      const u8 *uuid_e)
+                                      const u8 *uuid_e, const u8 *dev_pw,
+                                      size_t dev_pw_len)
 {
        struct hostapd_data *hapd = ctx;
        char uuid[40];
+       struct wps_stop_reg_data data;
        if (uuid_bin2str(uuid_e, uuid, sizeof(uuid)))
                return;
        wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s",
@@ -198,6 +215,11 @@ static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
        if (hapd->wps_reg_success_cb)
                hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx,
                                         mac_addr, uuid_e);
+       data.current_hapd = hapd;
+       data.uuid_e = uuid_e;
+       data.dev_pw = dev_pw;
+       data.dev_pw_len = dev_pw_len;
+       hostapd_wps_for_each(hapd, wps_stop_registrar, &data);
 }
 
 
@@ -494,6 +516,8 @@ static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx)
 
        if (hapd->conf->ap_setup_locked)
                return;
+       if (hapd->ap_pin_failures_consecutive >= 10)
+               return;
 
        wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN");
        wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED);
@@ -515,8 +539,10 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
         * force attacks.
         */
        hapd->ap_pin_failures++;
-       wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u",
-                  hapd->ap_pin_failures);
+       hapd->ap_pin_failures_consecutive++;
+       wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u "
+                  "(%u consecutive)",
+                  hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
        if (hapd->ap_pin_failures < 3)
                return 0;
 
@@ -525,7 +551,15 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
 
        wps_registrar_update_ie(hapd->wps->registrar);
 
-       if (!hapd->conf->ap_setup_locked) {
+       if (!hapd->conf->ap_setup_locked &&
+           hapd->ap_pin_failures_consecutive >= 10) {
+               /*
+                * In indefinite lockdown - disable automatic AP PIN
+                * reenablement.
+                */
+               eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
+               wpa_printf(MSG_DEBUG, "WPS: AP PIN disabled indefinitely");
+       } else 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 &&
@@ -551,6 +585,29 @@ static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
 }
 
 
+static int wps_ap_pin_success(struct hostapd_data *hapd, void *ctx)
+{
+       if (hapd->conf->ap_pin == NULL || hapd->wps == NULL)
+               return 0;
+
+       if (hapd->ap_pin_failures_consecutive == 0)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "WPS: Clear consecutive AP PIN failure counter "
+                  "- total validation failures %u (%u consecutive)",
+                  hapd->ap_pin_failures, hapd->ap_pin_failures_consecutive);
+       hapd->ap_pin_failures_consecutive = 0;
+
+       return 0;
+}
+
+
+static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
+{
+       hostapd_wps_for_each(hapd, wps_ap_pin_success, NULL);
+}
+
+
 static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
        "No Error", /* WPS_EI_NO_ERROR */
        "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
@@ -599,22 +656,19 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
                wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
                break;
        case WPS_EV_ER_AP_ADD:
-               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ER_AP_ADD);
                break;
        case WPS_EV_ER_AP_REMOVE:
-               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ER_AP_REMOVE);
                break;
        case WPS_EV_ER_ENROLLEE_ADD:
-               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ER_ENROLLEE_ADD);
                break;
        case WPS_EV_ER_ENROLLEE_REMOVE:
-               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ER_ENROLLEE_REMOVE);
                break;
        case WPS_EV_ER_AP_SETTINGS:
-               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ER_AP_SETTINGS);
                break;
        case WPS_EV_ER_SET_SELECTED_REGISTRAR:
-               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ER_SET_SEL_REG);
+               break;
+       case WPS_EV_AP_PIN_SUCCESS:
+               hostapd_wps_ap_pin_success(hapd);
                break;
        }
        if (hapd->wps_event_cb)
@@ -788,8 +842,14 @@ int hostapd_init_wps(struct hostapd_data *hapd,
        }
 
        wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
-       wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
-               WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+
+       if (conf->wps_rf_bands) {
+               wps->dev.rf_bands = conf->wps_rf_bands;
+       } else {
+               wps->dev.rf_bands =
+                       hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+                       WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+       }
 
        if (conf->wpa & WPA_PROTO_RSN) {
                if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK)
@@ -934,6 +994,20 @@ int hostapd_init_wps_complete(struct hostapd_data *hapd)
 }
 
 
+static void hostapd_wps_nfc_clear(struct wps_context *wps)
+{
+#ifdef CONFIG_WPS_NFC
+       wps->ap_nfc_dev_pw_id = 0;
+       wpabuf_free(wps->ap_nfc_dh_pubkey);
+       wps->ap_nfc_dh_pubkey = NULL;
+       wpabuf_free(wps->ap_nfc_dh_privkey);
+       wps->ap_nfc_dh_privkey = NULL;
+       wpabuf_free(wps->ap_nfc_dev_pw);
+       wps->ap_nfc_dev_pw = NULL;
+#endif /* CONFIG_WPS_NFC */
+}
+
+
 void hostapd_deinit_wps(struct hostapd_data *hapd)
 {
        eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
@@ -951,6 +1025,7 @@ void hostapd_deinit_wps(struct hostapd_data *hapd)
        wpabuf_free(hapd->wps->oob_conf.pubkey_hash);
        wpabuf_free(hapd->wps->oob_conf.dev_password);
        wps_free_pending_msgs(hapd->wps->upnp_msgs);
+       hostapd_wps_nfc_clear(hapd->wps);
        os_free(hapd->wps);
        hapd->wps = NULL;
        hostapd_wps_clear_ies(hapd);
@@ -1048,6 +1123,24 @@ int hostapd_wps_button_pushed(struct hostapd_data *hapd,
 }
 
 
+static int wps_cancel(struct hostapd_data *hapd, void *ctx)
+{
+       if (hapd->wps == NULL)
+               return 0;
+
+       wps_registrar_wps_cancel(hapd->wps->registrar);
+       ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
+
+       return 0;
+}
+
+
+int hostapd_wps_cancel(struct hostapd_data *hapd)
+{
+       return hostapd_wps_for_each(hapd, wps_cancel, NULL);
+}
+
+
 #ifdef CONFIG_WPS_OOB
 int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type,
                          char *path, char *method, char *name)
@@ -1104,7 +1197,8 @@ error:
 
 static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
                                    const u8 *bssid,
-                                   const u8 *ie, size_t ie_len)
+                                   const u8 *ie, size_t ie_len,
+                                   int ssi_signal)
 {
        struct hostapd_data *hapd = ctx;
        struct wpabuf *wps_ie;
@@ -1275,6 +1369,7 @@ 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->ap_pin_failures_consecutive = 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);
@@ -1332,7 +1427,7 @@ const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout)
        struct wps_ap_pin_data data;
 
        pin = wps_generate_pin();
-       os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%u", pin);
+       os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%08u", pin);
        data.timeout = timeout;
        hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
        return hapd->conf->ap_pin;
@@ -1420,3 +1515,156 @@ int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
 
        return wps_registrar_config_ap(hapd->wps->registrar, &cred);
 }
+
+
+#ifdef CONFIG_WPS_NFC
+
+struct wps_nfc_password_token_data {
+       const u8 *oob_dev_pw;
+       size_t oob_dev_pw_len;
+       int added;
+};
+
+
+static int wps_add_nfc_password_token(struct hostapd_data *hapd, void *ctx)
+{
+       struct wps_nfc_password_token_data *data = ctx;
+       int ret;
+
+       if (hapd->wps == NULL)
+               return 0;
+       ret = wps_registrar_add_nfc_password_token(hapd->wps->registrar,
+                                                  data->oob_dev_pw,
+                                                  data->oob_dev_pw_len);
+       if (ret == 0)
+               data->added++;
+       return ret;
+}
+
+
+static int hostapd_wps_add_nfc_password_token(struct hostapd_data *hapd,
+                                             struct wps_parse_attr *attr)
+{
+       struct wps_nfc_password_token_data data;
+
+       data.oob_dev_pw = attr->oob_dev_password;
+       data.oob_dev_pw_len = attr->oob_dev_password_len;
+       data.added = 0;
+       if (hostapd_wps_for_each(hapd, wps_add_nfc_password_token, &data) < 0)
+               return -1;
+       return data.added ? 0 : -1;
+}
+
+
+static int hostapd_wps_nfc_tag_process(struct hostapd_data *hapd,
+                                      const struct wpabuf *wps)
+{
+       struct wps_parse_attr attr;
+
+       wpa_hexdump_buf(MSG_DEBUG, "WPS: Received NFC tag payload", wps);
+
+       if (wps_parse_msg(wps, &attr)) {
+               wpa_printf(MSG_DEBUG, "WPS: Ignore invalid data from NFC tag");
+               return -1;
+       }
+
+       if (attr.oob_dev_password)
+               return hostapd_wps_add_nfc_password_token(hapd, &attr);
+
+       wpa_printf(MSG_DEBUG, "WPS: Ignore unrecognized NFC tag");
+       return -1;
+}
+
+
+int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
+                            const struct wpabuf *data)
+{
+       const struct wpabuf *wps = data;
+       struct wpabuf *tmp = NULL;
+       int ret;
+
+       if (wpabuf_len(data) < 4)
+               return -1;
+
+       if (*wpabuf_head_u8(data) != 0x10) {
+               /* Assume this contains full NDEF record */
+               tmp = ndef_parse_wifi(data);
+               if (tmp == NULL) {
+                       wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
+                       return -1;
+               }
+               wps = tmp;
+       }
+
+       ret = hostapd_wps_nfc_tag_process(hapd, wps);
+       wpabuf_free(tmp);
+       return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
+                                            int ndef)
+{
+       struct wpabuf *ret;
+
+       if (hapd->wps == NULL)
+               return NULL;
+
+       ret = wps_get_oob_cred(hapd->wps);
+       if (ndef && ret) {
+               struct wpabuf *tmp;
+               tmp = ndef_build_wifi(ret);
+               wpabuf_free(ret);
+               if (tmp == NULL)
+                       return NULL;
+               ret = tmp;
+       }
+
+       return ret;
+}
+
+
+struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef)
+{
+       return wps_nfc_token_gen(ndef, &hapd->conf->wps_nfc_dev_pw_id,
+                                &hapd->conf->wps_nfc_dh_pubkey,
+                                &hapd->conf->wps_nfc_dh_privkey,
+                                &hapd->conf->wps_nfc_dev_pw);
+}
+
+
+int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
+{
+       struct wps_context *wps = hapd->wps;
+
+       if (wps == NULL)
+               return -1;
+
+       if (!hapd->conf->wps_nfc_dh_pubkey ||
+           !hapd->conf->wps_nfc_dh_privkey ||
+           !hapd->conf->wps_nfc_dev_pw ||
+           !hapd->conf->wps_nfc_dev_pw_id)
+               return -1;
+
+       hostapd_wps_nfc_clear(wps);
+       wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
+       wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+       wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+       wps->ap_nfc_dev_pw = wpabuf_dup(hapd->conf->wps_nfc_dev_pw);
+
+       if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey ||
+           !wps->ap_nfc_dev_pw) {
+               hostapd_wps_nfc_clear(wps);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
+{
+       hostapd_wps_nfc_clear(hapd->wps);
+}
+
+#endif /* CONFIG_WPS_NFC */