WPS: Add wps_check_pin command for processing PIN from user input

UIs can use this command to process a PIN entered by a user and to
validate the checksum digit (if present).
This commit is contained in:
Jouni Malinen 2010-09-23 10:30:52 -07:00 committed by Jouni Malinen
parent fa37511fa7
commit 3981cb3cb8
6 changed files with 168 additions and 0 deletions

View file

@ -120,6 +120,13 @@ pushbutton event (for PBC) to allow a new WPS Enrollee to join the
network. hostapd uses the control interface as an input channel for network. hostapd uses the control interface as an input channel for
these events. these events.
The PIN value used in the commands must be processed by an UI to
remove non-digit characters and potentially, to verify the checksum
digit. "hostapd_cli wps_check_pin <PIN>" can be used to do such
processing. It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if
the checksum digit is incorrect, or the processed PIN (non-digit
characters removed) if the PIN is valid.
When a client device (WPS Enrollee) connects to hostapd (WPS When a client device (WPS Enrollee) connects to hostapd (WPS
Registrar) in order to start PIN mode negotiation for WPS, an Registrar) in order to start PIN mode negotiation for WPS, an
identifier (Enrollee UUID) is sent. hostapd will need to be configured identifier (Enrollee UUID) is sent. hostapd will need to be configured

View file

