From e65552ddf2912fa91228c5a793004dc4c67f5252 Mon Sep 17 00:00:00 2001 From: Jouni Malinen Date: Sun, 28 Oct 2012 17:39:46 +0200 Subject: [PATCH] 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 --- src/wps/ndef.c | 76 +++++++++++++++ src/wps/wps.h | 1 + wpa_supplicant/README-WPS | 24 +++++ wpa_supplicant/ctrl_iface.c | 159 +++++++++++++++++++++++++++++++- wpa_supplicant/wpa_cli.c | 78 ++++++++++++++++ wpa_supplicant/wps_supplicant.c | 39 ++++++++ wpa_supplicant/wps_supplicant.h | 6 ++ 7 files changed, 382 insertions(+), 1 deletion(-) diff --git a/src/wps/ndef.c b/src/wps/ndef.c index 7630ecbc6..acbf11548 100644 --- a/src/wps/ndef.c +++ b/src/wps/ndef.c @@ -17,6 +17,7 @@ #define FLAG_CHUNK (1 << 5) #define FLAG_SHORT_RECORD (1 << 4) #define FLAG_ID_LENGTH_PRESENT (1 << 3) +#define FLAG_TNF_NFC_FORUM (0x01) #define FLAG_TNF_RFC2046 (0x02) struct ndef_record { @@ -168,3 +169,78 @@ struct wpabuf * ndef_build_wifi(const struct wpabuf *buf) FLAG_TNF_RFC2046, wifi_handover_type, 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); +} diff --git a/src/wps/wps.h b/src/wps/wps.h index c45b68cf1..17ee620c9 100644 --- a/src/wps/wps.h +++ b/src/wps/wps.h @@ -858,6 +858,7 @@ struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey, /* ndef.c */ struct wpabuf * ndef_parse_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 int wps_validate_beacon(const struct wpabuf *wps_ie); diff --git a/wpa_supplicant/README-WPS b/wpa_supplicant/README-WPS index 35be3576e..1ea98432f 100644 --- a/wpa_supplicant/README-WPS +++ b/wpa_supplicant/README-WPS @@ -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 token is used to enable enrollment of a new station (that was the source of the NFC password token). + +"nfc_get_handover_req " 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 " 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 " 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 " 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. diff --git a/wpa_supplicant/ctrl_iface.c b/wpa_supplicant/ctrl_iface.c index 083543ded..21b0e17b3 100644 --- a/wpa_supplicant/ctrl_iface.c +++ b/wpa_supplicant/ctrl_iface.c @@ -846,6 +846,149 @@ static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read( 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 */ @@ -4502,7 +4645,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s, int reply_len; 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", (const u8 *) buf, os_strlen(buf)); } 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, buf + 17)) 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 */ } else if (os_strncmp(buf, "WPS_REG ", 8) == 0) { if (wpa_supplicant_ctrl_iface_wps_reg(wpa_s, buf + 8)) diff --git a/wpa_supplicant/wpa_cli.c b/wpa_supplicant/wpa_cli.c index e510b9bd4..f7e622776 100644 --- a/wpa_supplicant/wpa_cli.c +++ b/wpa_supplicant/wpa_cli.c @@ -796,6 +796,72 @@ static int wpa_cli_cmd_wps_nfc_tag_read(struct wpa_ctrl *ctrl, int argc, 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 */ @@ -2436,6 +2502,18 @@ static struct wpa_cli_cmd wpa_cli_commands[] = { { "wps_nfc_tag_read", wpa_cli_cmd_wps_nfc_tag_read, NULL, cli_cmd_flag_sensitive, " = report read NFC tag with WPS data" }, + { "nfc_get_handover_req", wpa_cli_cmd_nfc_get_handover_req, NULL, + cli_cmd_flag_none, + " = create NFC handover request" }, + { "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL, + cli_cmd_flag_none, + " = create NFC handover select" }, + { "nfc_rx_handover_req", wpa_cli_cmd_nfc_rx_handover_req, NULL, + cli_cmd_flag_none, + " = report received NFC handover request" }, + { "nfc_rx_handover_sel", wpa_cli_cmd_nfc_rx_handover_sel, NULL, + cli_cmd_flag_none, + " = report received NFC handover select" }, #endif /* CONFIG_WPS_NFC */ { "wps_reg", wpa_cli_cmd_wps_reg, wpa_cli_complete_bss, cli_cmd_flag_sensitive, diff --git a/wpa_supplicant/wps_supplicant.c b/wpa_supplicant/wps_supplicant.c index dd48ab778..06551567f 100644 --- a/wpa_supplicant/wps_supplicant.c +++ b/wpa_supplicant/wps_supplicant.c @@ -1975,6 +1975,45 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s, 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 */ diff --git a/wpa_supplicant/wps_supplicant.h b/wpa_supplicant/wps_supplicant.h index 65c5c5a84..2a3fea0c9 100644 --- a/wpa_supplicant/wps_supplicant.h +++ b/wpa_supplicant/wps_supplicant.h @@ -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_nfc_tag_read(struct wpa_supplicant *wpa_s, 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, struct wpa_scan_results *scan_res); void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);