Home | History | Annotate | Download | only in eap_peer
      1 /*
      2  * EAP-WSC peer for Wi-Fi Protected Setup
      3  * Copyright (c) 2007-2008, 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 void * eap_wsc_init(struct eap_sm *sm)
     69 {
     70 	struct eap_wsc_data *data;
     71 	const u8 *identity;
     72 	size_t identity_len;
     73 	int registrar;
     74 	struct wps_config cfg;
     75 	const char *pos;
     76 	const char *phase1;
     77 	struct wps_context *wps;
     78 
     79 	wps = sm->wps;
     80 	if (wps == NULL) {
     81 		wpa_printf(MSG_ERROR, "EAP-WSC: WPS context not available");
     82 		return NULL;
     83 	}
     84 
     85 	identity = eap_get_config_identity(sm, &identity_len);
     86 
     87 	if (identity && identity_len == WSC_ID_REGISTRAR_LEN &&
     88 	    os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0)
     89 		registrar = 1; /* Supplicant is Registrar */
     90 	else if (identity && identity_len == WSC_ID_ENROLLEE_LEN &&
     91 	    os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0)
     92 		registrar = 0; /* Supplicant is Enrollee */
     93 	else {
     94 		wpa_hexdump_ascii(MSG_INFO, "EAP-WSC: Unexpected identity",
     95 				  identity, identity_len);
     96 		return NULL;
     97 	}
     98 
     99 	data = os_zalloc(sizeof(*data));
    100 	if (data == NULL)
    101 		return NULL;
    102 	data->state = registrar ? MESG : WAIT_START;
    103 	data->registrar = registrar;
    104 	data->wps_ctx = wps;
    105 
    106 	os_memset(&cfg, 0, sizeof(cfg));
    107 	cfg.wps = wps;
    108 	cfg.registrar = registrar;
    109 
    110 	phase1 = eap_get_config_phase1(sm);
    111 	if (phase1 == NULL) {
    112 		wpa_printf(MSG_INFO, "EAP-WSC: phase1 configuration data not "
    113 			   "set");
    114 		os_free(data);
    115 		return NULL;
    116 	}
    117 
    118 	pos = os_strstr(phase1, "pin=");
    119 	if (pos) {
    120 		pos += 4;
    121 		cfg.pin = (const u8 *) pos;
    122 		while (*pos != '\0' && *pos != ' ')
    123 			pos++;
    124 		cfg.pin_len = pos - (const char *) cfg.pin;
    125 	} else {
    126 		pos = os_strstr(phase1, "pbc=1");
    127 		if (pos)
    128 			cfg.pbc = 1;
    129 	}
    130 
    131 	if (cfg.pin == NULL && !cfg.pbc) {
    132 		wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
    133 			   "configuration data");
    134 		os_free(data);
    135 		return NULL;
    136 	}
    137 
    138 	data->wps = wps_init(&cfg);
    139 	if (data->wps == NULL) {
    140 		os_free(data);
    141 		return NULL;
    142 	}
    143 	data->fragment_size = WSC_FRAGMENT_SIZE;
    144 
    145 	if (registrar && cfg.pin) {
    146 		wps_registrar_add_pin(data->wps_ctx->registrar, NULL,
    147 				      cfg.pin, cfg.pin_len, 0);
    148 	}
    149 
    150 	return data;
    151 }
    152 
    153 
    154 static void eap_wsc_deinit(struct eap_sm *sm, void *priv)
    155 {
    156 	struct eap_wsc_data *data = priv;
    157 	wpabuf_free(data->in_buf);
    158 	wpabuf_free(data->out_buf);
    159 	wps_deinit(data->wps);
    160 	os_free(data->wps_ctx->network_key);
    161 	data->wps_ctx->network_key = NULL;
    162 	os_free(data);
    163 }
    164 
    165 
    166 static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data,
    167 					 struct eap_method_ret *ret, u8 id)
    168 {
    169 	struct wpabuf *resp;
    170 	u8 flags;
    171 	size_t send_len, plen;
    172 
    173 	ret->ignore = FALSE;
    174 	wpa_printf(MSG_DEBUG, "EAP-WSC: Generating Response");
    175 	ret->allowNotifications = TRUE;
    176 
    177 	flags = 0;
    178 	send_len = wpabuf_len(data->out_buf) - data->out_used;
    179 	if (2 + send_len > data->fragment_size) {
    180 		send_len = data->fragment_size - 2;
    181 		flags |= WSC_FLAGS_MF;
    182 		if (data->out_used == 0) {
    183 			flags |= WSC_FLAGS_LF;
    184 			send_len -= 2;
    185 		}
    186 	}
    187 	plen = 2 + send_len;
    188 	if (flags & WSC_FLAGS_LF)
    189 		plen += 2;
    190 	resp = eap_msg_alloc(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, plen,
    191 			     EAP_CODE_RESPONSE, id);
    192 	if (resp == NULL)
    193 		return NULL;
    194 
    195 	wpabuf_put_u8(resp, data->out_op_code); /* Op-Code */
    196 	wpabuf_put_u8(resp, flags); /* Flags */
    197 	if (flags & WSC_FLAGS_LF)
    198 		wpabuf_put_be16(resp, wpabuf_len(data->out_buf));
    199 
    200 	wpabuf_put_data(resp, wpabuf_head_u8(data->out_buf) + data->out_used,
    201 			send_len);
    202 	data->out_used += send_len;
    203 
    204 	ret->methodState = METHOD_MAY_CONT;
    205 	ret->decision = DECISION_FAIL;
    206 
    207 	if (data->out_used == wpabuf_len(data->out_buf)) {
    208 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
    209 			   "(message sent completely)",
    210 			   (unsigned long) send_len);
    211 		wpabuf_free(data->out_buf);
    212 		data->out_buf = NULL;
    213 		data->out_used = 0;
    214 		if ((data->state == FAIL && data->out_op_code == WSC_ACK) ||
    215 		    data->out_op_code == WSC_NACK ||
    216 		    data->out_op_code == WSC_Done) {
    217 			eap_wsc_state(data, FAIL);
    218 			ret->methodState = METHOD_DONE;
    219 		} else
    220 			eap_wsc_state(data, MESG);
    221 	} else {
    222 		wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes "
    223 			   "(%lu more to send)", (unsigned long) send_len,
    224 			   (unsigned long) wpabuf_len(data->out_buf) -
    225 			   data->out_used);
    226 		eap_wsc_state(data, WAIT_FRAG_ACK);
    227 	}
    228 
    229 	return resp;
    230 }
    231 
    232 
    233 static int eap_wsc_process_cont(struct eap_wsc_data *data,
    234 				const u8 *buf, size_t len, u8 op_code)
    235 {
    236 	/* Process continuation of a pending message */
    237 	if (op_code != data->in_op_code) {
    238 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d in "
    239 			   "fragment (expected %d)",
    240 			   op_code, data->in_op_code);
    241 		return -1;
    242 	}
    243 
    244 	if (len > wpabuf_tailroom(data->in_buf)) {
    245 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment overflow");
    246 		eap_wsc_state(data, FAIL);
    247 		return -1;
    248 	}
    249 
    250 	wpabuf_put_data(data->in_buf, buf, len);
    251 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes, waiting "
    252 		   "for %lu bytes more", (unsigned long) len,
    253 		   (unsigned long) wpabuf_tailroom(data->in_buf));
    254 
    255 	return 0;
    256 }
    257 
    258 
    259 static struct wpabuf * eap_wsc_process_fragment(struct eap_wsc_data *data,
    260 						struct eap_method_ret *ret,
    261 						u8 id, u8 flags, u8 op_code,
    262 						u16 message_length,
    263 						const u8 *buf, size_t len)
    264 {
    265 	/* Process a fragment that is not the last one of the message */
    266 	if (data->in_buf == NULL && !(flags & WSC_FLAGS_LF)) {
    267 		wpa_printf(MSG_DEBUG, "EAP-WSC: No Message Length field in a "
    268 			   "fragmented packet");
    269 		ret->ignore = TRUE;
    270 		return NULL;
    271 	}
    272 
    273 	if (data->in_buf == NULL) {
    274 		/* First fragment of the message */
    275 		data->in_buf = wpabuf_alloc(message_length);
    276 		if (data->in_buf == NULL) {
    277 			wpa_printf(MSG_DEBUG, "EAP-WSC: No memory for "
    278 				   "message");
    279 			ret->ignore = TRUE;
    280 			return NULL;
    281 		}
    282 		data->in_op_code = op_code;
    283 		wpabuf_put_data(data->in_buf, buf, len);
    284 		wpa_printf(MSG_DEBUG, "EAP-WSC: Received %lu bytes in first "
    285 			   "fragment, waiting for %lu bytes more",
    286 			   (unsigned long) len,
    287 			   (unsigned long) wpabuf_tailroom(data->in_buf));
    288 	}
    289 
    290 	return eap_wsc_build_frag_ack(id, EAP_CODE_RESPONSE);
    291 }
    292 
    293 
    294 static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
    295 				       struct eap_method_ret *ret,
    296 				       const struct wpabuf *reqData)
    297 {
    298 	struct eap_wsc_data *data = priv;
    299 	const u8 *start, *pos, *end;
    300 	size_t len;
    301 	u8 op_code, flags, id;
    302 	u16 message_length = 0;
    303 	enum wps_process_res res;
    304 	struct wpabuf tmpbuf;
    305 
    306 	pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData,
    307 			       &len);
    308 	if (pos == NULL || len < 2) {
    309 		ret->ignore = TRUE;
    310 		return NULL;
    311 	}
    312 
    313 	id = eap_get_id(reqData);
    314 
    315 	start = pos;
    316 	end = start + len;
    317 
    318 	op_code = *pos++;
    319 	flags = *pos++;
    320 	if (flags & WSC_FLAGS_LF) {
    321 		if (end - pos < 2) {
    322 			wpa_printf(MSG_DEBUG, "EAP-WSC: Message underflow");
    323 			ret->ignore = TRUE;
    324 			return NULL;
    325 		}
    326 		message_length = WPA_GET_BE16(pos);
    327 		pos += 2;
    328 
    329 		if (message_length < end - pos) {
    330 			wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
    331 				   "Length");
    332 			ret->ignore = TRUE;
    333 			return NULL;
    334 		}
    335 	}
    336 
    337 	wpa_printf(MSG_DEBUG, "EAP-WSC: Received packet: Op-Code %d "
    338 		   "Flags 0x%x Message Length %d",
    339 		   op_code, flags, message_length);
    340 
    341 	if (data->state == WAIT_FRAG_ACK) {
    342 		if (op_code != WSC_FRAG_ACK) {
    343 			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
    344 				   "in WAIT_FRAG_ACK state", op_code);
    345 			ret->ignore = TRUE;
    346 			return NULL;
    347 		}
    348 		wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged");
    349 		eap_wsc_state(data, MESG);
    350 		return eap_wsc_build_msg(data, ret, id);
    351 	}
    352 
    353 	if (op_code != WSC_ACK && op_code != WSC_NACK && op_code != WSC_MSG &&
    354 	    op_code != WSC_Done && op_code != WSC_Start) {
    355 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
    356 			   op_code);
    357 		ret->ignore = TRUE;
    358 		return NULL;
    359 	}
    360 
    361 	if (data->state == WAIT_START) {
    362 		if (op_code != WSC_Start) {
    363 			wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d "
    364 				   "in WAIT_START state", op_code);
    365 			ret->ignore = TRUE;
    366 			return NULL;
    367 		}
    368 		wpa_printf(MSG_DEBUG, "EAP-WSC: Received start");
    369 		eap_wsc_state(data, MESG);
    370 		/* Start message has empty payload, skip processing */
    371 		goto send_msg;
    372 	} else if (op_code == WSC_Start) {
    373 		wpa_printf(MSG_DEBUG, "EAP-WSC: Unexpected Op-Code %d",
    374 			   op_code);
    375 		ret->ignore = TRUE;
    376 		return NULL;
    377 	}
    378 
    379 	if (data->in_buf &&
    380 	    eap_wsc_process_cont(data, pos, end - pos, op_code) < 0) {
    381 		ret->ignore = TRUE;
    382 		return NULL;
    383 	}
    384 
    385 	if (flags & WSC_FLAGS_MF) {
    386 		return eap_wsc_process_fragment(data, ret, id, flags, op_code,
    387 						message_length, pos,
    388 						end - pos);
    389 	}
    390 
    391 	if (data->in_buf == NULL) {
    392 		/* Wrap unfragmented messages as wpabuf without extra copy */
    393 		wpabuf_set(&tmpbuf, pos, end - pos);
    394 		data->in_buf = &tmpbuf;
    395 	}
    396 
    397 	res = wps_process_msg(data->wps, op_code, data->in_buf);
    398 	switch (res) {
    399 	case WPS_DONE:
    400 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing completed "
    401 			   "successfully - wait for EAP failure");
    402 		eap_wsc_state(data, FAIL);
    403 		break;
    404 	case WPS_CONTINUE:
    405 		eap_wsc_state(data, MESG);
    406 		break;
    407 	case WPS_FAILURE:
    408 	case WPS_PENDING:
    409 		wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed");
    410 		eap_wsc_state(data, FAIL);
    411 		break;
    412 	}
    413 
    414 	if (data->in_buf != &tmpbuf)
    415 		wpabuf_free(data->in_buf);
    416 	data->in_buf = NULL;
    417 
    418 send_msg:
    419 	if (data->out_buf == NULL) {
    420 		data->out_buf = wps_get_msg(data->wps, &data->out_op_code);
    421 		if (data->out_buf == NULL) {
    422 			wpa_printf(MSG_DEBUG, "EAP-WSC: Failed to receive "
    423 				   "message from WPS");
    424 			return NULL;
    425 		}
    426 		data->out_used = 0;
    427 	}
    428 
    429 	eap_wsc_state(data, MESG);
    430 	return eap_wsc_build_msg(data, ret, id);
    431 }
    432 
    433 
    434 int eap_peer_wsc_register(void)
    435 {
    436 	struct eap_method *eap;
    437 	int ret;
    438 
    439 	eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
    440 				    EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC,
    441 				    "WSC");
    442 	if (eap == NULL)
    443 		return -1;
    444 
    445 	eap->init = eap_wsc_init;
    446 	eap->deinit = eap_wsc_deinit;
    447 	eap->process = eap_wsc_process;
    448 
    449 	ret = eap_peer_method_register(eap);
    450 	if (ret)
    451 		eap_peer_method_free(eap);
    452 	return ret;
    453 }
    454