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