WPS: Add preliminary NFC connection handover support for Enrollee

This commit adds new wpa_supplicant ctrl_iface commands to allow
external programs to go through NFC connection handover mechanism
with wpa_supplicant taking care of the WPS processing. This version
includes only the case where wpa_supplicant is operating as a
station/Enrollee.

Signed-hostap: Jouni Malinen <j@w1.fi>
This commit is contained in:
Jouni Malinen 2012-10-28 17:39:46 +02:00
parent 86cf382b80
commit e65552ddf2
7 changed files with 382 additions and 1 deletions

View file

@ -17,6 +17,7 @@
#define FLAG_CHUNK (1 << 5) #define FLAG_CHUNK (1 << 5)
#define FLAG_SHORT_RECORD (1 << 4) #define FLAG_SHORT_RECORD (1 << 4)
#define FLAG_ID_LENGTH_PRESENT (1 << 3) #define FLAG_ID_LENGTH_PRESENT (1 << 3)
#define FLAG_TNF_NFC_FORUM (0x01)
#define FLAG_TNF_RFC2046 (0x02) #define FLAG_TNF_RFC2046 (0x02)
struct ndef_record { struct ndef_record {
@ -168,3 +169,78 @@ struct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
FLAG_TNF_RFC2046, wifi_handover_type, FLAG_TNF_RFC2046, wifi_handover_type,
os_strlen(wifi_handover_type), NULL, 0, buf); os_strlen(wifi_handover_type), NULL, 0, buf);
} }
struct wpabuf * ndef_build_wifi_hr(void)
{
struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr;
struct wpabuf *carrier, *hc;
rn = wpabuf_alloc(2);
if (rn == NULL)
return NULL;
wpabuf_put_be16(rn, os_random() & 0xffff);
cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2,
NULL, 0, rn);
wpabuf_free(rn);
if (cr == NULL)
return NULL;
ac_payload = wpabuf_alloc(4);
if (ac_payload == NULL) {
wpabuf_free(cr);
return NULL;
}
wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */
wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */
wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */
wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */
ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2,
NULL, 0, ac_payload);
wpabuf_free(ac_payload);
if (ac == NULL) {
wpabuf_free(cr);
return NULL;
}
hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac));
if (hr_payload == NULL) {
wpabuf_free(cr);
wpabuf_free(ac);
return NULL;
}
wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */
wpabuf_put_buf(hr_payload, cr);
wpabuf_put_buf(hr_payload, ac);
wpabuf_free(cr);
wpabuf_free(ac);
hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2,
NULL, 0, hr_payload);
wpabuf_free(hr_payload);
if (hr == NULL)
return NULL;
carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
if (carrier == NULL) {
wpabuf_free(hr);
return NULL;
}
wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
wpabuf_put_str(carrier, wifi_handover_type);
hc = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
"0", 1, carrier);
wpabuf_free(carrier);
if (hc == NULL) {
wpabuf_free(hr);
return NULL;
}
return wpabuf_concat(hr, hc);
}

View file

@ -858,6 +858,7 @@ struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
/* ndef.c */ /* ndef.c */
struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf); struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
struct wpabuf * ndef_build_wifi(const struct wpabuf *buf); struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
struct wpabuf * ndef_build_wifi_hr(void);
#ifdef CONFIG_WPS_STRICT #ifdef CONFIG_WPS_STRICT
int wps_validate_beacon(const struct wpabuf *wps_ie); int wps_validate_beacon(const struct wpabuf *wps_ie);

View file

@ -351,3 +351,27 @@ an AP) using a special value "nfc-pw" in place of the PIN parameter. If
the ER functionality has been started (wps_er_start), the NFC password the ER functionality has been started (wps_er_start), the NFC password
token is used to enable enrollment of a new station (that was the source token is used to enable enrollment of a new station (that was the source
of the NFC password token). of the NFC password token).
"nfc_get_handover_req <NDEF> <WPS>" command can be used to build the
contents of a Handover Request Message for connection handover. The
first argument selects the format of the output data and the second
argument selects which type of connection handover is requested (WPS =
Wi-Fi handover as specified in WSC 2.0).
"nfc_get_handover_sel <NDEF> <WPS>" command can be used to build the
contents of a Handover Select Message for connection handover when this
does not depend on the contents of the Handover Request Message. The
first argument selects the format of the output data and the second
argument selects which type of connection handover is requested (WPS =
Wi-Fi handover as specified in WSC 2.0).
"nfc_rx_handover_req <hexdump of payload>" is used to indicate receipt
of NFC connection handover request. The payload may include multiple
carriers the the applicable ones are matched based on the media
type. The reply data is contents for the Handover Select Message
(hexdump).
"nfc_rx_handover_sel <hexdump of payload>" is used to indicate receipt
of NFC connection handover select. The payload may include multiple
carriers the the applicable ones are matched based on the media
type.

