16d49e1aeSJan Lentfer /* 26d49e1aeSJan Lentfer * EAP-WSC peer for Wi-Fi Protected Setup 3*3ff40c12SJohn Marino * Copyright (c) 2007-2009, 2012, Jouni Malinen <j@w1.fi> 46d49e1aeSJan Lentfer * 5*3ff40c12SJohn Marino * This software may be distributed under the terms of the BSD license. 6*3ff40c12SJohn Marino * See README for more details. 76d49e1aeSJan Lentfer */ 86d49e1aeSJan Lentfer 96d49e1aeSJan Lentfer #include "includes.h" 106d49e1aeSJan Lentfer 116d49e1aeSJan Lentfer #include "common.h" 126d49e1aeSJan Lentfer #include "uuid.h" 136d49e1aeSJan Lentfer #include "eap_i.h" 146d49e1aeSJan Lentfer #include "eap_common/eap_wsc_common.h" 156d49e1aeSJan Lentfer #include "wps/wps.h" 166d49e1aeSJan Lentfer #include "wps/wps_defs.h" 176d49e1aeSJan Lentfer 186d49e1aeSJan Lentfer 196d49e1aeSJan Lentfer struct eap_wsc_data { 206d49e1aeSJan Lentfer enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; 216d49e1aeSJan Lentfer int registrar; 226d49e1aeSJan Lentfer struct wpabuf *in_buf; 236d49e1aeSJan Lentfer struct wpabuf *out_buf; 246d49e1aeSJan Lentfer enum wsc_op_code in_op_code, out_op_code; 256d49e1aeSJan Lentfer size_t out_used; 266d49e1aeSJan Lentfer size_t fragment_size; 276d49e1aeSJan Lentfer struct wps_data *wps; 286d49e1aeSJan Lentfer struct wps_context *wps_ctx; 296d49e1aeSJan Lentfer }; 306d49e1aeSJan Lentfer 316d49e1aeSJan Lentfer 326d49e1aeSJan Lentfer static const char * eap_wsc_state_txt(int state) 336d49e1aeSJan Lentfer { 346d49e1aeSJan Lentfer switch (state) { 356d49e1aeSJan Lentfer case WAIT_START: 366d49e1aeSJan Lentfer return "WAIT_START"; 376d49e1aeSJan Lentfer case MESG: 386d49e1aeSJan Lentfer return "MESG"; 396d49e1aeSJan Lentfer case FRAG_ACK: 406d49e1aeSJan Lentfer return "FRAG_ACK"; 416d49e1aeSJan Lentfer case WAIT_FRAG_ACK: 426d49e1aeSJan Lentfer return "WAIT_FRAG_ACK"; 436d49e1aeSJan Lentfer case DONE: 446d49e1aeSJan Lentfer return "DONE"; 456d49e1aeSJan Lentfer case FAIL: 466d49e1aeSJan Lentfer return "FAIL"; 476d49e1aeSJan Lentfer default: 486d49e1aeSJan Lentfer return "?"; 496d49e1aeSJan Lentfer } 506d49e1aeSJan Lentfer } 516d49e1aeSJan Lentfer 526d49e1aeSJan Lentfer 536d49e1aeSJan Lentfer static void eap_wsc_state(struct eap_wsc_data *data, int state) 546d49e1aeSJan Lentfer { 556d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", 566d49e1aeSJan Lentfer eap_wsc_state_txt(data->state), 576d49e1aeSJan Lentfer eap_wsc_state_txt(state)); 586d49e1aeSJan Lentfer data->state = state; 596d49e1aeSJan Lentfer } 606d49e1aeSJan Lentfer 616d49e1aeSJan Lentfer 62*3ff40c12SJohn Marino static int eap_wsc_new_ap_settings(struct wps_credential *cred, 63*3ff40c12SJohn Marino const char *params) 64*3ff40c12SJohn Marino { 65*3ff40c12SJohn Marino const char *pos, *end; 66*3ff40c12SJohn Marino size_t len; 67*3ff40c12SJohn Marino 68*3ff40c12SJohn Marino os_memset(cred, 0, sizeof(*cred)); 69*3ff40c12SJohn Marino 70*3ff40c12SJohn Marino pos = os_strstr(params, "new_ssid="); 71*3ff40c12SJohn Marino if (pos == NULL) 72*3ff40c12SJohn Marino return 0; 73*3ff40c12SJohn Marino pos += 9; 74*3ff40c12SJohn Marino end = os_strchr(pos, ' '); 75*3ff40c12SJohn Marino if (end == NULL) 76*3ff40c12SJohn Marino len = os_strlen(pos); 77*3ff40c12SJohn Marino else 78*3ff40c12SJohn Marino len = end - pos; 79*3ff40c12SJohn Marino if ((len & 1) || len > 2 * sizeof(cred->ssid) || 80*3ff40c12SJohn Marino hexstr2bin(pos, cred->ssid, len / 2)) { 81*3ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_ssid"); 82*3ff40c12SJohn Marino return -1; 83*3ff40c12SJohn Marino } 84*3ff40c12SJohn Marino cred->ssid_len = len / 2; 85*3ff40c12SJohn Marino 86*3ff40c12SJohn Marino pos = os_strstr(params, "new_auth="); 87*3ff40c12SJohn Marino if (pos == NULL) { 88*3ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_auth"); 89*3ff40c12SJohn Marino return -1; 90*3ff40c12SJohn Marino } 91*3ff40c12SJohn Marino if (os_strncmp(pos + 9, "OPEN", 4) == 0) 92*3ff40c12SJohn Marino cred->auth_type = WPS_AUTH_OPEN; 93*3ff40c12SJohn Marino else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0) 94*3ff40c12SJohn Marino cred->auth_type = WPS_AUTH_WPAPSK; 95*3ff40c12SJohn Marino else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0) 96*3ff40c12SJohn Marino cred->auth_type = WPS_AUTH_WPA2PSK; 97*3ff40c12SJohn Marino else { 98*3ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_auth"); 99*3ff40c12SJohn Marino return -1; 100*3ff40c12SJohn Marino } 101*3ff40c12SJohn Marino 102*3ff40c12SJohn Marino pos = os_strstr(params, "new_encr="); 103*3ff40c12SJohn Marino if (pos == NULL) { 104*3ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-WSC: Missing new_encr"); 105*3ff40c12SJohn Marino return -1; 106*3ff40c12SJohn Marino } 107*3ff40c12SJohn Marino if (os_strncmp(pos + 9, "NONE", 4) == 0) 108*3ff40c12SJohn Marino cred->encr_type = WPS_ENCR_NONE; 109*3ff40c12SJohn Marino else if (os_strncmp(pos + 9, "WEP", 3) == 0) 110*3ff40c12SJohn Marino cred->encr_type = WPS_ENCR_WEP; 111*3ff40c12SJohn Marino else if (os_strncmp(pos + 9, "TKIP", 4) == 0) 112*3ff40c12SJohn Marino cred->encr_type = WPS_ENCR_TKIP; 113*3ff40c12SJohn Marino else if (os_strncmp(pos + 9, "CCMP", 4) == 0) 114*3ff40c12SJohn Marino cred->encr_type = WPS_ENCR_AES; 115*3ff40c12SJohn Marino else { 116*3ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-WSC: Unknown new_encr"); 117*3ff40c12SJohn Marino return -1; 118*3ff40c12SJohn Marino } 119*3ff40c12SJohn Marino 120*3ff40c12SJohn Marino pos = os_strstr(params, "new_key="); 121*3ff40c12SJohn Marino if (pos == NULL) 122*3ff40c12SJohn Marino return 0; 123*3ff40c12SJohn Marino pos += 8; 124*3ff40c12SJohn Marino end = os_strchr(pos, ' '); 125*3ff40c12SJohn Marino if (end == NULL) 126*3ff40c12SJohn Marino len = os_strlen(pos); 127*3ff40c12SJohn Marino else 128*3ff40c12SJohn Marino len = end - pos; 129*3ff40c12SJohn Marino if ((len & 1) || len > 2 * sizeof(cred->key) || 130*3ff40c12SJohn Marino hexstr2bin(pos, cred->key, len / 2)) { 131*3ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid new_key"); 132*3ff40c12SJohn Marino return -1; 133*3ff40c12SJohn Marino } 134*3ff40c12SJohn Marino cred->key_len = len / 2; 135*3ff40c12SJohn Marino 136*3ff40c12SJohn Marino return 1; 137*3ff40c12SJohn Marino } 138*3ff40c12SJohn Marino 139*3ff40c12SJohn Marino 1406d49e1aeSJan Lentfer static void * eap_wsc_init(struct eap_sm *sm) 1416d49e1aeSJan Lentfer { 1426d49e1aeSJan Lentfer struct eap_wsc_data *data; 1436d49e1aeSJan Lentfer const u8 *identity; 1446d49e1aeSJan Lentfer size_t identity_len; 1456d49e1aeSJan Lentfer int registrar; 1466d49e1aeSJan Lentfer struct wps_config cfg; 147*3ff40c12SJohn Marino const char *pos, *end; 1486d49e1aeSJan Lentfer const char *phase1; 1496d49e1aeSJan Lentfer struct wps_context *wps; 150*3ff40c12SJohn Marino struct wps_credential new_ap_settings; 151*3ff40c12SJohn Marino int res; 152*3ff40c12SJohn Marino int nfc = 0; 153*3ff40c12SJohn Marino u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN]; 1546d49e1aeSJan Lentfer 1556d49e1aeSJan Lentfer wps = sm->wps; 1566d49e1aeSJan Lentfer if (wps == NULL) { 1576d49e1aeSJan Lentfer wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available"); 1586d49e1aeSJan Lentfer return NULL; 1596d49e1aeSJan Lentfer } 1606d49e1aeSJan Lentfer 1616d49e1aeSJan Lentfer identity = eap_get_config_identity(sm, &identity_len); 1626d49e1aeSJan Lentfer 1636d49e1aeSJan Lentfer if (identity && identity_len == WSC_ID_REGISTRAR_LEN && 1646d49e1aeSJan Lentfer os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) 1656d49e1aeSJan Lentfer registrar = 1; /* Supplicant is Registrar */ 1666d49e1aeSJan Lentfer else if (identity && identity_len == WSC_ID_ENROLLEE_LEN && 1676d49e1aeSJan Lentfer os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) 1686d49e1aeSJan Lentfer registrar = 0; /* Supplicant is Enrollee */ 1696d49e1aeSJan Lentfer else { 1706d49e1aeSJan Lentfer wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", 1716d49e1aeSJan Lentfer identity, identity_len); 1726d49e1aeSJan Lentfer return NULL; 1736d49e1aeSJan Lentfer } 1746d49e1aeSJan Lentfer 1756d49e1aeSJan Lentfer data = os_zalloc(sizeof(*data)); 1766d49e1aeSJan Lentfer if (data == NULL) 1776d49e1aeSJan Lentfer return NULL; 1786d49e1aeSJan Lentfer data->state = registrar ? MESG : WAIT_START; 1796d49e1aeSJan Lentfer data->registrar = registrar; 1806d49e1aeSJan Lentfer data->wps_ctx = wps; 1816d49e1aeSJan Lentfer 1826d49e1aeSJan Lentfer os_memset(&cfg, 0, sizeof(cfg)); 1836d49e1aeSJan Lentfer cfg.wps = wps; 1846d49e1aeSJan Lentfer cfg.registrar = registrar; 1856d49e1aeSJan Lentfer 1866d49e1aeSJan Lentfer phase1 = eap_get_config_phase1(sm); 1876d49e1aeSJan Lentfer if (phase1 == NULL) { 1886d49e1aeSJan Lentfer wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not " 1896d49e1aeSJan Lentfer "set"); 1906d49e1aeSJan Lentfer os_free(data); 1916d49e1aeSJan Lentfer return NULL; 1926d49e1aeSJan Lentfer } 1936d49e1aeSJan Lentfer 1946d49e1aeSJan Lentfer pos = os_strstr(phase1, "pin="); 1956d49e1aeSJan Lentfer if (pos) { 1966d49e1aeSJan Lentfer pos += 4; 1976d49e1aeSJan Lentfer cfg.pin = (const u8 *) pos; 1986d49e1aeSJan Lentfer while (*pos != '\0' && *pos != ' ') 1996d49e1aeSJan Lentfer pos++; 2006d49e1aeSJan Lentfer cfg.pin_len = pos - (const char *) cfg.pin; 201*3ff40c12SJohn Marino if (cfg.pin_len == 6 && 202*3ff40c12SJohn Marino os_strncmp((const char *) cfg.pin, "nfc-pw", 6) == 0) { 203*3ff40c12SJohn Marino cfg.pin = NULL; 204*3ff40c12SJohn Marino cfg.pin_len = 0; 205*3ff40c12SJohn Marino nfc = 1; 206*3ff40c12SJohn Marino } 2076d49e1aeSJan Lentfer } else { 2086d49e1aeSJan Lentfer pos = os_strstr(phase1, "pbc=1"); 2096d49e1aeSJan Lentfer if (pos) 2106d49e1aeSJan Lentfer cfg.pbc = 1; 2116d49e1aeSJan Lentfer } 2126d49e1aeSJan Lentfer 213*3ff40c12SJohn Marino pos = os_strstr(phase1, "dev_pw_id="); 214*3ff40c12SJohn Marino if (pos) { 215*3ff40c12SJohn Marino u16 id = atoi(pos + 10); 216*3ff40c12SJohn Marino if (id == DEV_PW_NFC_CONNECTION_HANDOVER) 217*3ff40c12SJohn Marino nfc = 1; 218*3ff40c12SJohn Marino if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER) 219*3ff40c12SJohn Marino cfg.dev_pw_id = id; 220*3ff40c12SJohn Marino } 221*3ff40c12SJohn Marino 222*3ff40c12SJohn Marino if (cfg.pin == NULL && !cfg.pbc && !nfc) { 2236d49e1aeSJan Lentfer wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 " 2246d49e1aeSJan Lentfer "configuration data"); 2256d49e1aeSJan Lentfer os_free(data); 2266d49e1aeSJan Lentfer return NULL; 2276d49e1aeSJan Lentfer } 2286d49e1aeSJan Lentfer 229*3ff40c12SJohn Marino pos = os_strstr(phase1, " pkhash="); 230*3ff40c12SJohn Marino if (pos) { 231*3ff40c12SJohn Marino size_t len; 232*3ff40c12SJohn Marino pos += 8; 233*3ff40c12SJohn Marino end = os_strchr(pos, ' '); 234*3ff40c12SJohn Marino if (end) 235*3ff40c12SJohn Marino len = end - pos; 236*3ff40c12SJohn Marino else 237*3ff40c12SJohn Marino len = os_strlen(pos); 238*3ff40c12SJohn Marino if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN || 239*3ff40c12SJohn Marino hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) { 240*3ff40c12SJohn Marino wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash"); 2416d49e1aeSJan Lentfer os_free(data); 2426d49e1aeSJan Lentfer return NULL; 2436d49e1aeSJan Lentfer } 244*3ff40c12SJohn Marino cfg.peer_pubkey_hash = pkhash; 245*3ff40c12SJohn Marino } 246*3ff40c12SJohn Marino 247*3ff40c12SJohn Marino res = eap_wsc_new_ap_settings(&new_ap_settings, phase1); 248*3ff40c12SJohn Marino if (res < 0) { 249*3ff40c12SJohn Marino os_free(data); 250*3ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to parse new AP " 251*3ff40c12SJohn Marino "settings"); 252*3ff40c12SJohn Marino return NULL; 253*3ff40c12SJohn Marino } 254*3ff40c12SJohn Marino if (res == 1) { 255*3ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for " 256*3ff40c12SJohn Marino "WPS"); 257*3ff40c12SJohn Marino cfg.new_ap_settings = &new_ap_settings; 258*3ff40c12SJohn Marino } 259*3ff40c12SJohn Marino 260*3ff40c12SJohn Marino data->wps = wps_init(&cfg); 261*3ff40c12SJohn Marino if (data->wps == NULL) { 262*3ff40c12SJohn Marino os_free(data); 263*3ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-WSC: wps_init failed"); 264*3ff40c12SJohn Marino return NULL; 265*3ff40c12SJohn Marino } 266*3ff40c12SJohn Marino res = eap_get_config_fragment_size(sm); 267*3ff40c12SJohn Marino if (res > 0) 268*3ff40c12SJohn Marino data->fragment_size = res; 269*3ff40c12SJohn Marino else 2706d49e1aeSJan Lentfer data->fragment_size = WSC_FRAGMENT_SIZE; 271*3ff40c12SJohn Marino wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u", 272*3ff40c12SJohn Marino (unsigned int) data->fragment_size); 2736d49e1aeSJan Lentfer 2746d49e1aeSJan Lentfer if (registrar && cfg.pin) { 275*3ff40c12SJohn Marino wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL, 2766d49e1aeSJan Lentfer cfg.pin, cfg.pin_len, 0); 2776d49e1aeSJan Lentfer } 2786d49e1aeSJan Lentfer 279*3ff40c12SJohn Marino /* Use reduced client timeout for WPS to avoid long wait */ 280*3ff40c12SJohn Marino if (sm->ClientTimeout > 30) 281*3ff40c12SJohn Marino sm->ClientTimeout = 30; 282*3ff40c12SJohn Marino 2836d49e1aeSJan Lentfer return data; 2846d49e1aeSJan Lentfer } 2856d49e1aeSJan Lentfer 2866d49e1aeSJan Lentfer 2876d49e1aeSJan Lentfer static void eap_wsc_deinit(struct eap_sm *sm, void *priv) 2886d49e1aeSJan Lentfer { 2896d49e1aeSJan Lentfer struct eap_wsc_data *data = priv; 2906d49e1aeSJan Lentfer wpabuf_free(data->in_buf); 2916d49e1aeSJan Lentfer wpabuf_free(data->out_buf); 2926d49e1aeSJan Lentfer wps_deinit(data->wps); 2936d49e1aeSJan Lentfer os_free(data->wps_ctx->network_key); 2946d49e1aeSJan Lentfer data->wps_ctx->network_key = NULL; 2956d49e1aeSJan Lentfer os_free(data); 2966d49e1aeSJan Lentfer } 2976d49e1aeSJan Lentfer 2986d49e1aeSJan Lentfer 2996d49e1aeSJan Lentfer static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, 3006d49e1aeSJan Lentfer struct eap_method_ret *ret, u8 id) 3016d49e1aeSJan Lentfer { 3026d49e1aeSJan Lentfer struct wpabuf *resp; 3036d49e1aeSJan Lentfer u8 flags; 3046d49e1aeSJan Lentfer size_t send_len, plen; 3056d49e1aeSJan Lentfer 3066d49e1aeSJan Lentfer ret->ignore = FALSE; 3076d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response"); 3086d49e1aeSJan Lentfer ret->allowNotifications = TRUE; 3096d49e1aeSJan Lentfer 3106d49e1aeSJan Lentfer flags = 0; 3116d49e1aeSJan Lentfer send_len = wpabuf_len(data->out_buf) - data->out_used; 3126d49e1aeSJan Lentfer if (2 + send_len > data->fragment_size) { 3136d49e1aeSJan Lentfer send_len = data->fragment_size - 2; 3146d49e1aeSJan Lentfer flags |= WSC_FLAGS_MF; 3156d49e1aeSJan Lentfer if (data->out_used == 0) { 3166d49e1aeSJan Lentfer flags |= WSC_FLAGS_LF; 3176d49e1aeSJan Lentfer send_len -= 2; 3186d49e1aeSJan Lentfer } 3196d49e1aeSJan Lentfer } 3206d49e1aeSJan Lentfer plen = 2 + send_len; 3216d49e1aeSJan Lentfer if (flags & WSC_FLAGS_LF) 3226d49e1aeSJan Lentfer plen += 2; 3236d49e1aeSJan Lentfer resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, 3246d49e1aeSJan Lentfer EAP_CODE_RESPONSE, id); 3256d49e1aeSJan Lentfer if (resp == NULL) 3266d49e1aeSJan Lentfer return NULL; 3276d49e1aeSJan Lentfer 3286d49e1aeSJan Lentfer wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */ 3296d49e1aeSJan Lentfer wpabuf_put_u8(resp, flags); /* Flags */ 3306d49e1aeSJan Lentfer if (flags & WSC_FLAGS_LF) 3316d49e1aeSJan Lentfer wpabuf_put_be16(resp, wpabuf_len(data->out_buf)); 3326d49e1aeSJan Lentfer 3336d49e1aeSJan Lentfer wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, 3346d49e1aeSJan Lentfer send_len); 3356d49e1aeSJan Lentfer data->out_used += send_len; 3366d49e1aeSJan Lentfer 3376d49e1aeSJan Lentfer ret->methodState = METHOD_MAY_CONT; 3386d49e1aeSJan Lentfer ret->decision = DECISION_FAIL; 3396d49e1aeSJan Lentfer 3406d49e1aeSJan Lentfer if (data->out_used == wpabuf_len(data->out_buf)) { 3416d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " 3426d49e1aeSJan Lentfer "(message sent completely)", 3436d49e1aeSJan Lentfer (unsigned long) send_len); 3446d49e1aeSJan Lentfer wpabuf_free(data->out_buf); 3456d49e1aeSJan Lentfer data->out_buf = NULL; 3466d49e1aeSJan Lentfer data->out_used = 0; 3476d49e1aeSJan Lentfer if ((data->state == FAIL && data->out_op_code == WSC_ACK) || 3486d49e1aeSJan Lentfer data->out_op_code == WSC_NACK || 3496d49e1aeSJan Lentfer data->out_op_code == WSC_Done) { 3506d49e1aeSJan Lentfer eap_wsc_state(data, FAIL); 3516d49e1aeSJan Lentfer ret->methodState = METHOD_DONE; 3526d49e1aeSJan Lentfer } else 3536d49e1aeSJan Lentfer eap_wsc_state(data, MESG); 3546d49e1aeSJan Lentfer } else { 3556d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " 3566d49e1aeSJan Lentfer "(%lu more to send)", (unsigned long) send_len, 3576d49e1aeSJan Lentfer (unsigned long) wpabuf_len(data->out_buf) - 3586d49e1aeSJan Lentfer data->out_used); 3596d49e1aeSJan Lentfer eap_wsc_state(data, WAIT_FRAG_ACK); 3606d49e1aeSJan Lentfer } 3616d49e1aeSJan Lentfer 3626d49e1aeSJan Lentfer return resp; 3636d49e1aeSJan Lentfer } 3646d49e1aeSJan Lentfer 3656d49e1aeSJan Lentfer 3666d49e1aeSJan Lentfer static int eap_wsc_process_cont(struct eap_wsc_data *data, 3676d49e1aeSJan Lentfer const u8 *buf, size_t len, u8 op_code) 3686d49e1aeSJan Lentfer { 3696d49e1aeSJan Lentfer /* Process continuation of a pending message */ 3706d49e1aeSJan Lentfer if (op_code != data->in_op_code) { 3716d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " 3726d49e1aeSJan Lentfer "fragment (expected %d)", 3736d49e1aeSJan Lentfer op_code, data->in_op_code); 3746d49e1aeSJan Lentfer return -1; 3756d49e1aeSJan Lentfer } 3766d49e1aeSJan Lentfer 3776d49e1aeSJan Lentfer if (len > wpabuf_tailroom(data->in_buf)) { 3786d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); 3796d49e1aeSJan Lentfer eap_wsc_state(data, FAIL); 3806d49e1aeSJan Lentfer return -1; 3816d49e1aeSJan Lentfer } 3826d49e1aeSJan Lentfer 3836d49e1aeSJan Lentfer wpabuf_put_data(data->in_buf, buf, len); 3846d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting " 3856d49e1aeSJan Lentfer "for %lu bytes more", (unsigned long) len, 3866d49e1aeSJan Lentfer (unsigned long) wpabuf_tailroom(data->in_buf)); 3876d49e1aeSJan Lentfer 3886d49e1aeSJan Lentfer return 0; 3896d49e1aeSJan Lentfer } 3906d49e1aeSJan Lentfer 3916d49e1aeSJan Lentfer 3926d49e1aeSJan Lentfer static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data, 3936d49e1aeSJan Lentfer struct eap_method_ret *ret, 3946d49e1aeSJan Lentfer u8 id, u8 flags, u8 op_code, 3956d49e1aeSJan Lentfer u16 message_length, 3966d49e1aeSJan Lentfer const u8 *buf, size_t len) 3976d49e1aeSJan Lentfer { 3986d49e1aeSJan Lentfer /* Process a fragment that is not the last one of the message */ 3996d49e1aeSJan Lentfer if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { 4006d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a " 4016d49e1aeSJan Lentfer "fragmented packet"); 4026d49e1aeSJan Lentfer ret->ignore = TRUE; 4036d49e1aeSJan Lentfer return NULL; 4046d49e1aeSJan Lentfer } 4056d49e1aeSJan Lentfer 4066d49e1aeSJan Lentfer if (data->in_buf == NULL) { 4076d49e1aeSJan Lentfer /* First fragment of the message */ 4086d49e1aeSJan Lentfer data->in_buf = wpabuf_alloc(message_length); 4096d49e1aeSJan Lentfer if (data->in_buf == NULL) { 4106d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " 4116d49e1aeSJan Lentfer "message"); 4126d49e1aeSJan Lentfer ret->ignore = TRUE; 4136d49e1aeSJan Lentfer return NULL; 4146d49e1aeSJan Lentfer } 4156d49e1aeSJan Lentfer data->in_op_code = op_code; 4166d49e1aeSJan Lentfer wpabuf_put_data(data->in_buf, buf, len); 4176d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first " 4186d49e1aeSJan Lentfer "fragment, waiting for %lu bytes more", 4196d49e1aeSJan Lentfer (unsigned long) len, 4206d49e1aeSJan Lentfer (unsigned long) wpabuf_tailroom(data->in_buf)); 4216d49e1aeSJan Lentfer } 4226d49e1aeSJan Lentfer 4236d49e1aeSJan Lentfer return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE); 4246d49e1aeSJan Lentfer } 4256d49e1aeSJan Lentfer 4266d49e1aeSJan Lentfer 4276d49e1aeSJan Lentfer static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv, 4286d49e1aeSJan Lentfer struct eap_method_ret *ret, 4296d49e1aeSJan Lentfer const struct wpabuf *reqData) 4306d49e1aeSJan Lentfer { 4316d49e1aeSJan Lentfer struct eap_wsc_data *data = priv; 4326d49e1aeSJan Lentfer const u8 *start, *pos, *end; 4336d49e1aeSJan Lentfer size_t len; 4346d49e1aeSJan Lentfer u8 op_code, flags, id; 4356d49e1aeSJan Lentfer u16 message_length = 0; 4366d49e1aeSJan Lentfer enum wps_process_res res; 4376d49e1aeSJan Lentfer struct wpabuf tmpbuf; 438*3ff40c12SJohn Marino struct wpabuf *r; 4396d49e1aeSJan Lentfer 4406d49e1aeSJan Lentfer pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData, 4416d49e1aeSJan Lentfer &len); 4426d49e1aeSJan Lentfer if (pos == NULL || len < 2) { 4436d49e1aeSJan Lentfer ret->ignore = TRUE; 4446d49e1aeSJan Lentfer return NULL; 4456d49e1aeSJan Lentfer } 4466d49e1aeSJan Lentfer 4476d49e1aeSJan Lentfer id = eap_get_id(reqData); 4486d49e1aeSJan Lentfer 4496d49e1aeSJan Lentfer start = pos; 4506d49e1aeSJan Lentfer end = start + len; 4516d49e1aeSJan Lentfer 4526d49e1aeSJan Lentfer op_code = *pos++; 4536d49e1aeSJan Lentfer flags = *pos++; 4546d49e1aeSJan Lentfer if (flags & WSC_FLAGS_LF) { 4556d49e1aeSJan Lentfer if (end - pos < 2) { 4566d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); 4576d49e1aeSJan Lentfer ret->ignore = TRUE; 4586d49e1aeSJan Lentfer return NULL; 4596d49e1aeSJan Lentfer } 4606d49e1aeSJan Lentfer message_length = WPA_GET_BE16(pos); 4616d49e1aeSJan Lentfer pos += 2; 4626d49e1aeSJan Lentfer 4636d49e1aeSJan Lentfer if (message_length < end - pos) { 4646d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " 4656d49e1aeSJan Lentfer "Length"); 4666d49e1aeSJan Lentfer ret->ignore = TRUE; 4676d49e1aeSJan Lentfer return NULL; 4686d49e1aeSJan Lentfer } 4696d49e1aeSJan Lentfer } 4706d49e1aeSJan Lentfer 4716d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " 4726d49e1aeSJan Lentfer "Flags 0x%x Message Length %d", 4736d49e1aeSJan Lentfer op_code, flags, message_length); 4746d49e1aeSJan Lentfer 4756d49e1aeSJan Lentfer if (data->state == WAIT_FRAG_ACK) { 4766d49e1aeSJan Lentfer if (op_code != WSC_FRAG_ACK) { 4776d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " 4786d49e1aeSJan Lentfer "in WAIT_FRAG_ACK state", op_code); 4796d49e1aeSJan Lentfer ret->ignore = TRUE; 4806d49e1aeSJan Lentfer return NULL; 4816d49e1aeSJan Lentfer } 4826d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); 4836d49e1aeSJan Lentfer eap_wsc_state(data, MESG); 4846d49e1aeSJan Lentfer return eap_wsc_build_msg(data, ret, id); 4856d49e1aeSJan Lentfer } 4866d49e1aeSJan Lentfer 4876d49e1aeSJan Lentfer if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && 4886d49e1aeSJan Lentfer op_code != WSC_Done && op_code != WSC_Start) { 4896d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", 4906d49e1aeSJan Lentfer op_code); 4916d49e1aeSJan Lentfer ret->ignore = TRUE; 4926d49e1aeSJan Lentfer return NULL; 4936d49e1aeSJan Lentfer } 4946d49e1aeSJan Lentfer 4956d49e1aeSJan Lentfer if (data->state == WAIT_START) { 4966d49e1aeSJan Lentfer if (op_code != WSC_Start) { 4976d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " 4986d49e1aeSJan Lentfer "in WAIT_START state", op_code); 4996d49e1aeSJan Lentfer ret->ignore = TRUE; 5006d49e1aeSJan Lentfer return NULL; 5016d49e1aeSJan Lentfer } 5026d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Received start"); 5036d49e1aeSJan Lentfer eap_wsc_state(data, MESG); 5046d49e1aeSJan Lentfer /* Start message has empty payload, skip processing */ 5056d49e1aeSJan Lentfer goto send_msg; 5066d49e1aeSJan Lentfer } else if (op_code == WSC_Start) { 5076d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", 5086d49e1aeSJan Lentfer op_code); 5096d49e1aeSJan Lentfer ret->ignore = TRUE; 5106d49e1aeSJan Lentfer return NULL; 5116d49e1aeSJan Lentfer } 5126d49e1aeSJan Lentfer 5136d49e1aeSJan Lentfer if (data->in_buf && 5146d49e1aeSJan Lentfer eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { 5156d49e1aeSJan Lentfer ret->ignore = TRUE; 5166d49e1aeSJan Lentfer return NULL; 5176d49e1aeSJan Lentfer } 5186d49e1aeSJan Lentfer 5196d49e1aeSJan Lentfer if (flags & WSC_FLAGS_MF) { 5206d49e1aeSJan Lentfer return eap_wsc_process_fragment(data, ret, id, flags, op_code, 5216d49e1aeSJan Lentfer message_length, pos, 5226d49e1aeSJan Lentfer end - pos); 5236d49e1aeSJan Lentfer } 5246d49e1aeSJan Lentfer 5256d49e1aeSJan Lentfer if (data->in_buf == NULL) { 5266d49e1aeSJan Lentfer /* Wrap unfragmented messages as wpabuf without extra copy */ 5276d49e1aeSJan Lentfer wpabuf_set(&tmpbuf, pos, end - pos); 5286d49e1aeSJan Lentfer data->in_buf = &tmpbuf; 5296d49e1aeSJan Lentfer } 5306d49e1aeSJan Lentfer 5316d49e1aeSJan Lentfer res = wps_process_msg(data->wps, op_code, data->in_buf); 5326d49e1aeSJan Lentfer switch (res) { 5336d49e1aeSJan Lentfer case WPS_DONE: 5346d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " 5356d49e1aeSJan Lentfer "successfully - wait for EAP failure"); 5366d49e1aeSJan Lentfer eap_wsc_state(data, FAIL); 5376d49e1aeSJan Lentfer break; 5386d49e1aeSJan Lentfer case WPS_CONTINUE: 5396d49e1aeSJan Lentfer eap_wsc_state(data, MESG); 5406d49e1aeSJan Lentfer break; 5416d49e1aeSJan Lentfer case WPS_FAILURE: 5426d49e1aeSJan Lentfer case WPS_PENDING: 5436d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); 5446d49e1aeSJan Lentfer eap_wsc_state(data, FAIL); 5456d49e1aeSJan Lentfer break; 5466d49e1aeSJan Lentfer } 5476d49e1aeSJan Lentfer 5486d49e1aeSJan Lentfer if (data->in_buf != &tmpbuf) 5496d49e1aeSJan Lentfer wpabuf_free(data->in_buf); 5506d49e1aeSJan Lentfer data->in_buf = NULL; 5516d49e1aeSJan Lentfer 5526d49e1aeSJan Lentfer send_msg: 5536d49e1aeSJan Lentfer if (data->out_buf == NULL) { 5546d49e1aeSJan Lentfer data->out_buf = wps_get_msg(data->wps, &data->out_op_code); 5556d49e1aeSJan Lentfer if (data->out_buf == NULL) { 5566d49e1aeSJan Lentfer wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive " 5576d49e1aeSJan Lentfer "message from WPS"); 5586d49e1aeSJan Lentfer return NULL; 5596d49e1aeSJan Lentfer } 5606d49e1aeSJan Lentfer data->out_used = 0; 5616d49e1aeSJan Lentfer } 5626d49e1aeSJan Lentfer 5636d49e1aeSJan Lentfer eap_wsc_state(data, MESG); 564*3ff40c12SJohn Marino r = eap_wsc_build_msg(data, ret, id); 565*3ff40c12SJohn Marino if (data->state == FAIL && ret->methodState == METHOD_DONE) { 566*3ff40c12SJohn Marino /* Use reduced client timeout for WPS to avoid long wait */ 567*3ff40c12SJohn Marino if (sm->ClientTimeout > 2) 568*3ff40c12SJohn Marino sm->ClientTimeout = 2; 569*3ff40c12SJohn Marino } 570*3ff40c12SJohn Marino return r; 5716d49e1aeSJan Lentfer } 5726d49e1aeSJan Lentfer 5736d49e1aeSJan Lentfer 5746d49e1aeSJan Lentfer int eap_peer_wsc_register(void) 5756d49e1aeSJan Lentfer { 5766d49e1aeSJan Lentfer struct eap_method *eap; 5776d49e1aeSJan Lentfer int ret; 5786d49e1aeSJan Lentfer 5796d49e1aeSJan Lentfer eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 5806d49e1aeSJan Lentfer EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 5816d49e1aeSJan Lentfer "WSC"); 5826d49e1aeSJan Lentfer if (eap == NULL) 5836d49e1aeSJan Lentfer return -1; 5846d49e1aeSJan Lentfer 5856d49e1aeSJan Lentfer eap->init = eap_wsc_init; 5866d49e1aeSJan Lentfer eap->deinit = eap_wsc_deinit; 5876d49e1aeSJan Lentfer eap->process = eap_wsc_process; 5886d49e1aeSJan Lentfer 5896d49e1aeSJan Lentfer ret = eap_peer_method_register(eap); 5906d49e1aeSJan Lentfer if (ret) 5916d49e1aeSJan Lentfer eap_peer_method_free(eap); 5926d49e1aeSJan Lentfer return ret; 5936d49e1aeSJan Lentfer } 594