@ -35,6 +35,7 @@
#include "ap/wps_hostapd.h" #include "ap/wps_hostapd.h"
#include "ap/ctrl_iface_ap.h" #include "ap/ctrl_iface_ap.h"
#include "wps/wps_defs.h" #include "wps/wps_defs.h"
#include "wps/wps.h"
#include "ctrl_iface.h" #include "ctrl_iface.h"
@ -369,6 +370,51 @@ static int hostapd_ctrl_iface_wps_pin(struct hostapd_data *hapd, char *txt)
} }
static int hostapd_ctrl_iface_wps_check_pin(
struct hostapd_data *hapd, char *cmd, char *buf, size_t buflen)
{
char pin[9];
size_t len;
char *pos;
int ret;
wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
(u8 *) cmd, os_strlen(cmd));
for (pos = cmd, len = 0; *pos != '\0'; pos++) {
if (*pos < '0' || *pos > '9')
continue;
pin[len++] = *pos;
if (len == 9) {
wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
return -1;
}
}
if (len != 4 && len != 8) {
wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
return -1;
}
pin[len] = '\0';
if (len == 8) {
unsigned int pin_val;
pin_val = atoi(pin);
if (!wps_pin_valid(pin_val)) {
wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
if (ret < 0 || (size_t) ret >= buflen)
return -1;
return ret;
}
}
ret = os_snprintf(buf, buflen, "%s", pin);
if (ret < 0 || (size_t) ret >= buflen)
return -1;
return ret;
}
#ifdef CONFIG_WPS_OOB #ifdef CONFIG_WPS_OOB
static int hostapd_ctrl_iface_wps_oob(struct hostapd_data *hapd, char *txt) static int hostapd_ctrl_iface_wps_oob(struct hostapd_data *hapd, char *txt)
{ {
@ -589,6 +635,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
} else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) { } else if (os_strncmp(buf, "WPS_PIN ", 8) == 0) {
if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8)) if (hostapd_ctrl_iface_wps_pin(hapd, buf + 8))
reply_len = -1; reply_len = -1;
} else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
reply_len = hostapd_ctrl_iface_wps_check_pin(
hapd, buf + 14, reply, reply_size);
} else if (os_strcmp(buf, "WPS_PBC") == 0) { } else if (os_strcmp(buf, "WPS_PBC") == 0) {
if (hostapd_wps_button_pushed(hapd)) if (hostapd_wps_button_pushed(hapd))
reply_len = -1; reply_len = -1;

View file

@ -90,6 +90,7 @@ static const char *commands_help =
#endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WPS #ifdef CONFIG_WPS
" wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n" " wps_pin <uuid> <pin> [timeout] [addr] add WPS Enrollee PIN\n"
" wps_check_pin <PIN> verify PIN checksum\n"
" wps_pbc indicate button pushed to initiate PBC\n" " wps_pbc indicate button pushed to initiate PBC\n"
#ifdef CONFIG_WPS_OOB #ifdef CONFIG_WPS_OOB
" wps_oob <type> <path> <method> use WPS with out-of-band (UFD)\n" " wps_oob <type> <path> <method> use WPS with out-of-band (UFD)\n"
@ -370,6 +371,32 @@ static int hostapd_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc,
} }
static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256];
int res;
if (argc != 1 && argc != 2) {
printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
"- PIN to be verified\n");
return -1;
}
if (argc == 2)
res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
argv[0], argv[1]);
else
res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
argv[0]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
printf("Too long WPS_CHECK_PIN command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc, static int hostapd_cli_cmd_wps_pbc(struct wpa_ctrl *ctrl, int argc,
char *argv[]) char *argv[])
{ {
@ -608,6 +635,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
#endif /* CONFIG_IEEE80211W */ #endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WPS #ifdef CONFIG_WPS
{ "wps_pin", hostapd_cli_cmd_wps_pin }, { "wps_pin", hostapd_cli_cmd_wps_pin },
{ "wps_check_pin", hostapd_cli_cmd_wps_check_pin },
{ "wps_pbc", hostapd_cli_cmd_wps_pbc }, { "wps_pbc", hostapd_cli_cmd_wps_pbc },
#ifdef CONFIG_WPS_OOB #ifdef CONFIG_WPS_OOB
{ "wps_oob", hostapd_cli_cmd_wps_oob }, { "wps_oob", hostapd_cli_cmd_wps_oob },

View file

@ -94,6 +94,13 @@ pushbutton event (for PBC) to allow a new WPS Enrollee to join the
network. wpa_supplicant uses the control interface as an input channel network. wpa_supplicant uses the control interface as an input channel
for these events. for these events.
The PIN value used in the commands must be processed by an UI to
remove non-digit characters and potentially, to verify the checksum
digit. "wpa_cli wps_check_pin <PIN>" can be used to do such processing.
It returns FAIL if the PIN is invalid, or FAIL-CHECKSUM if the checksum
digit is incorrect, or the processed PIN (non-digit characters removed)
if the PIN is valid.
If the client device has a display, a random PIN has to be generated If the client device has a display, a random PIN has to be generated
for each WPS registration session. wpa_supplicant can do this with a for each WPS registration session. wpa_supplicant can do this with a
control interface request, e.g., by calling wpa_cli: control interface request, e.g., by calling wpa_cli:

View file

@ -261,6 +261,51 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
} }
static int wpa_supplicant_ctrl_iface_wps_check_pin(
struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
{
char pin[9];
size_t len;
char *pos;
int ret;
wpa_hexdump_ascii_key(MSG_DEBUG, "WPS_CHECK_PIN",
(u8 *) cmd, os_strlen(cmd));
for (pos = cmd, len = 0; *pos != '\0'; pos++) {
if (*pos < '0' || *pos > '9')
continue;
pin[len++] = *pos;
if (len == 9) {
wpa_printf(MSG_DEBUG, "WPS: Too long PIN");
return -1;
}
}
if (len != 4 && len != 8) {
wpa_printf(MSG_DEBUG, "WPS: Invalid PIN length %d", (int) len);
return -1;
}
pin[len] = '\0';
if (len == 8) {
unsigned int pin_val;
pin_val = atoi(pin);
if (!wps_pin_valid(pin_val)) {
wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
if (ret < 0 || (size_t) ret >= buflen)
return -1;
return ret;
}
}
ret = os_snprintf(buf, buflen, "%s", pin);
if (ret < 0 || (size_t) ret >= buflen)
return -1;
return ret;
}
#ifdef CONFIG_WPS_OOB #ifdef CONFIG_WPS_OOB
static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s, static int wpa_supplicant_ctrl_iface_wps_oob(struct wpa_supplicant *wpa_s,
char *cmd) char *cmd)
@ -2715,6 +2760,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8, reply_len = wpa_supplicant_ctrl_iface_wps_pin(wpa_s, buf + 8,
reply, reply,
reply_size); reply_size);
} else if (os_strncmp(buf, "WPS_CHECK_PIN ", 14) == 0) {
reply_len = wpa_supplicant_ctrl_iface_wps_check_pin(
wpa_s, buf + 14, reply, reply_size);
} else if (os_strcmp(buf, "WPS_CANCEL") == 0) { } else if (os_strcmp(buf, "WPS_CANCEL") == 0) {
if (wpas_wps_cancel(wpa_s)) if (wpas_wps_cancel(wpa_s))
reply_len = -1; reply_len = -1;

View file

@ -615,6 +615,32 @@ static int wpa_cli_cmd_wps_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
} }
static int wpa_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
char cmd[256];
int res;
if (argc != 1 && argc != 2) {
printf("Invalid WPS_CHECK_PIN command: needs one argument:\n"
"- PIN to be verified\n");
return -1;
}
if (argc == 2)
res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s %s",
argv[0], argv[1]);
else
res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
argv[0]);
if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
printf("Too long WPS_CHECK_PIN command.\n");
return -1;
}
return wpa_ctrl_command(ctrl, cmd);
}
static int wpa_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc, static int wpa_cli_cmd_wps_cancel(struct wpa_ctrl *ctrl, int argc,
char *argv[]) char *argv[])
{ {
@ -2271,6 +2297,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
cli_cmd_flag_sensitive, cli_cmd_flag_sensitive,
"<BSSID> [PIN] = start WPS PIN method (returns PIN, if not " "<BSSID> [PIN] = start WPS PIN method (returns PIN, if not "
"hardcoded)" }, "hardcoded)" },
{ "wps_check_pin", wpa_cli_cmd_wps_check_pin,
cli_cmd_flag_sensitive,
"<PIN> = verify PIN checksum" },
{ "wps_cancel", wpa_cli_cmd_wps_cancel, cli_cmd_flag_none, { "wps_cancel", wpa_cli_cmd_wps_cancel, cli_cmd_flag_none,
"Cancels the pending WPS operation" }, "Cancels the pending WPS operation" },
#ifdef CONFIG_WPS_OOB #ifdef CONFIG_WPS_OOB