View file

@ -846,6 +846,149 @@ static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
return ret; return ret;
} }
static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
char *reply, size_t max_len)
{
struct wpabuf *buf;
int res;
buf = wpas_wps_nfc_handover_req(wpa_s);
if (buf == NULL)
return -1;
res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
wpabuf_len(buf));
reply[res++] = '\n';
reply[res] = '\0';
wpabuf_free(buf);
return res;
}
static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s,
char *cmd, char *reply,
size_t max_len)
{
char *pos;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
if (os_strcmp(cmd, "NDEF") != 0)
return -1;
if (os_strcmp(pos, "WPS") == 0) {
return wpas_ctrl_nfc_get_handover_req_wps(wpa_s, reply,
max_len);
}
return -1;
}
static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s,
char *reply, size_t max_len)
{
struct wpabuf *buf;
int res;
buf = wpas_wps_nfc_handover_sel(wpa_s);
if (buf == NULL)
return -1;
res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
wpabuf_len(buf));
reply[res++] = '\n';
reply[res] = '\0';
wpabuf_free(buf);
return res;
}
static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
char *cmd, char *reply,
size_t max_len)
{
char *pos;
pos = os_strchr(cmd, ' ');
if (pos == NULL)
return -1;
*pos++ = '\0';
if (os_strcmp(cmd, "NDEF") != 0)
return -1;
if (os_strcmp(pos, "WPS") == 0) {
return wpas_ctrl_nfc_get_handover_sel_wps(wpa_s, reply,
max_len);
}
return -1;
}
static int wpas_ctrl_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
char *cmd, char *reply,
size_t max_len)
{
size_t len;
struct wpabuf *buf;
int ret;
len = os_strlen(cmd);
if (len & 0x01)
return -1;
len /= 2;
buf = wpabuf_alloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
wpabuf_free(buf);
return -1;
}
ret = wpas_wps_nfc_rx_handover_req(wpa_s, buf);
wpabuf_free(buf);
return ret;
}
static int wpas_ctrl_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
char *cmd)
{
size_t len;
struct wpabuf *buf;
int ret;
len = os_strlen(cmd);
if (len & 0x01)
return -1;
len /= 2;
buf = wpabuf_alloc(len);
if (buf == NULL)
return -1;
if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
wpabuf_free(buf);
return -1;
}
ret = wpas_wps_nfc_rx_handover_sel(wpa_s, buf);
wpabuf_free(buf);
return ret;
}
#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_NFC */
@ -4502,7 +4645,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
int reply_len; int reply_len;
if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 || if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
os_strncmp(buf, "SET_NETWORK ", 12) == 0) { os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
os_strncmp(buf, "NFC_RX_HANDOVER_SEL", 19) == 0) {
wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface", wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
(const u8 *) buf, os_strlen(buf)); (const u8 *) buf, os_strlen(buf));
} else { } else {
@ -4640,6 +4785,18 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s, if (wpa_supplicant_ctrl_iface_wps_nfc_tag_read(wpa_s,
buf + 17)) buf + 17))
reply_len = -1; reply_len = -1;
} else if (os_strncmp(buf, "NFC_GET_HANDOVER_REQ ", 21) == 0) {
reply_len = wpas_ctrl_nfc_get_handover_req(
wpa_s, buf + 21, reply, reply_size);
} else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
reply_len = wpas_ctrl_nfc_get_handover_sel(
wpa_s, buf + 21, reply, reply_size);
} else if (os_strncmp(buf, "NFC_RX_HANDOVER_REQ ", 20) == 0) {
reply_len = wpas_ctrl_nfc_rx_handover_req(
wpa_s, buf + 20, reply, reply_size);
} else if (os_strncmp(buf, "NFC_RX_HANDOVER_SEL ", 20) == 0) {
if (wpas_ctrl_nfc_rx_handover_sel(wpa_s, buf + 20))
reply_len = -1;
#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_NFC */
} else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) {
if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8))

