139beb93cSSam Leffler /*
239beb93cSSam Leffler * EAP-WSC peer for Wi-Fi Protected Setup
3f05cddf9SRui Paulo * Copyright (c) 2007-2009, 2012, Jouni Malinen <j@w1.fi>
439beb93cSSam Leffler *
5f05cddf9SRui Paulo * This software may be distributed under the terms of the BSD license.
6f05cddf9SRui Paulo * See README for more details.
739beb93cSSam Leffler */
839beb93cSSam Leffler
939beb93cSSam Leffler #include "includes.h"
1039beb93cSSam Leffler
1139beb93cSSam Leffler #include "common.h"
1239beb93cSSam Leffler #include "uuid.h"
1339beb93cSSam Leffler #include "eap_i.h"
1439beb93cSSam Leffler #include "eap_common/eap_wsc_common.h"
1539beb93cSSam Leffler #include "wps/wps.h"
1639beb93cSSam Leffler #include "wps/wps_defs.h"
1739beb93cSSam Leffler
1839beb93cSSam Leffler
1939beb93cSSam Leffler struct eap_wsc_data {
20780fb4a2SCy Schubert enum { WAIT_START, MESG, WAIT_FRAG_ACK, FAIL } state;
2139beb93cSSam Leffler int registrar;
2239beb93cSSam Leffler struct wpabuf *in_buf;
2339beb93cSSam Leffler struct wpabuf *out_buf;
2439beb93cSSam Leffler enum wsc_op_code in_op_code, out_op_code;
2539beb93cSSam Leffler size_t out_used;
2639beb93cSSam Leffler size_t fragment_size;
2739beb93cSSam Leffler struct wps_data *wps;
2839beb93cSSam Leffler struct wps_context *wps_ctx;
2939beb93cSSam Leffler };
3039beb93cSSam Leffler
3139beb93cSSam Leffler
eap_wsc_state_txt(int state)3239beb93cSSam Leffler static const char * eap_wsc_state_txt(int state)
3339beb93cSSam Leffler {
3439beb93cSSam Leffler switch (state) {
3539beb93cSSam Leffler case WAIT_START:
3639beb93cSSam Leffler return "WAIT_START";
3739beb93cSSam Leffler case MESG:
3839beb93cSSam Leffler return "MESG";
3939beb93cSSam Leffler case WAIT_FRAG_ACK:
4039beb93cSSam Leffler return "WAIT_FRAG_ACK";
4139beb93cSSam Leffler case FAIL:
4239beb93cSSam Leffler return "FAIL";
4339beb93cSSam Leffler default:
4439beb93cSSam Leffler return "?";
4539beb93cSSam Leffler }
4639beb93cSSam Leffler }
4739beb93cSSam Leffler
4839beb93cSSam Leffler
eap_wsc_state(struct eap_wsc_data * data,int state)4939beb93cSSam Leffler static void eap_wsc_state(struct eap_wsc_data *data, int state)
5039beb93cSSam Leffler {
5139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s",
5239beb93cSSam Leffler eap_wsc_state_txt(data->state),
5339beb93cSSam Leffler eap_wsc_state_txt(state));
5439beb93cSSam Leffler data->state = state;
5539beb93cSSam Leffler }
5639beb93cSSam Leffler
5739beb93cSSam Leffler
eap_wsc_new_ap_settings(struct wps_credential * cred,const char * params)58e28a4053SRui Paulo static int eap_wsc_new_ap_settings(struct wps_credential *cred,
59e28a4053SRui Paulo const char *params)
60e28a4053SRui Paulo {
61e28a4053SRui Paulo const char *pos, *end;
62e28a4053SRui Paulo size_t len;
63e28a4053SRui Paulo
64e28a4053SRui Paulo os_memset(cred, 0, sizeof(*cred));
65e28a4053SRui Paulo
66e28a4053SRui Paulo pos = os_strstr(params, "new_ssid=");
67e28a4053SRui Paulo if (pos == NULL)
68e28a4053SRui Paulo return 0;
69e28a4053SRui Paulo pos += 9;
70e28a4053SRui Paulo end = os_strchr(pos, ' ');
71e28a4053SRui Paulo if (end == NULL)
72e28a4053SRui Paulo len = os_strlen(pos);
73e28a4053SRui Paulo else
74e28a4053SRui Paulo len = end - pos;
75e28a4053SRui Paulo if ((len & 1) || len > 2 * sizeof(cred->ssid) ||
765b9c547cSRui Paulo hexstr2bin(pos, cred->ssid, len / 2)) {
775b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid");
78e28a4053SRui Paulo return -1;
795b9c547cSRui Paulo }
80e28a4053SRui Paulo cred->ssid_len = len / 2;
81e28a4053SRui Paulo
82e28a4053SRui Paulo pos = os_strstr(params, "new_auth=");
835b9c547cSRui Paulo if (pos == NULL) {
845b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth");
85e28a4053SRui Paulo return -1;
865b9c547cSRui Paulo }
87e28a4053SRui Paulo if (os_strncmp(pos + 9, "OPEN", 4) == 0)
88e28a4053SRui Paulo cred->auth_type = WPS_AUTH_OPEN;
89e28a4053SRui Paulo else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0)
90e28a4053SRui Paulo cred->auth_type = WPS_AUTH_WPAPSK;
91e28a4053SRui Paulo else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0)
92e28a4053SRui Paulo cred->auth_type = WPS_AUTH_WPA2PSK;
935b9c547cSRui Paulo else {
945b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth");
95e28a4053SRui Paulo return -1;
965b9c547cSRui Paulo }
97e28a4053SRui Paulo
98e28a4053SRui Paulo pos = os_strstr(params, "new_encr=");
995b9c547cSRui Paulo if (pos == NULL) {
1005b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr");
101e28a4053SRui Paulo return -1;
1025b9c547cSRui Paulo }
103e28a4053SRui Paulo if (os_strncmp(pos + 9, "NONE", 4) == 0)
104e28a4053SRui Paulo cred->encr_type = WPS_ENCR_NONE;
1055b9c547cSRui Paulo #ifdef CONFIG_TESTING_OPTIONS
106e28a4053SRui Paulo else if (os_strncmp(pos + 9, "WEP", 3) == 0)
107e28a4053SRui Paulo cred->encr_type = WPS_ENCR_WEP;
1085b9c547cSRui Paulo #endif /* CONFIG_TESTING_OPTIONS */
109e28a4053SRui Paulo else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
110e28a4053SRui Paulo cred->encr_type = WPS_ENCR_TKIP;
111e28a4053SRui Paulo else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
112e28a4053SRui Paulo cred->encr_type = WPS_ENCR_AES;
1135b9c547cSRui Paulo else {
1145b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr");
115e28a4053SRui Paulo return -1;
1165b9c547cSRui Paulo }
117e28a4053SRui Paulo
118e28a4053SRui Paulo pos = os_strstr(params, "new_key=");
119e28a4053SRui Paulo if (pos == NULL)
120e28a4053SRui Paulo return 0;
121e28a4053SRui Paulo pos += 8;
122e28a4053SRui Paulo end = os_strchr(pos, ' ');
123e28a4053SRui Paulo if (end == NULL)
124e28a4053SRui Paulo len = os_strlen(pos);
125e28a4053SRui Paulo else
126e28a4053SRui Paulo len = end - pos;
127e28a4053SRui Paulo if ((len & 1) || len > 2 * sizeof(cred->key) ||
1285b9c547cSRui Paulo hexstr2bin(pos, cred->key, len / 2)) {
1295b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key");
130e28a4053SRui Paulo return -1;
1315b9c547cSRui Paulo }
132e28a4053SRui Paulo cred->key_len = len / 2;
133e28a4053SRui Paulo
134e28a4053SRui Paulo return 1;
135e28a4053SRui Paulo }
136e28a4053SRui Paulo
137e28a4053SRui Paulo
eap_wsc_init(struct eap_sm * sm)13839beb93cSSam Leffler static void * eap_wsc_init(struct eap_sm *sm)
13939beb93cSSam Leffler {
14039beb93cSSam Leffler struct eap_wsc_data *data;
14139beb93cSSam Leffler const u8 *identity;
14239beb93cSSam Leffler size_t identity_len;
14339beb93cSSam Leffler int registrar;
14439beb93cSSam Leffler struct wps_config cfg;
1455b9c547cSRui Paulo const char *pos, *end;
14639beb93cSSam Leffler const char *phase1;
14739beb93cSSam Leffler struct wps_context *wps;
148e28a4053SRui Paulo struct wps_credential new_ap_settings;
149e28a4053SRui Paulo int res;
150f05cddf9SRui Paulo int nfc = 0;
1515b9c547cSRui Paulo u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
15239beb93cSSam Leffler
15339beb93cSSam Leffler wps = sm->wps;
15439beb93cSSam Leffler if (wps == NULL) {
15539beb93cSSam Leffler wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
15639beb93cSSam Leffler return NULL;
15739beb93cSSam Leffler }
15839beb93cSSam Leffler
15939beb93cSSam Leffler identity = eap_get_config_identity(sm, &identity_len);
16039beb93cSSam Leffler
16139beb93cSSam Leffler if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
16239beb93cSSam Leffler os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
16339beb93cSSam Leffler registrar = 1; /* Supplicant is Registrar */
16439beb93cSSam Leffler else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
16539beb93cSSam Leffler os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
16639beb93cSSam Leffler registrar = 0; /* Supplicant is Enrollee */
16739beb93cSSam Leffler else {
16839beb93cSSam Leffler wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
16939beb93cSSam Leffler identity, identity_len);
17039beb93cSSam Leffler return NULL;
17139beb93cSSam Leffler }
17239beb93cSSam Leffler
17339beb93cSSam Leffler data = os_zalloc(sizeof(*data));
17439beb93cSSam Leffler if (data == NULL)
17539beb93cSSam Leffler return NULL;
17639beb93cSSam Leffler data->state = registrar ? MESG : WAIT_START;
17739beb93cSSam Leffler data->registrar = registrar;
17839beb93cSSam Leffler data->wps_ctx = wps;
17939beb93cSSam Leffler
18039beb93cSSam Leffler os_memset(&cfg, 0, sizeof(cfg));
18139beb93cSSam Leffler cfg.wps = wps;
18239beb93cSSam Leffler cfg.registrar = registrar;
18339beb93cSSam Leffler
18439beb93cSSam Leffler phase1 = eap_get_config_phase1(sm);
18539beb93cSSam Leffler if (phase1 == NULL) {
18639beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
18739beb93cSSam Leffler "set");
18839beb93cSSam Leffler os_free(data);
18939beb93cSSam Leffler return NULL;
19039beb93cSSam Leffler }
19139beb93cSSam Leffler
19239beb93cSSam Leffler pos = os_strstr(phase1, "pin=");
19339beb93cSSam Leffler if (pos) {
19439beb93cSSam Leffler pos += 4;
19539beb93cSSam Leffler cfg.pin = (const u8 *) pos;
19639beb93cSSam Leffler while (*pos != '\0' && *pos != ' ')
19739beb93cSSam Leffler pos++;
19839beb93cSSam Leffler cfg.pin_len = pos - (const char *) cfg.pin;
1995b9c547cSRui Paulo if (cfg.pin_len == 6 &&
2005b9c547cSRui Paulo os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) {
201f05cddf9SRui Paulo cfg.pin = NULL;
202f05cddf9SRui Paulo cfg.pin_len = 0;
203f05cddf9SRui Paulo nfc = 1;
204f05cddf9SRui Paulo }
20539beb93cSSam Leffler } else {
20639beb93cSSam Leffler pos = os_strstr(phase1, "pbc=1");
20739beb93cSSam Leffler if (pos)
20839beb93cSSam Leffler cfg.pbc = 1;
20939beb93cSSam Leffler }
21039beb93cSSam Leffler
2115b9c547cSRui Paulo pos = os_strstr(phase1, "dev_pw_id=");
2125b9c547cSRui Paulo if (pos) {
2135b9c547cSRui Paulo u16 id = atoi(pos + 10);
2145b9c547cSRui Paulo if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
2155b9c547cSRui Paulo nfc = 1;
2165b9c547cSRui Paulo if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
2175b9c547cSRui Paulo cfg.dev_pw_id = id;
2185b9c547cSRui Paulo }
2195b9c547cSRui Paulo
220f05cddf9SRui Paulo if (cfg.pin == NULL && !cfg.pbc && !nfc) {
22139beb93cSSam Leffler wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
22239beb93cSSam Leffler "configuration data");
22339beb93cSSam Leffler os_free(data);
22439beb93cSSam Leffler return NULL;
22539beb93cSSam Leffler }
22639beb93cSSam Leffler
2275b9c547cSRui Paulo pos = os_strstr(phase1, " pkhash=");
2285b9c547cSRui Paulo if (pos) {
2295b9c547cSRui Paulo size_t len;
2305b9c547cSRui Paulo pos += 8;
2315b9c547cSRui Paulo end = os_strchr(pos, ' ');
2325b9c547cSRui Paulo if (end)
2335b9c547cSRui Paulo len = end - pos;
2345b9c547cSRui Paulo else
2355b9c547cSRui Paulo len = os_strlen(pos);
2365b9c547cSRui Paulo if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
2375b9c547cSRui Paulo hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
2385b9c547cSRui Paulo wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
2395b9c547cSRui Paulo os_free(data);
2405b9c547cSRui Paulo return NULL;
2415b9c547cSRui Paulo }
2425b9c547cSRui Paulo cfg.peer_pubkey_hash = pkhash;
2435b9c547cSRui Paulo }
244f05cddf9SRui Paulo
245e28a4053SRui Paulo res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
246e28a4053SRui Paulo if (res < 0) {
247e28a4053SRui Paulo os_free(data);
2485b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP "
2495b9c547cSRui Paulo "settings");
250e28a4053SRui Paulo return NULL;
251e28a4053SRui Paulo }
252e28a4053SRui Paulo if (res == 1) {
253e28a4053SRui Paulo wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
254e28a4053SRui Paulo "WPS");
255e28a4053SRui Paulo cfg.new_ap_settings = &new_ap_settings;
256e28a4053SRui Paulo }
257e28a4053SRui Paulo
2584bc52338SCy Schubert if (os_strstr(phase1, "multi_ap=1"))
2594bc52338SCy Schubert cfg.multi_ap_backhaul_sta = 1;
2604bc52338SCy Schubert
26139beb93cSSam Leffler data->wps = wps_init(&cfg);
26239beb93cSSam Leffler if (data->wps == NULL) {
26339beb93cSSam Leffler os_free(data);
2645b9c547cSRui Paulo wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed");
26539beb93cSSam Leffler return NULL;
26639beb93cSSam Leffler }
267f05cddf9SRui Paulo res = eap_get_config_fragment_size(sm);
268f05cddf9SRui Paulo if (res > 0)
269f05cddf9SRui Paulo data->fragment_size = res;
270f05cddf9SRui Paulo else
27139beb93cSSam Leffler data->fragment_size = WSC_FRAGMENT_SIZE;
272f05cddf9SRui Paulo wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
273f05cddf9SRui Paulo (unsigned int) data->fragment_size);
27439beb93cSSam Leffler
27539beb93cSSam Leffler if (registrar && cfg.pin) {
276f05cddf9SRui Paulo wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
2773157ba21SRui Paulo cfg.pin, cfg.pin_len, 0);
27839beb93cSSam Leffler }
27939beb93cSSam Leffler
280e28a4053SRui Paulo /* Use reduced client timeout for WPS to avoid long wait */
281e28a4053SRui Paulo if (sm->ClientTimeout > 30)
282e28a4053SRui Paulo sm->ClientTimeout = 30;
283e28a4053SRui Paulo
28439beb93cSSam Leffler return data;
28539beb93cSSam Leffler }
28639beb93cSSam Leffler
28739beb93cSSam Leffler
eap_wsc_deinit(struct eap_sm * sm,void * priv)28839beb93cSSam Leffler static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
28939beb93cSSam Leffler {
29039beb93cSSam Leffler struct eap_wsc_data *data = priv;
29139beb93cSSam Leffler wpabuf_free(data->in_buf);
29239beb93cSSam Leffler wpabuf_free(data->out_buf);
29339beb93cSSam Leffler wps_deinit(data->wps);
29439beb93cSSam Leffler os_free(data->wps_ctx->network_key);
29539beb93cSSam Leffler data->wps_ctx->network_key = NULL;
29639beb93cSSam Leffler os_free(data);
29739beb93cSSam Leffler }
29839beb93cSSam Leffler
29939beb93cSSam Leffler
eap_wsc_build_msg(struct eap_wsc_data * data,struct eap_method_ret * ret,u8 id)30039beb93cSSam Leffler static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
30139beb93cSSam Leffler struct eap_method_ret *ret, u8 id)
30239beb93cSSam Leffler {
30339beb93cSSam Leffler struct wpabuf *resp;
30439beb93cSSam Leffler u8 flags;
30539beb93cSSam Leffler size_t send_len, plen;
30639beb93cSSam Leffler
307*c1d255d3SCy Schubert ret->ignore = false;
30839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
309*c1d255d3SCy Schubert ret->allowNotifications = true;
31039beb93cSSam Leffler
31139beb93cSSam Leffler flags = 0;
31239beb93cSSam Leffler send_len = wpabuf_len(data->out_buf) - data->out_used;
31339beb93cSSam Leffler if (2 + send_len > data->fragment_size) {
31439beb93cSSam Leffler send_len = data->fragment_size - 2;
31539beb93cSSam Leffler flags |= WSC_FLAGS_MF;
31639beb93cSSam Leffler if (data->out_used == 0) {
31739beb93cSSam Leffler flags |= WSC_FLAGS_LF;
31839beb93cSSam Leffler send_len -= 2;
31939beb93cSSam Leffler }
32039beb93cSSam Leffler }
32139beb93cSSam Leffler plen = 2 + send_len;
32239beb93cSSam Leffler if (flags & WSC_FLAGS_LF)
32339beb93cSSam Leffler plen += 2;
32439beb93cSSam Leffler resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
32539beb93cSSam Leffler EAP_CODE_RESPONSE, id);
32639beb93cSSam Leffler if (resp == NULL)
32739beb93cSSam Leffler return NULL;
32839beb93cSSam Leffler
32939beb93cSSam Leffler wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
33039beb93cSSam Leffler wpabuf_put_u8(resp, flags); /* Flags */
33139beb93cSSam Leffler if (flags & WSC_FLAGS_LF)
33239beb93cSSam Leffler wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
33339beb93cSSam Leffler
33439beb93cSSam Leffler wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
33539beb93cSSam Leffler send_len);
33639beb93cSSam Leffler data->out_used += send_len;
33739beb93cSSam Leffler
33839beb93cSSam Leffler ret->methodState = METHOD_MAY_CONT;
33939beb93cSSam Leffler ret->decision = DECISION_FAIL;
34039beb93cSSam Leffler
34139beb93cSSam Leffler if (data->out_used == wpabuf_len(data->out_buf)) {
34239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
34339beb93cSSam Leffler "(message sent completely)",
34439beb93cSSam Leffler (unsigned long) send_len);
34539beb93cSSam Leffler wpabuf_free(data->out_buf);
34639beb93cSSam Leffler data->out_buf = NULL;
34739beb93cSSam Leffler data->out_used = 0;
34839beb93cSSam Leffler if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
34939beb93cSSam Leffler data->out_op_code == WSC_NACK ||
35039beb93cSSam Leffler data->out_op_code == WSC_Done) {
35139beb93cSSam Leffler eap_wsc_state(data, FAIL);
35239beb93cSSam Leffler ret->methodState = METHOD_DONE;
35339beb93cSSam Leffler } else
35439beb93cSSam Leffler eap_wsc_state(data, MESG);
35539beb93cSSam Leffler } else {
35639beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
35739beb93cSSam Leffler "(%lu more to send)", (unsigned long) send_len,
35839beb93cSSam Leffler (unsigned long) wpabuf_len(data->out_buf) -
35939beb93cSSam Leffler data->out_used);
36039beb93cSSam Leffler eap_wsc_state(data, WAIT_FRAG_ACK);
36139beb93cSSam Leffler }
36239beb93cSSam Leffler
36339beb93cSSam Leffler return resp;
36439beb93cSSam Leffler }
36539beb93cSSam Leffler
36639beb93cSSam Leffler
eap_wsc_process_cont(struct eap_wsc_data * data,const u8 * buf,size_t len,u8 op_code)36739beb93cSSam Leffler static int eap_wsc_process_cont(struct eap_wsc_data *data,
36839beb93cSSam Leffler const u8 *buf, size_t len, u8 op_code)
36939beb93cSSam Leffler {
37039beb93cSSam Leffler /* Process continuation of a pending message */
37139beb93cSSam Leffler if (op_code != data->in_op_code) {
37239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
37339beb93cSSam Leffler "fragment (expected %d)",
37439beb93cSSam Leffler op_code, data->in_op_code);
37539beb93cSSam Leffler return -1;
37639beb93cSSam Leffler }
37739beb93cSSam Leffler
37839beb93cSSam Leffler if (len > wpabuf_tailroom(data->in_buf)) {
37939beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
38039beb93cSSam Leffler eap_wsc_state(data, FAIL);
38139beb93cSSam Leffler return -1;
38239beb93cSSam Leffler }
38339beb93cSSam Leffler
38439beb93cSSam Leffler wpabuf_put_data(data->in_buf, buf, len);
38539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
38639beb93cSSam Leffler "for %lu bytes more", (unsigned long) len,
38739beb93cSSam Leffler (unsigned long) wpabuf_tailroom(data->in_buf));
38839beb93cSSam Leffler
38939beb93cSSam Leffler return 0;
39039beb93cSSam Leffler }
39139beb93cSSam Leffler
39239beb93cSSam Leffler
eap_wsc_process_fragment(struct eap_wsc_data * data,struct eap_method_ret * ret,u8 id,u8 flags,u8 op_code,u16 message_length,const u8 * buf,size_t len)39339beb93cSSam Leffler static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
39439beb93cSSam Leffler struct eap_method_ret *ret,
39539beb93cSSam Leffler u8 id, u8 flags, u8 op_code,
39639beb93cSSam Leffler u16 message_length,
39739beb93cSSam Leffler const u8 *buf, size_t len)
39839beb93cSSam Leffler {
39939beb93cSSam Leffler /* Process a fragment that is not the last one of the message */
40039beb93cSSam Leffler if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
40139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
40239beb93cSSam Leffler "fragmented packet");
403*c1d255d3SCy Schubert ret->ignore = true;
40439beb93cSSam Leffler return NULL;
40539beb93cSSam Leffler }
40639beb93cSSam Leffler
40739beb93cSSam Leffler if (data->in_buf == NULL) {
40839beb93cSSam Leffler /* First fragment of the message */
40939beb93cSSam Leffler data->in_buf = wpabuf_alloc(message_length);
41039beb93cSSam Leffler if (data->in_buf == NULL) {
41139beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
41239beb93cSSam Leffler "message");
413*c1d255d3SCy Schubert ret->ignore = true;
41439beb93cSSam Leffler return NULL;
41539beb93cSSam Leffler }
41639beb93cSSam Leffler data->in_op_code = op_code;
41739beb93cSSam Leffler wpabuf_put_data(data->in_buf, buf, len);
41839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
41939beb93cSSam Leffler "fragment, waiting for %lu bytes more",
42039beb93cSSam Leffler (unsigned long) len,
42139beb93cSSam Leffler (unsigned long) wpabuf_tailroom(data->in_buf));
42239beb93cSSam Leffler }
42339beb93cSSam Leffler
42439beb93cSSam Leffler return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
42539beb93cSSam Leffler }
42639beb93cSSam Leffler
42739beb93cSSam Leffler
eap_wsc_process(struct eap_sm * sm,void * priv,struct eap_method_ret * ret,const struct wpabuf * reqData)42839beb93cSSam Leffler static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
42939beb93cSSam Leffler struct eap_method_ret *ret,
43039beb93cSSam Leffler const struct wpabuf *reqData)
43139beb93cSSam Leffler {
43239beb93cSSam Leffler struct eap_wsc_data *data = priv;
43339beb93cSSam Leffler const u8 *start, *pos, *end;
43439beb93cSSam Leffler size_t len;
43539beb93cSSam Leffler u8 op_code, flags, id;
43639beb93cSSam Leffler u16 message_length = 0;
43739beb93cSSam Leffler enum wps_process_res res;
43839beb93cSSam Leffler struct wpabuf tmpbuf;
439e28a4053SRui Paulo struct wpabuf *r;
44039beb93cSSam Leffler
44139beb93cSSam Leffler pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
44239beb93cSSam Leffler &len);
44339beb93cSSam Leffler if (pos == NULL || len < 2) {
444*c1d255d3SCy Schubert ret->ignore = true;
44539beb93cSSam Leffler return NULL;
44639beb93cSSam Leffler }
44739beb93cSSam Leffler
44839beb93cSSam Leffler id = eap_get_id(reqData);
44939beb93cSSam Leffler
45039beb93cSSam Leffler start = pos;
45139beb93cSSam Leffler end = start + len;
45239beb93cSSam Leffler
45339beb93cSSam Leffler op_code = *pos++;
45439beb93cSSam Leffler flags = *pos++;
45539beb93cSSam Leffler if (flags & WSC_FLAGS_LF) {
45639beb93cSSam Leffler if (end - pos < 2) {
45739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
458*c1d255d3SCy Schubert ret->ignore = true;
45939beb93cSSam Leffler return NULL;
46039beb93cSSam Leffler }
46139beb93cSSam Leffler message_length = WPA_GET_BE16(pos);
46239beb93cSSam Leffler pos += 2;
46339beb93cSSam Leffler
4645b9c547cSRui Paulo if (message_length < end - pos || message_length > 50000) {
46539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
46639beb93cSSam Leffler "Length");
467*c1d255d3SCy Schubert ret->ignore = true;
46839beb93cSSam Leffler return NULL;
46939beb93cSSam Leffler }
47039beb93cSSam Leffler }
47139beb93cSSam Leffler
47239beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
47339beb93cSSam Leffler "Flags 0x%x Message Length %d",
47439beb93cSSam Leffler op_code, flags, message_length);
47539beb93cSSam Leffler
47639beb93cSSam Leffler if (data->state == WAIT_FRAG_ACK) {
47739beb93cSSam Leffler if (op_code != WSC_FRAG_ACK) {
47839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
47939beb93cSSam Leffler "in WAIT_FRAG_ACK state", op_code);
480*c1d255d3SCy Schubert ret->ignore = true;
48139beb93cSSam Leffler return NULL;
48239beb93cSSam Leffler }
48339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
48439beb93cSSam Leffler eap_wsc_state(data, MESG);
48539beb93cSSam Leffler return eap_wsc_build_msg(data, ret, id);
48639beb93cSSam Leffler }
48739beb93cSSam Leffler
48839beb93cSSam Leffler if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
48939beb93cSSam Leffler op_code != WSC_Done && op_code != WSC_Start) {
49039beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
49139beb93cSSam Leffler op_code);
492*c1d255d3SCy Schubert ret->ignore = true;
49339beb93cSSam Leffler return NULL;
49439beb93cSSam Leffler }
49539beb93cSSam Leffler
49639beb93cSSam Leffler if (data->state == WAIT_START) {
49739beb93cSSam Leffler if (op_code != WSC_Start) {
49839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
49939beb93cSSam Leffler "in WAIT_START state", op_code);
500*c1d255d3SCy Schubert ret->ignore = true;
50139beb93cSSam Leffler return NULL;
50239beb93cSSam Leffler }
50339beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
50439beb93cSSam Leffler eap_wsc_state(data, MESG);
50539beb93cSSam Leffler /* Start message has empty payload, skip processing */
50639beb93cSSam Leffler goto send_msg;
50739beb93cSSam Leffler } else if (op_code == WSC_Start) {
50839beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
50939beb93cSSam Leffler op_code);
510*c1d255d3SCy Schubert ret->ignore = true;
51139beb93cSSam Leffler return NULL;
51239beb93cSSam Leffler }
51339beb93cSSam Leffler
51439beb93cSSam Leffler if (data->in_buf &&
51539beb93cSSam Leffler eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
516*c1d255d3SCy Schubert ret->ignore = true;
51739beb93cSSam Leffler return NULL;
51839beb93cSSam Leffler }
51939beb93cSSam Leffler
52039beb93cSSam Leffler if (flags & WSC_FLAGS_MF) {
52139beb93cSSam Leffler return eap_wsc_process_fragment(data, ret, id, flags, op_code,
52239beb93cSSam Leffler message_length, pos,
52339beb93cSSam Leffler end - pos);
52439beb93cSSam Leffler }
52539beb93cSSam Leffler
52639beb93cSSam Leffler if (data->in_buf == NULL) {
52739beb93cSSam Leffler /* Wrap unfragmented messages as wpabuf without extra copy */
52839beb93cSSam Leffler wpabuf_set(&tmpbuf, pos, end - pos);
52939beb93cSSam Leffler data->in_buf = &tmpbuf;
53039beb93cSSam Leffler }
53139beb93cSSam Leffler
53239beb93cSSam Leffler res = wps_process_msg(data->wps, op_code, data->in_buf);
53339beb93cSSam Leffler switch (res) {
53439beb93cSSam Leffler case WPS_DONE:
53539beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
53639beb93cSSam Leffler "successfully - wait for EAP failure");
53739beb93cSSam Leffler eap_wsc_state(data, FAIL);
53839beb93cSSam Leffler break;
53939beb93cSSam Leffler case WPS_CONTINUE:
54039beb93cSSam Leffler eap_wsc_state(data, MESG);
54139beb93cSSam Leffler break;
54239beb93cSSam Leffler case WPS_FAILURE:
54339beb93cSSam Leffler case WPS_PENDING:
54439beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
54539beb93cSSam Leffler eap_wsc_state(data, FAIL);
54639beb93cSSam Leffler break;
54739beb93cSSam Leffler }
54839beb93cSSam Leffler
54939beb93cSSam Leffler if (data->in_buf != &tmpbuf)
55039beb93cSSam Leffler wpabuf_free(data->in_buf);
55139beb93cSSam Leffler data->in_buf = NULL;
55239beb93cSSam Leffler
55339beb93cSSam Leffler send_msg:
55439beb93cSSam Leffler if (data->out_buf == NULL) {
55539beb93cSSam Leffler data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
55639beb93cSSam Leffler if (data->out_buf == NULL) {
55739beb93cSSam Leffler wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
55839beb93cSSam Leffler "message from WPS");
559325151a3SRui Paulo eap_wsc_state(data, FAIL);
560325151a3SRui Paulo ret->methodState = METHOD_DONE;
561325151a3SRui Paulo ret->decision = DECISION_FAIL;
56239beb93cSSam Leffler return NULL;
56339beb93cSSam Leffler }
56439beb93cSSam Leffler data->out_used = 0;
56539beb93cSSam Leffler }
56639beb93cSSam Leffler
56739beb93cSSam Leffler eap_wsc_state(data, MESG);
568e28a4053SRui Paulo r = eap_wsc_build_msg(data, ret, id);
569e28a4053SRui Paulo if (data->state == FAIL && ret->methodState == METHOD_DONE) {
570e28a4053SRui Paulo /* Use reduced client timeout for WPS to avoid long wait */
571e28a4053SRui Paulo if (sm->ClientTimeout > 2)
572e28a4053SRui Paulo sm->ClientTimeout = 2;
573e28a4053SRui Paulo }
574e28a4053SRui Paulo return r;
57539beb93cSSam Leffler }
57639beb93cSSam Leffler
57739beb93cSSam Leffler
eap_peer_wsc_register(void)57839beb93cSSam Leffler int eap_peer_wsc_register(void)
57939beb93cSSam Leffler {
58039beb93cSSam Leffler struct eap_method *eap;
58139beb93cSSam Leffler
58239beb93cSSam Leffler eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
58339beb93cSSam Leffler EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
58439beb93cSSam Leffler "WSC");
58539beb93cSSam Leffler if (eap == NULL)
58639beb93cSSam Leffler return -1;
58739beb93cSSam Leffler
58839beb93cSSam Leffler eap->init = eap_wsc_init;
58939beb93cSSam Leffler eap->deinit = eap_wsc_deinit;
59039beb93cSSam Leffler eap->process = eap_wsc_process;
59139beb93cSSam Leffler
592780fb4a2SCy Schubert return eap_peer_method_register(eap);
59339beb93cSSam Leffler }
594