1189251Ssam/* 2189251Ssam * EAP-WSC peer for Wi-Fi Protected Setup 3214734Srpaulo * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> 4189251Ssam * 5189251Ssam * This program is free software; you can redistribute it and/or modify 6189251Ssam * it under the terms of the GNU General Public License version 2 as 7189251Ssam * published by the Free Software Foundation. 8189251Ssam * 9189251Ssam * Alternatively, this software may be distributed under the terms of BSD 10189251Ssam * license. 11189251Ssam * 12189251Ssam * See README and COPYING for more details. 13189251Ssam */ 14189251Ssam 15189251Ssam#include "includes.h" 16189251Ssam 17189251Ssam#include "common.h" 18189251Ssam#include "uuid.h" 19189251Ssam#include "eap_i.h" 20189251Ssam#include "eap_common/eap_wsc_common.h" 21189251Ssam#include "wps/wps.h" 22189251Ssam#include "wps/wps_defs.h" 23189251Ssam 24189251Ssam 25189251Ssamstruct eap_wsc_data { 26189251Ssam enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; 27189251Ssam int registrar; 28189251Ssam struct wpabuf *in_buf; 29189251Ssam struct wpabuf *out_buf; 30189251Ssam enum wsc_op_code in_op_code, out_op_code; 31189251Ssam size_t out_used; 32189251Ssam size_t fragment_size; 33189251Ssam struct wps_data *wps; 34189251Ssam struct wps_context *wps_ctx; 35189251Ssam}; 36189251Ssam 37189251Ssam 38189251Ssamstatic const char * eap_wsc_state_txt(int state) 39189251Ssam{ 40189251Ssam switch (state) { 41189251Ssam case WAIT_START: 42189251Ssam return "WAIT_START"; 43189251Ssam case MESG: 44189251Ssam return "MESG"; 45189251Ssam case FRAG_ACK: 46189251Ssam return "FRAG_ACK"; 47189251Ssam case WAIT_FRAG_ACK: 48189251Ssam return "WAIT_FRAG_ACK"; 49189251Ssam case DONE: 50189251Ssam return "DONE"; 51189251Ssam case FAIL: 52189251Ssam return "FAIL"; 53189251Ssam default: 54189251Ssam return "?"; 55189251Ssam } 56189251Ssam} 57189251Ssam 58189251Ssam 59189251Ssamstatic void eap_wsc_state(struct eap_wsc_data *data, int state) 60189251Ssam{ 61189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", 62189251Ssam eap_wsc_state_txt(data->state), 63189251Ssam eap_wsc_state_txt(state)); 64189251Ssam data->state = state; 65189251Ssam} 66189251Ssam 67189251Ssam 68214734Srpaulostatic int eap_wsc_new_ap_settings(struct wps_credential *cred, 69214734Srpaulo const char *params) 70214734Srpaulo{ 71214734Srpaulo const char *pos, *end; 72214734Srpaulo size_t len; 73214734Srpaulo 74214734Srpaulo os_memset(cred, 0, sizeof(*cred)); 75214734Srpaulo 76214734Srpaulo pos = os_strstr(params, "new_ssid="); 77214734Srpaulo if (pos == NULL) 78214734Srpaulo return 0; 79214734Srpaulo pos += 9; 80214734Srpaulo end = os_strchr(pos, ' '); 81214734Srpaulo if (end == NULL) 82214734Srpaulo len = os_strlen(pos); 83214734Srpaulo else 84214734Srpaulo len = end - pos; 85214734Srpaulo if ((len & 1) || len > 2 * sizeof(cred->ssid) || 86214734Srpaulo hexstr2bin(pos, cred->ssid, len / 2)) 87214734Srpaulo return -1; 88214734Srpaulo cred->ssid_len = len / 2; 89214734Srpaulo 90214734Srpaulo pos = os_strstr(params, "new_auth="); 91214734Srpaulo if (pos == NULL) 92214734Srpaulo return -1; 93214734Srpaulo if (os_strncmp(pos + 9, "OPEN", 4) == 0) 94214734Srpaulo cred->auth_type = WPS_AUTH_OPEN; 95214734Srpaulo else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0) 96214734Srpaulo cred->auth_type = WPS_AUTH_WPAPSK; 97214734Srpaulo else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0) 98214734Srpaulo cred->auth_type = WPS_AUTH_WPA2PSK; 99214734Srpaulo else 100214734Srpaulo return -1; 101214734Srpaulo 102214734Srpaulo pos = os_strstr(params, "new_encr="); 103214734Srpaulo if (pos == NULL) 104214734Srpaulo return -1; 105214734Srpaulo if (os_strncmp(pos + 9, "NONE", 4) == 0) 106214734Srpaulo cred->encr_type = WPS_ENCR_NONE; 107214734Srpaulo else if (os_strncmp(pos + 9, "WEP", 3) == 0) 108214734Srpaulo cred->encr_type = WPS_ENCR_WEP; 109214734Srpaulo else if (os_strncmp(pos + 9, "TKIP", 4) == 0) 110214734Srpaulo cred->encr_type = WPS_ENCR_TKIP; 111214734Srpaulo else if (os_strncmp(pos + 9, "CCMP", 4) == 0) 112214734Srpaulo cred->encr_type = WPS_ENCR_AES; 113214734Srpaulo else 114214734Srpaulo return -1; 115214734Srpaulo 116214734Srpaulo pos = os_strstr(params, "new_key="); 117214734Srpaulo if (pos == NULL) 118214734Srpaulo return 0; 119214734Srpaulo pos += 8; 120214734Srpaulo end = os_strchr(pos, ' '); 121214734Srpaulo if (end == NULL) 122214734Srpaulo len = os_strlen(pos); 123214734Srpaulo else 124214734Srpaulo len = end - pos; 125214734Srpaulo if ((len & 1) || len > 2 * sizeof(cred->key) || 126214734Srpaulo hexstr2bin(pos, cred->key, len / 2)) 127214734Srpaulo return -1; 128214734Srpaulo cred->key_len = len / 2; 129214734Srpaulo 130214734Srpaulo return 1; 131214734Srpaulo} 132214734Srpaulo 133214734Srpaulo 134189251Ssamstatic void * eap_wsc_init(struct eap_sm *sm) 135189251Ssam{ 136189251Ssam struct eap_wsc_data *data; 137189251Ssam const u8 *identity; 138189251Ssam size_t identity_len; 139189251Ssam int registrar; 140189251Ssam struct wps_config cfg; 141189251Ssam const char *pos; 142189251Ssam const char *phase1; 143189251Ssam struct wps_context *wps; 144214734Srpaulo struct wps_credential new_ap_settings; 145214734Srpaulo int res; 146189251Ssam 147189251Ssam wps = sm->wps; 148189251Ssam if (wps == NULL) { 149189251Ssam wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available"); 150189251Ssam return NULL; 151189251Ssam } 152189251Ssam 153189251Ssam identity = eap_get_config_identity(sm, &identity_len); 154189251Ssam 155189251Ssam if (identity && identity_len == WSC_ID_REGISTRAR_LEN && 156189251Ssam os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) 157189251Ssam registrar = 1; /* Supplicant is Registrar */ 158189251Ssam else if (identity && identity_len == WSC_ID_ENROLLEE_LEN && 159189251Ssam os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) 160189251Ssam registrar = 0; /* Supplicant is Enrollee */ 161189251Ssam else { 162189251Ssam wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", 163189251Ssam identity, identity_len); 164189251Ssam return NULL; 165189251Ssam } 166189251Ssam 167189251Ssam data = os_zalloc(sizeof(*data)); 168189251Ssam if (data == NULL) 169189251Ssam return NULL; 170189251Ssam data->state = registrar ? MESG : WAIT_START; 171189251Ssam data->registrar = registrar; 172189251Ssam data->wps_ctx = wps; 173189251Ssam 174189251Ssam os_memset(&cfg, 0, sizeof(cfg)); 175189251Ssam cfg.wps = wps; 176189251Ssam cfg.registrar = registrar; 177189251Ssam 178189251Ssam phase1 = eap_get_config_phase1(sm); 179189251Ssam if (phase1 == NULL) { 180189251Ssam wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not " 181189251Ssam "set"); 182189251Ssam os_free(data); 183189251Ssam return NULL; 184189251Ssam } 185189251Ssam 186189251Ssam pos = os_strstr(phase1, "pin="); 187189251Ssam if (pos) { 188189251Ssam pos += 4; 189189251Ssam cfg.pin = (const u8 *) pos; 190189251Ssam while (*pos != '\0' && *pos != ' ') 191189251Ssam pos++; 192189251Ssam cfg.pin_len = pos - (const char *) cfg.pin; 193189251Ssam } else { 194189251Ssam pos = os_strstr(phase1, "pbc=1"); 195189251Ssam if (pos) 196189251Ssam cfg.pbc = 1; 197189251Ssam } 198189251Ssam 199189251Ssam if (cfg.pin == NULL && !cfg.pbc) { 200189251Ssam wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 " 201189251Ssam "configuration data"); 202189251Ssam os_free(data); 203189251Ssam return NULL; 204189251Ssam } 205189251Ssam 206214734Srpaulo res = eap_wsc_new_ap_settings(&new_ap_settings, phase1); 207214734Srpaulo if (res < 0) { 208214734Srpaulo os_free(data); 209214734Srpaulo return NULL; 210214734Srpaulo } 211214734Srpaulo if (res == 1) { 212214734Srpaulo wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for " 213214734Srpaulo "WPS"); 214214734Srpaulo cfg.new_ap_settings = &new_ap_settings; 215214734Srpaulo } 216214734Srpaulo 217189251Ssam data->wps = wps_init(&cfg); 218189251Ssam if (data->wps == NULL) { 219189251Ssam os_free(data); 220189251Ssam return NULL; 221189251Ssam } 222189251Ssam data->fragment_size = WSC_FRAGMENT_SIZE; 223189251Ssam 224189251Ssam if (registrar && cfg.pin) { 225189251Ssam wps_registrar_add_pin(data->wps_ctx->registrar, NULL, 226209158Srpaulo cfg.pin, cfg.pin_len, 0); 227189251Ssam } 228189251Ssam 229214734Srpaulo /* Use reduced client timeout for WPS to avoid long wait */ 230214734Srpaulo if (sm->ClientTimeout > 30) 231214734Srpaulo sm->ClientTimeout = 30; 232214734Srpaulo 233189251Ssam return data; 234189251Ssam} 235189251Ssam 236189251Ssam 237189251Ssamstatic void eap_wsc_deinit(struct eap_sm *sm, void *priv) 238189251Ssam{ 239189251Ssam struct eap_wsc_data *data = priv; 240189251Ssam wpabuf_free(data->in_buf); 241189251Ssam wpabuf_free(data->out_buf); 242189251Ssam wps_deinit(data->wps); 243189251Ssam os_free(data->wps_ctx->network_key); 244189251Ssam data->wps_ctx->network_key = NULL; 245189251Ssam os_free(data); 246189251Ssam} 247189251Ssam 248189251Ssam 249189251Ssamstatic struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, 250189251Ssam struct eap_method_ret *ret, u8 id) 251189251Ssam{ 252189251Ssam struct wpabuf *resp; 253189251Ssam u8 flags; 254189251Ssam size_t send_len, plen; 255189251Ssam 256189251Ssam ret->ignore = FALSE; 257189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response"); 258189251Ssam ret->allowNotifications = TRUE; 259189251Ssam 260189251Ssam flags = 0; 261189251Ssam send_len = wpabuf_len(data->out_buf) - data->out_used; 262189251Ssam if (2 + send_len > data->fragment_size) { 263189251Ssam send_len = data->fragment_size - 2; 264189251Ssam flags |= WSC_FLAGS_MF; 265189251Ssam if (data->out_used == 0) { 266189251Ssam flags |= WSC_FLAGS_LF; 267189251Ssam send_len -= 2; 268189251Ssam } 269189251Ssam } 270189251Ssam plen = 2 + send_len; 271189251Ssam if (flags & WSC_FLAGS_LF) 272189251Ssam plen += 2; 273189251Ssam resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, 274189251Ssam EAP_CODE_RESPONSE, id); 275189251Ssam if (resp == NULL) 276189251Ssam return NULL; 277189251Ssam 278189251Ssam wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */ 279189251Ssam wpabuf_put_u8(resp, flags); /* Flags */ 280189251Ssam if (flags & WSC_FLAGS_LF) 281189251Ssam wpabuf_put_be16(resp, wpabuf_len(data->out_buf)); 282189251Ssam 283189251Ssam wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, 284189251Ssam send_len); 285189251Ssam data->out_used += send_len; 286189251Ssam 287189251Ssam ret->methodState = METHOD_MAY_CONT; 288189251Ssam ret->decision = DECISION_FAIL; 289189251Ssam 290189251Ssam if (data->out_used == wpabuf_len(data->out_buf)) { 291189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " 292189251Ssam "(message sent completely)", 293189251Ssam (unsigned long) send_len); 294189251Ssam wpabuf_free(data->out_buf); 295189251Ssam data->out_buf = NULL; 296189251Ssam data->out_used = 0; 297189251Ssam if ((data->state == FAIL && data->out_op_code == WSC_ACK) || 298189251Ssam data->out_op_code == WSC_NACK || 299189251Ssam data->out_op_code == WSC_Done) { 300189251Ssam eap_wsc_state(data, FAIL); 301189251Ssam ret->methodState = METHOD_DONE; 302189251Ssam } else 303189251Ssam eap_wsc_state(data, MESG); 304189251Ssam } else { 305189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " 306189251Ssam "(%lu more to send)", (unsigned long) send_len, 307189251Ssam (unsigned long) wpabuf_len(data->out_buf) - 308189251Ssam data->out_used); 309189251Ssam eap_wsc_state(data, WAIT_FRAG_ACK); 310189251Ssam } 311189251Ssam 312189251Ssam return resp; 313189251Ssam} 314189251Ssam 315189251Ssam 316189251Ssamstatic int eap_wsc_process_cont(struct eap_wsc_data *data, 317189251Ssam const u8 *buf, size_t len, u8 op_code) 318189251Ssam{ 319189251Ssam /* Process continuation of a pending message */ 320189251Ssam if (op_code != data->in_op_code) { 321189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " 322189251Ssam "fragment (expected %d)", 323189251Ssam op_code, data->in_op_code); 324189251Ssam return -1; 325189251Ssam } 326189251Ssam 327189251Ssam if (len > wpabuf_tailroom(data->in_buf)) { 328189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); 329189251Ssam eap_wsc_state(data, FAIL); 330189251Ssam return -1; 331189251Ssam } 332189251Ssam 333189251Ssam wpabuf_put_data(data->in_buf, buf, len); 334189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting " 335189251Ssam "for %lu bytes more", (unsigned long) len, 336189251Ssam (unsigned long) wpabuf_tailroom(data->in_buf)); 337189251Ssam 338189251Ssam return 0; 339189251Ssam} 340189251Ssam 341189251Ssam 342189251Ssamstatic struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data, 343189251Ssam struct eap_method_ret *ret, 344189251Ssam u8 id, u8 flags, u8 op_code, 345189251Ssam u16 message_length, 346189251Ssam const u8 *buf, size_t len) 347189251Ssam{ 348189251Ssam /* Process a fragment that is not the last one of the message */ 349189251Ssam if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { 350189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a " 351189251Ssam "fragmented packet"); 352189251Ssam ret->ignore = TRUE; 353189251Ssam return NULL; 354189251Ssam } 355189251Ssam 356189251Ssam if (data->in_buf == NULL) { 357189251Ssam /* First fragment of the message */ 358189251Ssam data->in_buf = wpabuf_alloc(message_length); 359189251Ssam if (data->in_buf == NULL) { 360189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " 361189251Ssam "message"); 362189251Ssam ret->ignore = TRUE; 363189251Ssam return NULL; 364189251Ssam } 365189251Ssam data->in_op_code = op_code; 366189251Ssam wpabuf_put_data(data->in_buf, buf, len); 367189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first " 368189251Ssam "fragment, waiting for %lu bytes more", 369189251Ssam (unsigned long) len, 370189251Ssam (unsigned long) wpabuf_tailroom(data->in_buf)); 371189251Ssam } 372189251Ssam 373189251Ssam return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE); 374189251Ssam} 375189251Ssam 376189251Ssam 377189251Ssamstatic struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv, 378189251Ssam struct eap_method_ret *ret, 379189251Ssam const struct wpabuf *reqData) 380189251Ssam{ 381189251Ssam struct eap_wsc_data *data = priv; 382189251Ssam const u8 *start, *pos, *end; 383189251Ssam size_t len; 384189251Ssam u8 op_code, flags, id; 385189251Ssam u16 message_length = 0; 386189251Ssam enum wps_process_res res; 387189251Ssam struct wpabuf tmpbuf; 388214734Srpaulo struct wpabuf *r; 389189251Ssam 390189251Ssam pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData, 391189251Ssam &len); 392189251Ssam if (pos == NULL || len < 2) { 393189251Ssam ret->ignore = TRUE; 394189251Ssam return NULL; 395189251Ssam } 396189251Ssam 397189251Ssam id = eap_get_id(reqData); 398189251Ssam 399189251Ssam start = pos; 400189251Ssam end = start + len; 401189251Ssam 402189251Ssam op_code = *pos++; 403189251Ssam flags = *pos++; 404189251Ssam if (flags & WSC_FLAGS_LF) { 405189251Ssam if (end - pos < 2) { 406189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); 407189251Ssam ret->ignore = TRUE; 408189251Ssam return NULL; 409189251Ssam } 410189251Ssam message_length = WPA_GET_BE16(pos); 411189251Ssam pos += 2; 412189251Ssam 413189251Ssam if (message_length < end - pos) { 414189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " 415189251Ssam "Length"); 416189251Ssam ret->ignore = TRUE; 417189251Ssam return NULL; 418189251Ssam } 419189251Ssam } 420189251Ssam 421189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " 422189251Ssam "Flags 0x%x Message Length %d", 423189251Ssam op_code, flags, message_length); 424189251Ssam 425189251Ssam if (data->state == WAIT_FRAG_ACK) { 426189251Ssam if (op_code != WSC_FRAG_ACK) { 427189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " 428189251Ssam "in WAIT_FRAG_ACK state", op_code); 429189251Ssam ret->ignore = TRUE; 430189251Ssam return NULL; 431189251Ssam } 432189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); 433189251Ssam eap_wsc_state(data, MESG); 434189251Ssam return eap_wsc_build_msg(data, ret, id); 435189251Ssam } 436189251Ssam 437189251Ssam if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && 438189251Ssam op_code != WSC_Done && op_code != WSC_Start) { 439189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", 440189251Ssam op_code); 441189251Ssam ret->ignore = TRUE; 442189251Ssam return NULL; 443189251Ssam } 444189251Ssam 445189251Ssam if (data->state == WAIT_START) { 446189251Ssam if (op_code != WSC_Start) { 447189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " 448189251Ssam "in WAIT_START state", op_code); 449189251Ssam ret->ignore = TRUE; 450189251Ssam return NULL; 451189251Ssam } 452189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Received start"); 453189251Ssam eap_wsc_state(data, MESG); 454189251Ssam /* Start message has empty payload, skip processing */ 455189251Ssam goto send_msg; 456189251Ssam } else if (op_code == WSC_Start) { 457189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", 458189251Ssam op_code); 459189251Ssam ret->ignore = TRUE; 460189251Ssam return NULL; 461189251Ssam } 462189251Ssam 463189251Ssam if (data->in_buf && 464189251Ssam eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { 465189251Ssam ret->ignore = TRUE; 466189251Ssam return NULL; 467189251Ssam } 468189251Ssam 469189251Ssam if (flags & WSC_FLAGS_MF) { 470189251Ssam return eap_wsc_process_fragment(data, ret, id, flags, op_code, 471189251Ssam message_length, pos, 472189251Ssam end - pos); 473189251Ssam } 474189251Ssam 475189251Ssam if (data->in_buf == NULL) { 476189251Ssam /* Wrap unfragmented messages as wpabuf without extra copy */ 477189251Ssam wpabuf_set(&tmpbuf, pos, end - pos); 478189251Ssam data->in_buf = &tmpbuf; 479189251Ssam } 480189251Ssam 481189251Ssam res = wps_process_msg(data->wps, op_code, data->in_buf); 482189251Ssam switch (res) { 483189251Ssam case WPS_DONE: 484189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " 485189251Ssam "successfully - wait for EAP failure"); 486189251Ssam eap_wsc_state(data, FAIL); 487189251Ssam break; 488189251Ssam case WPS_CONTINUE: 489189251Ssam eap_wsc_state(data, MESG); 490189251Ssam break; 491189251Ssam case WPS_FAILURE: 492189251Ssam case WPS_PENDING: 493189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); 494189251Ssam eap_wsc_state(data, FAIL); 495189251Ssam break; 496189251Ssam } 497189251Ssam 498189251Ssam if (data->in_buf != &tmpbuf) 499189251Ssam wpabuf_free(data->in_buf); 500189251Ssam data->in_buf = NULL; 501189251Ssam 502189251Ssamsend_msg: 503189251Ssam if (data->out_buf == NULL) { 504189251Ssam data->out_buf = wps_get_msg(data->wps, &data->out_op_code); 505189251Ssam if (data->out_buf == NULL) { 506189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive " 507189251Ssam "message from WPS"); 508189251Ssam return NULL; 509189251Ssam } 510189251Ssam data->out_used = 0; 511189251Ssam } 512189251Ssam 513189251Ssam eap_wsc_state(data, MESG); 514214734Srpaulo r = eap_wsc_build_msg(data, ret, id); 515214734Srpaulo if (data->state == FAIL && ret->methodState == METHOD_DONE) { 516214734Srpaulo /* Use reduced client timeout for WPS to avoid long wait */ 517214734Srpaulo if (sm->ClientTimeout > 2) 518214734Srpaulo sm->ClientTimeout = 2; 519214734Srpaulo } 520214734Srpaulo return r; 521189251Ssam} 522189251Ssam 523189251Ssam 524189251Ssamint eap_peer_wsc_register(void) 525189251Ssam{ 526189251Ssam struct eap_method *eap; 527189251Ssam int ret; 528189251Ssam 529189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 530189251Ssam EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 531189251Ssam "WSC"); 532189251Ssam if (eap == NULL) 533189251Ssam return -1; 534189251Ssam 535189251Ssam eap->init = eap_wsc_init; 536189251Ssam eap->deinit = eap_wsc_deinit; 537189251Ssam eap->process = eap_wsc_process; 538189251Ssam 539189251Ssam ret = eap_peer_method_register(eap); 540189251Ssam if (ret) 541189251Ssam eap_peer_method_free(eap); 542189251Ssam return ret; 543189251Ssam} 544