Home | History | Annotate | Download | only in eap_peer
      1 /*
      2  * EAP-WSC peer for Wi-Fi Protected Setup
      3  * Copyright (c) 2007-2009, Jouni Malinen <j (at) 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 	pos = os_strstr(phase1, "dev_pw_id=");
    207 	if (pos && cfg.pin)
    208 		cfg.dev_pw_id = atoi(pos + 10);
    209 
    210 	res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
    211 	if (res < 0) {
    212 		os_free(data);
    213 		return NULL;
    214 	}
    215 	if (res == 1) {
    216 		wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for "
    217 			   "WPS");
    218 		cfg.new_ap_settings = &new_ap_settings;
    219 	}
    220 
    221 	data->wps = wps_init(&cfg);
    222 	if (data->wps == NULL) {
    223 		os_free(data);
    224 		return NULL;
    225 	}
    226 	res = eap_get_config_fragment_size(sm);
    227 	if (res > 0)
    228 		data->fragment_size = res;
    229 	else
    230 		data->fragment_size = WSC_FRAGMENT_SIZE;
    231 	wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment size limit %u",
    232 		   (unsigned int) data->fragment_size);
    233 
    234 	if (registrar && cfg.pin) {
    235 		wps_registrar_add_pin(data->wps_ctx->registrar, NULL, NULL,
    236 				      cfg.pin, cfg.pin_len, 0);
    237 	}
    238 
    239 	/* Use reduced client timeout for WPS to avoid long wait */
    240 	if (sm->ClientTimeout > 30)
    241 		sm->ClientTimeout = 30;
    242 
    243 	return data;
    244 }
    245 
    246 
    247 static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
    248 {
    249 	struct eap_wsc_data *data = priv;
    250 	wpabuf_free(data->in_buf);
    251 	wpabuf_free(data->out_buf);
    252 	wps_deinit(data->wps);
    253 	os_free(data->wps_ctx->network_key);
    254 	data->wps_ctx->network_key = NULL;
    255 	os_free(data);
    256 }
    257 
    258 
    259 static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
    260 					 struct eap_method_ret *ret, u8 id)
    261 {
    262 	struct wpabuf *resp;
    263 	u8 flags;
    264 	size_t send_len, plen;
    265 
    266 	ret->ignore = FALSE;
    267 	wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
    268 	ret->allowNotifications = TRUE;
    269 
    270 	flags = 0;
    271 	send_len = wpabuf_len(data->out_buf) - data->out_used;
    272 	if (2 + send_len > data->fragment_size) {
    273 		send_len = data->fragment_size - 2;
    274 		flags |= WSC_FLAGS_MF;
    275 		if (data->out_used == 0) {
    276 			flags |= WSC_FLAGS_LF;
    277 			send_len -= 2;
    278 		}
    279 	}
    280 	plen = 2 + send_len;
    281 	if (flags & WSC_FLAGS_LF)
    282 		plen += 2;
    283 	resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
    284 			     EAP_CODE_RESPONSE, id);
    285 	if (resp == NULL)
    286 		return NULL;
    287 
    288 	wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
    289 	wpabuf_put_u8(resp, flags); /* Flags */
    290 	if (flags & WSC_FLAGS_LF)
    291 		wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
    292 
    293 	wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
    294 			send_len);
    295 	data->out_used += send_len;
    296 
    297 	ret->methodState = METHOD_MAY_CONT;
    298 	ret->decision = DECISION_FAIL;
    299 
    300 	if (data->out_used == wpabuf_len(data->out_buf)) {
    301 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
    302 			   "(message sent completely)",
    303 			   (unsigned long) send_len);
    304 		wpabuf_free(data->out_buf);
    305 		data->out_buf = NULL;
    306 		data->out_used = 0;
    307 		if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
    308 		    data->out_op_code == WSC_NACK ||
    309 		    data->out_op_code == WSC_Done) {
    310 			eap_wsc_state(data, FAIL);
    311 			ret->methodState = METHOD_DONE;
    312 		} else
    313 			eap_wsc_state(data, MESG);
    314 	} else {
    315 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
    316 			   "(%lu more to send)", (unsigned long) send_len,
    317 			   (unsigned long) wpabuf_len(data->out_buf) -
    318 			   data->out_used);
    319 		eap_wsc_state(data, WAIT_FRAG_ACK);
    320 	}
    321 
    322 	return resp;
    323 }
    324 
    325 
    326 static int eap_wsc_process_cont(struct eap_wsc_data *data,
    327 				const u8 *buf, size_t len, u8 op_code)
    328 {
    329 	/* Process continuation of a pending message */
    330 	if (op_code != data->in_op_code) {
    331 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
    332 			   "fragment (expected %d)",
    333 			   op_code, data->in_op_code);
    334 		return -1;
    335 	}
    336 
    337 	if (len > wpabuf_tailroom(data->in_buf)) {
    338 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
    339 		eap_wsc_state(data, FAIL);
    340 		return -1;
    341 	}
    342 
    343 	wpabuf_put_data(data->in_buf, buf, len);
    344 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
    345 		   "for %lu bytes more", (unsigned long) len,
    346 		   (unsigned long) wpabuf_tailroom(data->in_buf));
    347 
    348 	return 0;
    349 }
    350 
    351 
    352 static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
    353 						struct eap_method_ret *ret,
    354 						u8 id, u8 flags, u8 op_code,
    355 						u16 message_length,
    356 						const u8 *buf, size_t len)
    357 {
    358 	/* Process a fragment that is not the last one of the message */
    359 	if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
    360 		wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
    361 			   "fragmented packet");
    362 		ret->ignore = TRUE;
    363 		return NULL;
    364 	}
    365 
    366 	if (data->in_buf == NULL) {
    367 		/* First fragment of the message */
    368 		data->in_buf = wpabuf_alloc(message_length);
    369 		if (data->in_buf == NULL) {
    370 			wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
    371 				   "message");
    372 			ret->ignore = TRUE;
    373 			return NULL;
    374 		}
    375 		data->in_op_code = op_code;
    376 		wpabuf_put_data(data->in_buf, buf, len);
    377 		wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
    378 			   "fragment, waiting for %lu bytes more",
    379 			   (unsigned long) len,
    380 			   (unsigned long) wpabuf_tailroom(data->in_buf));
    381 	}
    382 
    383 	return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
    384 }
    385 
    386 
    387 static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
    388 				       struct eap_method_ret *ret,
    389 				       const struct wpabuf *reqData)
    390 {
    391 	struct eap_wsc_data *data = priv;
    392 	const u8 *start, *pos, *end;
    393 	size_t len;
    394 	u8 op_code, flags, id;
    395 	u16 message_length = 0;
    396 	enum wps_process_res res;
    397 	struct wpabuf tmpbuf;
    398 	struct wpabuf *r;
    399 
    400 	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
    401 			       &len);
    402 	if (pos == NULL || len < 2) {
    403 		ret->ignore = TRUE;
    404 		return NULL;
    405 	}
    406 
    407 	id = eap_get_id(reqData);
    408 
    409 	start = pos;
    410 	end = start + len;
    411 
    412 	op_code = *pos++;
    413 	flags = *pos++;
    414 	if (flags & WSC_FLAGS_LF) {
    415 		if (end - pos < 2) {
    416 			wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
    417 			ret->ignore = TRUE;
    418 			return NULL;
    419 		}
    420 		message_length = WPA_GET_BE16(pos);
    421 		pos += 2;
    422 
    423 		if (message_length < end - pos) {
    424 			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
    425 				   "Length");
    426 			ret->ignore = TRUE;
    427 			return NULL;
    428 		}
    429 	}
    430 
    431 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
    432 		   "Flags 0x%x Message Length %d",
    433 		   op_code, flags, message_length);
    434 
    435 	if (data->state == WAIT_FRAG_ACK) {
    436 		if (op_code != WSC_FRAG_ACK) {
    437 			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
    438 				   "in WAIT_FRAG_ACK state", op_code);
    439 			ret->ignore = TRUE;
    440 			return NULL;
    441 		}
    442 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
    443 		eap_wsc_state(data, MESG);
    444 		return eap_wsc_build_msg(data, ret, id);
    445 	}
    446 
    447 	if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
    448 	    op_code != WSC_Done && op_code != WSC_Start) {
    449 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
    450 			   op_code);
    451 		ret->ignore = TRUE;
    452 		return NULL;
    453 	}
    454 
    455 	if (data->state == WAIT_START) {
    456 		if (op_code != WSC_Start) {
    457 			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
    458 				   "in WAIT_START state", op_code);
    459 			ret->ignore = TRUE;
    460 			return NULL;
    461 		}
    462 		wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
    463 		eap_wsc_state(data, MESG);
    464 		/* Start message has empty payload, skip processing */
    465 		goto send_msg;
    466 	} else if (op_code == WSC_Start) {
    467 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
    468 			   op_code);
    469 		ret->ignore = TRUE;
    470 		return NULL;
    471 	}
    472 
    473 	if (data->in_buf &&
    474 	    eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
    475 		ret->ignore = TRUE;
    476 		return NULL;
    477 	}
    478 
    479 	if (flags & WSC_FLAGS_MF) {
    480 		return eap_wsc_process_fragment(data, ret, id, flags, op_code,
    481 						message_length, pos,
    482 						end - pos);
    483 	}
    484 
    485 	if (data->in_buf == NULL) {
    486 		/* Wrap unfragmented messages as wpabuf without extra copy */
    487 		wpabuf_set(&tmpbuf, pos, end - pos);
    488 		data->in_buf = &tmpbuf;
    489 	}
    490 
    491 	res = wps_process_msg(data->wps, op_code, data->in_buf);
    492 	switch (res) {
    493 	case WPS_DONE:
    494 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
    495 			   "successfully - wait for EAP failure");
    496 		eap_wsc_state(data, FAIL);
    497 		break;
    498 	case WPS_CONTINUE:
    499 		eap_wsc_state(data, MESG);
    500 		break;
    501 	case WPS_FAILURE:
    502 	case WPS_PENDING:
    503 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
    504 		eap_wsc_state(data, FAIL);
    505 		break;
    506 	}
    507 
    508 	if (data->in_buf != &tmpbuf)
    509 		wpabuf_free(data->in_buf);
    510 	data->in_buf = NULL;
    511 
    512 send_msg:
    513 	if (data->out_buf == NULL) {
    514 		data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
    515 		if (data->out_buf == NULL) {
    516 			wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
    517 				   "message from WPS");
    518 			return NULL;
    519 		}
    520 		data->out_used = 0;
    521 	}
    522 
    523 	eap_wsc_state(data, MESG);
    524 	r = eap_wsc_build_msg(data, ret, id);
    525 	if (data->state == FAIL && ret->methodState == METHOD_DONE) {
    526 		/* Use reduced client timeout for WPS to avoid long wait */
    527 		if (sm->ClientTimeout > 2)
    528 			sm->ClientTimeout = 2;
    529 	}
    530 	return r;
    531 }
    532 
    533 
    534 int eap_peer_wsc_register(void)
    535 {
    536 	struct eap_method *eap;
    537 	int ret;
    538 
    539 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
    540 				    EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
    541 				    "WSC");
    542 	if (eap == NULL)
    543 		return -1;
    544 
    545 	eap->init = eap_wsc_init;
    546 	eap->deinit = eap_wsc_deinit;
    547 	eap->process = eap_wsc_process;
    548 
    549 	ret = eap_peer_method_register(eap);
    550 	if (ret)
    551 		eap_peer_method_free(eap);
    552 	return ret;
    553 }
    554