eap_wsc.c revision 252726
1189251Ssam/* 2189251Ssam * EAP-WSC peer for Wi-Fi Protected Setup 3252726Srpaulo * Copyright (c) 2007-2009, 2012, Jouni Malinen <j@w1.fi> 4189251Ssam * 5252726Srpaulo * This software may be distributed under the terms of the BSD license. 6252726Srpaulo * See README for more details. 7189251Ssam */ 8189251Ssam 9189251Ssam#include "includes.h" 10189251Ssam 11189251Ssam#include "common.h" 12189251Ssam#include "uuid.h" 13189251Ssam#include "eap_i.h" 14189251Ssam#include "eap_common/eap_wsc_common.h" 15189251Ssam#include "wps/wps.h" 16189251Ssam#include "wps/wps_defs.h" 17189251Ssam 18189251Ssam 19189251Ssamstruct eap_wsc_data { 20189251Ssam enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; 21189251Ssam int registrar; 22189251Ssam struct wpabuf *in_buf; 23189251Ssam struct wpabuf *out_buf; 24189251Ssam enum wsc_op_code in_op_code, out_op_code; 25189251Ssam size_t out_used; 26189251Ssam size_t fragment_size; 27189251Ssam struct wps_data *wps; 28189251Ssam struct wps_context *wps_ctx; 29189251Ssam}; 30189251Ssam 31189251Ssam 32189251Ssamstatic const char * eap_wsc_state_txt(int state) 33189251Ssam{ 34189251Ssam switch (state) { 35189251Ssam case WAIT_START: 36189251Ssam return "WAIT_START"; 37189251Ssam case MESG: 38189251Ssam return "MESG"; 39189251Ssam case FRAG_ACK: 40189251Ssam return "FRAG_ACK"; 41189251Ssam case WAIT_FRAG_ACK: 42189251Ssam return "WAIT_FRAG_ACK"; 43189251Ssam case DONE: 44189251Ssam return "DONE"; 45189251Ssam case FAIL: 46189251Ssam return "FAIL"; 47189251Ssam default: 48189251Ssam return "?"; 49189251Ssam } 50189251Ssam} 51189251Ssam 52189251Ssam 53189251Ssamstatic void eap_wsc_state(struct eap_wsc_data *data, int state) 54189251Ssam{ 55189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", 56189251Ssam eap_wsc_state_txt(data->state), 57189251Ssam eap_wsc_state_txt(state)); 58189251Ssam data->state = state; 59189251Ssam} 60189251Ssam 61189251Ssam 62214734Srpaulostatic int eap_wsc_new_ap_settings(struct wps_credential *cred, 63214734Srpaulo const char *params) 64214734Srpaulo{ 65214734Srpaulo const char *pos, *end; 66214734Srpaulo size_t len; 67214734Srpaulo 68214734Srpaulo os_memset(cred, 0, sizeof(*cred)); 69214734Srpaulo 70214734Srpaulo pos = os_strstr(params, "new_ssid="); 71214734Srpaulo if (pos == NULL) 72214734Srpaulo return 0; 73214734Srpaulo pos += 9; 74214734Srpaulo end = os_strchr(pos, ' '); 75214734Srpaulo if (end == NULL) 76214734Srpaulo len = os_strlen(pos); 77214734Srpaulo else 78214734Srpaulo len = end - pos; 79214734Srpaulo if ((len & 1) || len > 2 * sizeof(cred->ssid) || 80214734Srpaulo hexstr2bin(pos, cred->ssid, len / 2)) 81214734Srpaulo return -1; 82214734Srpaulo cred->ssid_len = len / 2; 83214734Srpaulo 84214734Srpaulo pos = os_strstr(params, "new_auth="); 85214734Srpaulo if (pos == NULL) 86214734Srpaulo return -1; 87214734Srpaulo if (os_strncmp(pos + 9, "OPEN", 4) == 0) 88214734Srpaulo cred->auth_type = WPS_AUTH_OPEN; 89214734Srpaulo else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0) 90214734Srpaulo cred->auth_type = WPS_AUTH_WPAPSK; 91214734Srpaulo else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0) 92214734Srpaulo cred->auth_type = WPS_AUTH_WPA2PSK; 93214734Srpaulo else 94214734Srpaulo return -1; 95214734Srpaulo 96214734Srpaulo pos = os_strstr(params, "new_encr="); 97214734Srpaulo if (pos == NULL) 98214734Srpaulo return -1; 99214734Srpaulo if (os_strncmp(pos + 9, "NONE", 4) == 0) 100214734Srpaulo cred->encr_type = WPS_ENCR_NONE; 101214734Srpaulo else if (os_strncmp(pos + 9, "WEP", 3) == 0) 102214734Srpaulo cred->encr_type = WPS_ENCR_WEP; 103214734Srpaulo else if (os_strncmp(pos + 9, "TKIP", 4) == 0) 104214734Srpaulo cred->encr_type = WPS_ENCR_TKIP; 105214734Srpaulo else if (os_strncmp(pos + 9, "CCMP", 4) == 0) 106214734Srpaulo cred->encr_type = WPS_ENCR_AES; 107214734Srpaulo else 108214734Srpaulo return -1; 109214734Srpaulo 110214734Srpaulo pos = os_strstr(params, "new_key="); 111214734Srpaulo if (pos == NULL) 112214734Srpaulo return 0; 113214734Srpaulo pos += 8; 114214734Srpaulo end = os_strchr(pos, ' '); 115214734Srpaulo if (end == NULL) 116214734Srpaulo len = os_strlen(pos); 117214734Srpaulo else 118214734Srpaulo len = end - pos; 119214734Srpaulo if ((len & 1) || len > 2 * sizeof(cred->key) || 120214734Srpaulo hexstr2bin(pos, cred->key, len / 2)) 121214734Srpaulo return -1; 122214734Srpaulo cred->key_len = len / 2; 123214734Srpaulo 124214734Srpaulo return 1; 125214734Srpaulo} 126214734Srpaulo 127214734Srpaulo 128189251Ssamstatic void * eap_wsc_init(struct eap_sm *sm) 129189251Ssam{ 130189251Ssam struct eap_wsc_data *data; 131189251Ssam const u8 *identity; 132189251Ssam size_t identity_len; 133189251Ssam int registrar; 134189251Ssam struct wps_config cfg; 135189251Ssam const char *pos; 136189251Ssam const char *phase1; 137189251Ssam struct wps_context *wps; 138214734Srpaulo struct wps_credential new_ap_settings; 139214734Srpaulo int res; 140252726Srpaulo u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN]; 141252726Srpaulo int nfc = 0; 142189251Ssam 143189251Ssam wps = sm->wps; 144189251Ssam if (wps == NULL) { 145189251Ssam wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available"); 146189251Ssam return NULL; 147189251Ssam } 148189251Ssam 149189251Ssam identity = eap_get_config_identity(sm, &identity_len); 150189251Ssam 151189251Ssam if (identity && identity_len == WSC_ID_REGISTRAR_LEN && 152189251Ssam os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) 153189251Ssam registrar = 1; /* Supplicant is Registrar */ 154189251Ssam else if (identity && identity_len == WSC_ID_ENROLLEE_LEN && 155189251Ssam os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) 156189251Ssam registrar = 0; /* Supplicant is Enrollee */ 157189251Ssam else { 158189251Ssam wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", 159189251Ssam identity, identity_len); 160189251Ssam return NULL; 161189251Ssam } 162189251Ssam 163189251Ssam data = os_zalloc(sizeof(*data)); 164189251Ssam if (data == NULL) 165189251Ssam return NULL; 166189251Ssam data->state = registrar ? MESG : WAIT_START; 167189251Ssam data->registrar = registrar; 168189251Ssam data->wps_ctx = wps; 169189251Ssam 170189251Ssam os_memset(&cfg, 0, sizeof(cfg)); 171189251Ssam cfg.wps = wps; 172189251Ssam cfg.registrar = registrar; 173189251Ssam 174189251Ssam phase1 = eap_get_config_phase1(sm); 175189251Ssam if (phase1 == NULL) { 176189251Ssam wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not " 177189251Ssam "set"); 178189251Ssam os_free(data); 179189251Ssam return NULL; 180189251Ssam } 181189251Ssam 182189251Ssam pos = os_strstr(phase1, "pin="); 183189251Ssam if (pos) { 184189251Ssam pos += 4; 185189251Ssam cfg.pin = (const u8 *) pos; 186189251Ssam while (*pos != '\0' && *pos != ' ') 187189251Ssam pos++; 188189251Ssam cfg.pin_len = pos - (const char *) cfg.pin; 189252726Srpaulo if (cfg.pin_len >= WPS_OOB_DEVICE_PASSWORD_MIN_LEN * 2 && 190252726Srpaulo cfg.pin_len <= WPS_OOB_DEVICE_PASSWORD_LEN * 2 && 191252726Srpaulo hexstr2bin((const char *) cfg.pin, dev_pw, 192252726Srpaulo cfg.pin_len / 2) == 0) { 193252726Srpaulo /* Convert OOB Device Password to binary */ 194252726Srpaulo cfg.pin = dev_pw; 195252726Srpaulo cfg.pin_len /= 2; 196252726Srpaulo } 197252726Srpaulo if (cfg.pin_len == 6 && os_strncmp(pos, "nfc-pw", 6) == 0) { 198252726Srpaulo cfg.pin = NULL; 199252726Srpaulo cfg.pin_len = 0; 200252726Srpaulo nfc = 1; 201252726Srpaulo } 202189251Ssam } else { 203189251Ssam pos = os_strstr(phase1, "pbc=1"); 204189251Ssam if (pos) 205189251Ssam cfg.pbc = 1; 206189251Ssam } 207189251Ssam 208252726Srpaulo if (cfg.pin == NULL && !cfg.pbc && !nfc) { 209189251Ssam wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 " 210189251Ssam "configuration data"); 211189251Ssam os_free(data); 212189251Ssam return NULL; 213189251Ssam } 214189251Ssam 215252726Srpaulo pos = os_strstr(phase1, "dev_pw_id="); 216252726Srpaulo if (pos && cfg.pin) 217252726Srpaulo cfg.dev_pw_id = atoi(pos + 10); 218252726Srpaulo 219214734Srpaulo res = eap_wsc_new_ap_settings(&new_ap_settings, phase1); 220214734Srpaulo if (res < 0) { 221214734Srpaulo os_free(data); 222214734Srpaulo return NULL; 223214734Srpaulo } 224214734Srpaulo if (res == 1) { 225214734Srpaulo wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for " 226214734Srpaulo "WPS"); 227214734Srpaulo cfg.new_ap_settings = &new_ap_settings; 228214734Srpaulo } 229214734Srpaulo 230189251Ssam data->wps = wps_init(&cfg); 231189251Ssam if (data->wps == NULL) { 232189251Ssam os_free(data); 233189251Ssam return NULL; 234189251Ssam } 235252726Srpaulo res = eap_get_config_fragment_size(sm); 236252726Srpaulo if (res > 0) 237252726Srpaulo data->fragment_size = res; 238252726Srpaulo else 239252726Srpaulo data->fragment_size = WSC_FRAGMENT_SIZE; 240252726Srpaulo wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u", 241252726Srpaulo (unsigned int) data->fragment_size); 242189251Ssam 243189251Ssam if (registrar && cfg.pin) { 244252726Srpaulo wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL, 245209158Srpaulo cfg.pin, cfg.pin_len, 0); 246189251Ssam } 247189251Ssam 248214734Srpaulo /* Use reduced client timeout for WPS to avoid long wait */ 249214734Srpaulo if (sm->ClientTimeout > 30) 250214734Srpaulo sm->ClientTimeout = 30; 251214734Srpaulo 252189251Ssam return data; 253189251Ssam} 254189251Ssam 255189251Ssam 256189251Ssamstatic void eap_wsc_deinit(struct eap_sm *sm, void *priv) 257189251Ssam{ 258189251Ssam struct eap_wsc_data *data = priv; 259189251Ssam wpabuf_free(data->in_buf); 260189251Ssam wpabuf_free(data->out_buf); 261189251Ssam wps_deinit(data->wps); 262189251Ssam os_free(data->wps_ctx->network_key); 263189251Ssam data->wps_ctx->network_key = NULL; 264189251Ssam os_free(data); 265189251Ssam} 266189251Ssam 267189251Ssam 268189251Ssamstatic struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, 269189251Ssam struct eap_method_ret *ret, u8 id) 270189251Ssam{ 271189251Ssam struct wpabuf *resp; 272189251Ssam u8 flags; 273189251Ssam size_t send_len, plen; 274189251Ssam 275189251Ssam ret->ignore = FALSE; 276189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response"); 277189251Ssam ret->allowNotifications = TRUE; 278189251Ssam 279189251Ssam flags = 0; 280189251Ssam send_len = wpabuf_len(data->out_buf) - data->out_used; 281189251Ssam if (2 + send_len > data->fragment_size) { 282189251Ssam send_len = data->fragment_size - 2; 283189251Ssam flags |= WSC_FLAGS_MF; 284189251Ssam if (data->out_used == 0) { 285189251Ssam flags |= WSC_FLAGS_LF; 286189251Ssam send_len -= 2; 287189251Ssam } 288189251Ssam } 289189251Ssam plen = 2 + send_len; 290189251Ssam if (flags & WSC_FLAGS_LF) 291189251Ssam plen += 2; 292189251Ssam resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, 293189251Ssam EAP_CODE_RESPONSE, id); 294189251Ssam if (resp == NULL) 295189251Ssam return NULL; 296189251Ssam 297189251Ssam wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */ 298189251Ssam wpabuf_put_u8(resp, flags); /* Flags */ 299189251Ssam if (flags & WSC_FLAGS_LF) 300189251Ssam wpabuf_put_be16(resp, wpabuf_len(data->out_buf)); 301189251Ssam 302189251Ssam wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, 303189251Ssam send_len); 304189251Ssam data->out_used += send_len; 305189251Ssam 306189251Ssam ret->methodState = METHOD_MAY_CONT; 307189251Ssam ret->decision = DECISION_FAIL; 308189251Ssam 309189251Ssam if (data->out_used == wpabuf_len(data->out_buf)) { 310189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " 311189251Ssam "(message sent completely)", 312189251Ssam (unsigned long) send_len); 313189251Ssam wpabuf_free(data->out_buf); 314189251Ssam data->out_buf = NULL; 315189251Ssam data->out_used = 0; 316189251Ssam if ((data->state == FAIL && data->out_op_code == WSC_ACK) || 317189251Ssam data->out_op_code == WSC_NACK || 318189251Ssam data->out_op_code == WSC_Done) { 319189251Ssam eap_wsc_state(data, FAIL); 320189251Ssam ret->methodState = METHOD_DONE; 321189251Ssam } else 322189251Ssam eap_wsc_state(data, MESG); 323189251Ssam } else { 324189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " 325189251Ssam "(%lu more to send)", (unsigned long) send_len, 326189251Ssam (unsigned long) wpabuf_len(data->out_buf) - 327189251Ssam data->out_used); 328189251Ssam eap_wsc_state(data, WAIT_FRAG_ACK); 329189251Ssam } 330189251Ssam 331189251Ssam return resp; 332189251Ssam} 333189251Ssam 334189251Ssam 335189251Ssamstatic int eap_wsc_process_cont(struct eap_wsc_data *data, 336189251Ssam const u8 *buf, size_t len, u8 op_code) 337189251Ssam{ 338189251Ssam /* Process continuation of a pending message */ 339189251Ssam if (op_code != data->in_op_code) { 340189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " 341189251Ssam "fragment (expected %d)", 342189251Ssam op_code, data->in_op_code); 343189251Ssam return -1; 344189251Ssam } 345189251Ssam 346189251Ssam if (len > wpabuf_tailroom(data->in_buf)) { 347189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); 348189251Ssam eap_wsc_state(data, FAIL); 349189251Ssam return -1; 350189251Ssam } 351189251Ssam 352189251Ssam wpabuf_put_data(data->in_buf, buf, len); 353189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting " 354189251Ssam "for %lu bytes more", (unsigned long) len, 355189251Ssam (unsigned long) wpabuf_tailroom(data->in_buf)); 356189251Ssam 357189251Ssam return 0; 358189251Ssam} 359189251Ssam 360189251Ssam 361189251Ssamstatic struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data, 362189251Ssam struct eap_method_ret *ret, 363189251Ssam u8 id, u8 flags, u8 op_code, 364189251Ssam u16 message_length, 365189251Ssam const u8 *buf, size_t len) 366189251Ssam{ 367189251Ssam /* Process a fragment that is not the last one of the message */ 368189251Ssam if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { 369189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a " 370189251Ssam "fragmented packet"); 371189251Ssam ret->ignore = TRUE; 372189251Ssam return NULL; 373189251Ssam } 374189251Ssam 375189251Ssam if (data->in_buf == NULL) { 376189251Ssam /* First fragment of the message */ 377189251Ssam data->in_buf = wpabuf_alloc(message_length); 378189251Ssam if (data->in_buf == NULL) { 379189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " 380189251Ssam "message"); 381189251Ssam ret->ignore = TRUE; 382189251Ssam return NULL; 383189251Ssam } 384189251Ssam data->in_op_code = op_code; 385189251Ssam wpabuf_put_data(data->in_buf, buf, len); 386189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first " 387189251Ssam "fragment, waiting for %lu bytes more", 388189251Ssam (unsigned long) len, 389189251Ssam (unsigned long) wpabuf_tailroom(data->in_buf)); 390189251Ssam } 391189251Ssam 392189251Ssam return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE); 393189251Ssam} 394189251Ssam 395189251Ssam 396189251Ssamstatic struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv, 397189251Ssam struct eap_method_ret *ret, 398189251Ssam const struct wpabuf *reqData) 399189251Ssam{ 400189251Ssam struct eap_wsc_data *data = priv; 401189251Ssam const u8 *start, *pos, *end; 402189251Ssam size_t len; 403189251Ssam u8 op_code, flags, id; 404189251Ssam u16 message_length = 0; 405189251Ssam enum wps_process_res res; 406189251Ssam struct wpabuf tmpbuf; 407214734Srpaulo struct wpabuf *r; 408189251Ssam 409189251Ssam pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData, 410189251Ssam &len); 411189251Ssam if (pos == NULL || len < 2) { 412189251Ssam ret->ignore = TRUE; 413189251Ssam return NULL; 414189251Ssam } 415189251Ssam 416189251Ssam id = eap_get_id(reqData); 417189251Ssam 418189251Ssam start = pos; 419189251Ssam end = start + len; 420189251Ssam 421189251Ssam op_code = *pos++; 422189251Ssam flags = *pos++; 423189251Ssam if (flags & WSC_FLAGS_LF) { 424189251Ssam if (end - pos < 2) { 425189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); 426189251Ssam ret->ignore = TRUE; 427189251Ssam return NULL; 428189251Ssam } 429189251Ssam message_length = WPA_GET_BE16(pos); 430189251Ssam pos += 2; 431189251Ssam 432189251Ssam if (message_length < end - pos) { 433189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " 434189251Ssam "Length"); 435189251Ssam ret->ignore = TRUE; 436189251Ssam return NULL; 437189251Ssam } 438189251Ssam } 439189251Ssam 440189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " 441189251Ssam "Flags 0x%x Message Length %d", 442189251Ssam op_code, flags, message_length); 443189251Ssam 444189251Ssam if (data->state == WAIT_FRAG_ACK) { 445189251Ssam if (op_code != WSC_FRAG_ACK) { 446189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " 447189251Ssam "in WAIT_FRAG_ACK state", op_code); 448189251Ssam ret->ignore = TRUE; 449189251Ssam return NULL; 450189251Ssam } 451189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); 452189251Ssam eap_wsc_state(data, MESG); 453189251Ssam return eap_wsc_build_msg(data, ret, id); 454189251Ssam } 455189251Ssam 456189251Ssam if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && 457189251Ssam op_code != WSC_Done && op_code != WSC_Start) { 458189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", 459189251Ssam op_code); 460189251Ssam ret->ignore = TRUE; 461189251Ssam return NULL; 462189251Ssam } 463189251Ssam 464189251Ssam if (data->state == WAIT_START) { 465189251Ssam if (op_code != WSC_Start) { 466189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " 467189251Ssam "in WAIT_START state", op_code); 468189251Ssam ret->ignore = TRUE; 469189251Ssam return NULL; 470189251Ssam } 471189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Received start"); 472189251Ssam eap_wsc_state(data, MESG); 473189251Ssam /* Start message has empty payload, skip processing */ 474189251Ssam goto send_msg; 475189251Ssam } else if (op_code == WSC_Start) { 476189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", 477189251Ssam op_code); 478189251Ssam ret->ignore = TRUE; 479189251Ssam return NULL; 480189251Ssam } 481189251Ssam 482189251Ssam if (data->in_buf && 483189251Ssam eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { 484189251Ssam ret->ignore = TRUE; 485189251Ssam return NULL; 486189251Ssam } 487189251Ssam 488189251Ssam if (flags & WSC_FLAGS_MF) { 489189251Ssam return eap_wsc_process_fragment(data, ret, id, flags, op_code, 490189251Ssam message_length, pos, 491189251Ssam end - pos); 492189251Ssam } 493189251Ssam 494189251Ssam if (data->in_buf == NULL) { 495189251Ssam /* Wrap unfragmented messages as wpabuf without extra copy */ 496189251Ssam wpabuf_set(&tmpbuf, pos, end - pos); 497189251Ssam data->in_buf = &tmpbuf; 498189251Ssam } 499189251Ssam 500189251Ssam res = wps_process_msg(data->wps, op_code, data->in_buf); 501189251Ssam switch (res) { 502189251Ssam case WPS_DONE: 503189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " 504189251Ssam "successfully - wait for EAP failure"); 505189251Ssam eap_wsc_state(data, FAIL); 506189251Ssam break; 507189251Ssam case WPS_CONTINUE: 508189251Ssam eap_wsc_state(data, MESG); 509189251Ssam break; 510189251Ssam case WPS_FAILURE: 511189251Ssam case WPS_PENDING: 512189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); 513189251Ssam eap_wsc_state(data, FAIL); 514189251Ssam break; 515189251Ssam } 516189251Ssam 517189251Ssam if (data->in_buf != &tmpbuf) 518189251Ssam wpabuf_free(data->in_buf); 519189251Ssam data->in_buf = NULL; 520189251Ssam 521189251Ssamsend_msg: 522189251Ssam if (data->out_buf == NULL) { 523189251Ssam data->out_buf = wps_get_msg(data->wps, &data->out_op_code); 524189251Ssam if (data->out_buf == NULL) { 525189251Ssam wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive " 526189251Ssam "message from WPS"); 527189251Ssam return NULL; 528189251Ssam } 529189251Ssam data->out_used = 0; 530189251Ssam } 531189251Ssam 532189251Ssam eap_wsc_state(data, MESG); 533214734Srpaulo r = eap_wsc_build_msg(data, ret, id); 534214734Srpaulo if (data->state == FAIL && ret->methodState == METHOD_DONE) { 535214734Srpaulo /* Use reduced client timeout for WPS to avoid long wait */ 536214734Srpaulo if (sm->ClientTimeout > 2) 537214734Srpaulo sm->ClientTimeout = 2; 538214734Srpaulo } 539214734Srpaulo return r; 540189251Ssam} 541189251Ssam 542189251Ssam 543189251Ssamint eap_peer_wsc_register(void) 544189251Ssam{ 545189251Ssam struct eap_method *eap; 546189251Ssam int ret; 547189251Ssam 548189251Ssam eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 549189251Ssam EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 550189251Ssam "WSC"); 551189251Ssam if (eap == NULL) 552189251Ssam return -1; 553189251Ssam 554189251Ssam eap->init = eap_wsc_init; 555189251Ssam eap->deinit = eap_wsc_deinit; 556189251Ssam eap->process = eap_wsc_process; 557189251Ssam 558189251Ssam ret = eap_peer_method_register(eap); 559189251Ssam if (ret) 560189251Ssam eap_peer_method_free(eap); 561189251Ssam return ret; 562189251Ssam} 563