View file

@ -796,6 +796,72 @@ static int wpa_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc,
return ret; return ret;
} }
static int wpa_cli_cmd_nfc_get_handover_req(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_REQ", 2, argc, argv);
}
static int wpa_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
return wpa_cli_cmd(ctrl, "NFC_GET_HANDOVER_SEL", 2, argc, argv);
}
static int wpa_cli_cmd_nfc_rx_handover_req(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
int ret;
char *buf;
size_t buflen;
if (argc != 1) {
printf("Invalid 'nfc_rx_handover_req' command - one argument "
"is required.\n");
return -1;
}
buflen = 21 + os_strlen(argv[0]);
buf = os_malloc(buflen);
if (buf == NULL)
return -1;
os_snprintf(buf, buflen, "NFC_RX_HANDOVER_REQ %s", argv[0]);
ret = wpa_ctrl_command(ctrl, buf);
os_free(buf);
return ret;
}
static int wpa_cli_cmd_nfc_rx_handover_sel(struct wpa_ctrl *ctrl, int argc,
char *argv[])
{
int ret;
char *buf;
size_t buflen;
if (argc != 1) {
printf("Invalid 'nfc_rx_handover_sel' command - one argument "
"is required.\n");
return -1;
}
buflen = 21 + os_strlen(argv[0]);
buf = os_malloc(buflen);
if (buf == NULL)
return -1;
os_snprintf(buf, buflen, "NFC_RX_HANDOVER_SEL %s", argv[0]);
ret = wpa_ctrl_command(ctrl, buf);
os_free(buf);
return ret;
}
#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_NFC */
@ -2436,6 +2502,18 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
{ "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL, { "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL,
cli_cmd_flag_sensitive, cli_cmd_flag_sensitive,
"<hexdump of payload> = report read NFC tag with WPS data" }, "<hexdump of payload> = report read NFC tag with WPS data" },
{ "nfc_get_handover_req", wpa_cli_cmd_nfc_get_handover_req, NULL,
cli_cmd_flag_none,
"<NDEF> <WPS> = create NFC handover request" },
{ "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL,
cli_cmd_flag_none,
"<NDEF> <WPS> = create NFC handover select" },
{ "nfc_rx_handover_req", wpa_cli_cmd_nfc_rx_handover_req, NULL,
cli_cmd_flag_none,
"<hexdump of payload> = report received NFC handover request" },
{ "nfc_rx_handover_sel", wpa_cli_cmd_nfc_rx_handover_sel, NULL,
cli_cmd_flag_none,
"<hexdump of payload> = report received NFC handover select" },
#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_NFC */
{ "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss, { "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss,
cli_cmd_flag_sensitive, cli_cmd_flag_sensitive,

View file

@ -1975,6 +1975,45 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
return ret; return ret;
} }
struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s)
{
return ndef_build_wifi_hr();
}
struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s)
{
return NULL;
}
int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
const struct wpabuf *data)
{
/* TODO */
return -1;
}
int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
const struct wpabuf *data)
{
struct wpabuf *wps;
int ret;
wps = ndef_parse_wifi(data);
if (wps == NULL)
return -1;
wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
"payload from NFC connection handover");
wpa_hexdump_buf_key(MSG_DEBUG, "WPS: NFC payload", wps);
ret = wpas_wps_nfc_tag_process(wpa_s, wps);
wpabuf_free(wps);
return ret;
}
#endif /* CONFIG_WPS_NFC */ #endif /* CONFIG_WPS_NFC */

View file

@ -69,6 +69,12 @@ struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef);
int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid); int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid);
int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
const struct wpabuf *data); const struct wpabuf *data);
struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s);
struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s);
int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
const struct wpabuf *data);
int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
const struct wpabuf *data);
void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s, void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
struct wpa_scan_results *scan_res); struct wpa_scan_results *scan_res);
void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid); void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);