1 /* 2 * EAP-WSC peer for Wi-Fi Protected Setup 3 * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License version 2 as 7 * published by the Free Software Foundation. 8 * 9 * Alternatively, this software may be distributed under the terms of BSD 10 * license. 11 * 12 * See README and COPYING for more details. 13 */ 14 15 #include "includes.h" 16 17 #include "common.h" 18 #include "uuid.h" 19 #include "eap_i.h" 20 #include "eap_common/eap_wsc_common.h" 21 #include "wps/wps.h" 22 #include "wps/wps_defs.h" 23 24 25 struct eap_wsc_data { 26 enum { WAIT_START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; 27 int registrar; 28 struct wpabuf *in_buf; 29 struct wpabuf *out_buf; 30 enum wsc_op_code in_op_code, out_op_code; 31 size_t out_used; 32 size_t fragment_size; 33 struct wps_data *wps; 34 struct wps_context *wps_ctx; 35 }; 36 37 38 static const char * eap_wsc_state_txt(int state) 39 { 40 switch (state) { 41 case WAIT_START: 42 return "WAIT_START"; 43 case MESG: 44 return "MESG"; 45 case FRAG_ACK: 46 return "FRAG_ACK"; 47 case WAIT_FRAG_ACK: 48 return "WAIT_FRAG_ACK"; 49 case DONE: 50 return "DONE"; 51 case FAIL: 52 return "FAIL"; 53 default: 54 return "?"; 55 } 56 } 57 58 59 static void eap_wsc_state(struct eap_wsc_data *data, int state) 60 { 61 wpa_printf(MSG_DEBUG, "EAP-WSC: %s -> %s", 62 eap_wsc_state_txt(data->state), 63 eap_wsc_state_txt(state)); 64 data->state = state; 65 } 66 67 68 static int eap_wsc_new_ap_settings(struct wps_credential *cred, 69 const char *params) 70 { 71 const char *pos, *end; 72 size_t len; 73 74 os_memset(cred, 0, sizeof(*cred)); 75 76 pos = os_strstr(params, "new_ssid="); 77 if (pos == NULL) 78 return 0; 79 pos += 9; 80 end = os_strchr(pos, ' '); 81 if (end == NULL) 82 len = os_strlen(pos); 83 else 84 len = end - pos; 85 if ((len & 1) || len > 2 * sizeof(cred->ssid) || 86 hexstr2bin(pos, cred->ssid, len / 2)) 87 return -1; 88 cred->ssid_len = len / 2; 89 90 pos = os_strstr(params, "new_auth="); 91 if (pos == NULL) 92 return -1; 93 if (os_strncmp(pos + 9, "OPEN", 4) == 0) 94 cred->auth_type = WPS_AUTH_OPEN; 95 else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0) 96 cred->auth_type = WPS_AUTH_WPAPSK; 97 else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0) 98 cred->auth_type = WPS_AUTH_WPA2PSK; 99 else 100 return -1; 101 102 pos = os_strstr(params, "new_encr="); 103 if (pos == NULL) 104 return -1; 105 if (os_strncmp(pos + 9, "NONE", 4) == 0) 106 cred->encr_type = WPS_ENCR_NONE; 107 else if (os_strncmp(pos + 9, "WEP", 3) == 0) 108 cred->encr_type = WPS_ENCR_WEP; 109 else if (os_strncmp(pos + 9, "TKIP", 4) == 0) 110 cred->encr_type = WPS_ENCR_TKIP; 111 else if (os_strncmp(pos + 9, "CCMP", 4) == 0) 112 cred->encr_type = WPS_ENCR_AES; 113 else 114 return -1; 115 116 pos = os_strstr(params, "new_key="); 117 if (pos == NULL) 118 return 0; 119 pos += 8; 120 end = os_strchr(pos, ' '); 121 if (end == NULL) 122 len = os_strlen(pos); 123 else 124 len = end - pos; 125 if ((len & 1) || len > 2 * sizeof(cred->key) || 126 hexstr2bin(pos, cred->key, len / 2)) 127 return -1; 128 cred->key_len = len / 2; 129 130 return 1; 131 } 132 133 134 static void * eap_wsc_init(struct eap_sm *sm) 135 { 136 struct eap_wsc_data *data; 137 const u8 *identity; 138 size_t identity_len; 139 int registrar; 140 struct wps_config cfg; 141 const char *pos; 142 const char *phase1; 143 struct wps_context *wps; 144 struct wps_credential new_ap_settings; 145 int res; 146 147 wps = sm->wps; 148 if (wps == NULL) { 149 wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available"); 150 return NULL; 151 } 152 153 identity = eap_get_config_identity(sm, &identity_len); 154 155 if (identity && identity_len == WSC_ID_REGISTRAR_LEN && 156 os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) 157 registrar = 1; /* Supplicant is Registrar */ 158 else if (identity && identity_len == WSC_ID_ENROLLEE_LEN && 159 os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) 160 registrar = 0; /* Supplicant is Enrollee */ 161 else { 162 wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity", 163 identity, identity_len); 164 return NULL; 165 } 166 167 data = os_zalloc(sizeof(*data)); 168 if (data == NULL) 169 return NULL; 170 data->state = registrar ? MESG : WAIT_START; 171 data->registrar = registrar; 172 data->wps_ctx = wps; 173 174 os_memset(&cfg, 0, sizeof(cfg)); 175 cfg.wps = wps; 176 cfg.registrar = registrar; 177 178 phase1 = eap_get_config_phase1(sm); 179 if (phase1 == NULL) { 180 wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not " 181 "set"); 182 os_free(data); 183 return NULL; 184 } 185 186 pos = os_strstr(phase1, "pin="); 187 if (pos) { 188 pos += 4; 189 cfg.pin = (const u8 *) pos; 190 while (*pos != '\0' && *pos != ' ') 191 pos++; 192 cfg.pin_len = pos - (const char *) cfg.pin; 193 } else { 194 pos = os_strstr(phase1, "pbc=1"); 195 if (pos) 196 cfg.pbc = 1; 197 } 198 199 if (cfg.pin == NULL && !cfg.pbc) { 200 wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 " 201 "configuration data"); 202 os_free(data); 203 return NULL; 204 } 205 206 res = eap_wsc_new_ap_settings(&new_ap_settings, phase1); 207 if (res < 0) { 208 os_free(data); 209 return NULL; 210 } 211 if (res == 1) { 212 wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for " 213 "WPS"); 214 cfg.new_ap_settings = &new_ap_settings; 215 } 216 217 data->wps = wps_init(&cfg); 218 if (data->wps == NULL) { 219 os_free(data); 220 return NULL; 221 } 222 data->fragment_size = WSC_FRAGMENT_SIZE; 223 224 if (registrar && cfg.pin) { 225 wps_registrar_add_pin(data->wps_ctx->registrar, NULL, 226 cfg.pin, cfg.pin_len, 0); 227 } 228 229 /* Use reduced client timeout for WPS to avoid long wait */ 230 if (sm->ClientTimeout > 30) 231 sm->ClientTimeout = 30; 232 233 return data; 234 } 235 236 237 static void eap_wsc_deinit(struct eap_sm *sm, void *priv) 238 { 239 struct eap_wsc_data *data = priv; 240 wpabuf_free(data->in_buf); 241 wpabuf_free(data->out_buf); 242 wps_deinit(data->wps); 243 os_free(data->wps_ctx->network_key); 244 data->wps_ctx->network_key = NULL; 245 os_free(data); 246 } 247 248 249 static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, 250 struct eap_method_ret *ret, u8 id) 251 { 252 struct wpabuf *resp; 253 u8 flags; 254 size_t send_len, plen; 255 256 ret->ignore = FALSE; 257 wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response"); 258 ret->allowNotifications = TRUE; 259 260 flags = 0; 261 send_len = wpabuf_len(data->out_buf) - data->out_used; 262 if (2 + send_len > data->fragment_size) { 263 send_len = data->fragment_size - 2; 264 flags |= WSC_FLAGS_MF; 265 if (data->out_used == 0) { 266 flags |= WSC_FLAGS_LF; 267 send_len -= 2; 268 } 269 } 270 plen = 2 + send_len; 271 if (flags & WSC_FLAGS_LF) 272 plen += 2; 273 resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen, 274 EAP_CODE_RESPONSE, id); 275 if (resp == NULL) 276 return NULL; 277 278 wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */ 279 wpabuf_put_u8(resp, flags); /* Flags */ 280 if (flags & WSC_FLAGS_LF) 281 wpabuf_put_be16(resp, wpabuf_len(data->out_buf)); 282 283 wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used, 284 send_len); 285 data->out_used += send_len; 286 287 ret->methodState = METHOD_MAY_CONT; 288 ret->decision = DECISION_FAIL; 289 290 if (data->out_used == wpabuf_len(data->out_buf)) { 291 wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " 292 "(message sent completely)", 293 (unsigned long) send_len); 294 wpabuf_free(data->out_buf); 295 data->out_buf = NULL; 296 data->out_used = 0; 297 if ((data->state == FAIL && data->out_op_code == WSC_ACK) || 298 data->out_op_code == WSC_NACK || 299 data->out_op_code == WSC_Done) { 300 eap_wsc_state(data, FAIL); 301 ret->methodState = METHOD_DONE; 302 } else 303 eap_wsc_state(data, MESG); 304 } else { 305 wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " 306 "(%lu more to send)", (unsigned long) send_len, 307 (unsigned long) wpabuf_len(data->out_buf) - 308 data->out_used); 309 eap_wsc_state(data, WAIT_FRAG_ACK); 310 } 311 312 return resp; 313 } 314 315 316 static int eap_wsc_process_cont(struct eap_wsc_data *data, 317 const u8 *buf, size_t len, u8 op_code) 318 { 319 /* Process continuation of a pending message */ 320 if (op_code != data->in_op_code) { 321 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in " 322 "fragment (expected %d)", 323 op_code, data->in_op_code); 324 return -1; 325 } 326 327 if (len > wpabuf_tailroom(data->in_buf)) { 328 wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow"); 329 eap_wsc_state(data, FAIL); 330 return -1; 331 } 332 333 wpabuf_put_data(data->in_buf, buf, len); 334 wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting " 335 "for %lu bytes more", (unsigned long) len, 336 (unsigned long) wpabuf_tailroom(data->in_buf)); 337 338 return 0; 339 } 340 341 342 static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data, 343 struct eap_method_ret *ret, 344 u8 id, u8 flags, u8 op_code, 345 u16 message_length, 346 const u8 *buf, size_t len) 347 { 348 /* Process a fragment that is not the last one of the message */ 349 if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) { 350 wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a " 351 "fragmented packet"); 352 ret->ignore = TRUE; 353 return NULL; 354 } 355 356 if (data->in_buf == NULL) { 357 /* First fragment of the message */ 358 data->in_buf = wpabuf_alloc(message_length); 359 if (data->in_buf == NULL) { 360 wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for " 361 "message"); 362 ret->ignore = TRUE; 363 return NULL; 364 } 365 data->in_op_code = op_code; 366 wpabuf_put_data(data->in_buf, buf, len); 367 wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first " 368 "fragment, waiting for %lu bytes more", 369 (unsigned long) len, 370 (unsigned long) wpabuf_tailroom(data->in_buf)); 371 } 372 373 return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE); 374 } 375 376 377 static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv, 378 struct eap_method_ret *ret, 379 const struct wpabuf *reqData) 380 { 381 struct eap_wsc_data *data = priv; 382 const u8 *start, *pos, *end; 383 size_t len; 384 u8 op_code, flags, id; 385 u16 message_length = 0; 386 enum wps_process_res res; 387 struct wpabuf tmpbuf; 388 struct wpabuf *r; 389 390 pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData, 391 &len); 392 if (pos == NULL || len < 2) { 393 ret->ignore = TRUE; 394 return NULL; 395 } 396 397 id = eap_get_id(reqData); 398 399 start = pos; 400 end = start + len; 401 402 op_code = *pos++; 403 flags = *pos++; 404 if (flags & WSC_FLAGS_LF) { 405 if (end - pos < 2) { 406 wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow"); 407 ret->ignore = TRUE; 408 return NULL; 409 } 410 message_length = WPA_GET_BE16(pos); 411 pos += 2; 412 413 if (message_length < end - pos) { 414 wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message " 415 "Length"); 416 ret->ignore = TRUE; 417 return NULL; 418 } 419 } 420 421 wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d " 422 "Flags 0x%x Message Length %d", 423 op_code, flags, message_length); 424 425 if (data->state == WAIT_FRAG_ACK) { 426 if (op_code != WSC_FRAG_ACK) { 427 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " 428 "in WAIT_FRAG_ACK state", op_code); 429 ret->ignore = TRUE; 430 return NULL; 431 } 432 wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); 433 eap_wsc_state(data, MESG); 434 return eap_wsc_build_msg(data, ret, id); 435 } 436 437 if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG && 438 op_code != WSC_Done && op_code != WSC_Start) { 439 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", 440 op_code); 441 ret->ignore = TRUE; 442 return NULL; 443 } 444 445 if (data->state == WAIT_START) { 446 if (op_code != WSC_Start) { 447 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d " 448 "in WAIT_START state", op_code); 449 ret->ignore = TRUE; 450 return NULL; 451 } 452 wpa_printf(MSG_DEBUG, "EAP-WSC: Received start"); 453 eap_wsc_state(data, MESG); 454 /* Start message has empty payload, skip processing */ 455 goto send_msg; 456 } else if (op_code == WSC_Start) { 457 wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d", 458 op_code); 459 ret->ignore = TRUE; 460 return NULL; 461 } 462 463 if (data->in_buf && 464 eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) { 465 ret->ignore = TRUE; 466 return NULL; 467 } 468 469 if (flags & WSC_FLAGS_MF) { 470 return eap_wsc_process_fragment(data, ret, id, flags, op_code, 471 message_length, pos, 472 end - pos); 473 } 474 475 if (data->in_buf == NULL) { 476 /* Wrap unfragmented messages as wpabuf without extra copy */ 477 wpabuf_set(&tmpbuf, pos, end - pos); 478 data->in_buf = &tmpbuf; 479 } 480 481 res = wps_process_msg(data->wps, op_code, data->in_buf); 482 switch (res) { 483 case WPS_DONE: 484 wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed " 485 "successfully - wait for EAP failure"); 486 eap_wsc_state(data, FAIL); 487 break; 488 case WPS_CONTINUE: 489 eap_wsc_state(data, MESG); 490 break; 491 case WPS_FAILURE: 492 case WPS_PENDING: 493 wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); 494 eap_wsc_state(data, FAIL); 495 break; 496 } 497 498 if (data->in_buf != &tmpbuf) 499 wpabuf_free(data->in_buf); 500 data->in_buf = NULL; 501 502 send_msg: 503 if (data->out_buf == NULL) { 504 data->out_buf = wps_get_msg(data->wps, &data->out_op_code); 505 if (data->out_buf == NULL) { 506 wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive " 507 "message from WPS"); 508 return NULL; 509 } 510 data->out_used = 0; 511 } 512 513 eap_wsc_state(data, MESG); 514 r = eap_wsc_build_msg(data, ret, id); 515 if (data->state == FAIL && ret->methodState == METHOD_DONE) { 516 /* Use reduced client timeout for WPS to avoid long wait */ 517 if (sm->ClientTimeout > 2) 518 sm->ClientTimeout = 2; 519 } 520 return r; 521 } 522 523 524 int eap_peer_wsc_register(void) 525 { 526 struct eap_method *eap; 527 int ret; 528 529 eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION, 530 EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, 531 "WSC"); 532 if (eap == NULL) 533 return -1; 534 535 eap->init = eap_wsc_init; 536 eap->deinit = eap_wsc_deinit; 537 eap->process = eap_wsc_process; 538 539 ret = eap_peer_method_register(eap); 540 if (ret) 541 eap_peer_method_free(eap); 542 return ret; 543 } 544