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