Home | History | Annotate | Download | only in eap_server
      1 /*
      2  * EAP server method: EAP-TNC (Trusted Network Connect)
      3  * Copyright (c) 2007-2010, 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 "base64.h"
     19 #include "eap_i.h"
     20 #include "tncs.h"
     21 
     22 
     23 struct eap_tnc_data {
     24 	enum eap_tnc_state {
     25 		START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE,
     26 		FAIL
     27 	} state;
     28 	enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation;
     29 	struct tncs_data *tncs;
     30 	struct wpabuf *in_buf;
     31 	struct wpabuf *out_buf;
     32 	size_t out_used;
     33 	size_t fragment_size;
     34 	unsigned int was_done:1;
     35 	unsigned int was_fail:1;
     36 };
     37 
     38 
     39 /* EAP-TNC Flags */
     40 #define EAP_TNC_FLAGS_LENGTH_INCLUDED 0x80
     41 #define EAP_TNC_FLAGS_MORE_FRAGMENTS 0x40
     42 #define EAP_TNC_FLAGS_START 0x20
     43 #define EAP_TNC_VERSION_MASK 0x07
     44 
     45 #define EAP_TNC_VERSION 1
     46 
     47 
     48 static const char * eap_tnc_state_txt(enum eap_tnc_state state)
     49 {
     50 	switch (state) {
     51 	case START:
     52 		return "START";
     53 	case CONTINUE:
     54 		return "CONTINUE";
     55 	case RECOMMENDATION:
     56 		return "RECOMMENDATION";
     57 	case FRAG_ACK:
     58 		return "FRAG_ACK";
     59 	case WAIT_FRAG_ACK:
     60 		return "WAIT_FRAG_ACK";
     61 	case DONE:
     62 		return "DONE";
     63 	case FAIL:
     64 		return "FAIL";
     65 	}
     66 	return "??";
     67 }
     68 
     69 
     70 static void eap_tnc_set_state(struct eap_tnc_data *data,
     71 			      enum eap_tnc_state new_state)
     72 {
     73 	wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s",
     74 		   eap_tnc_state_txt(data->state),
     75 		   eap_tnc_state_txt(new_state));
     76 	data->state = new_state;
     77 }
     78 
     79 
     80 static void * eap_tnc_init(struct eap_sm *sm)
     81 {
     82 	struct eap_tnc_data *data;
     83 
     84 	data = os_zalloc(sizeof(*data));
     85 	if (data == NULL)
     86 		return NULL;
     87 	eap_tnc_set_state(data, START);
     88 	data->tncs = tncs_init();
     89 	if (data->tncs == NULL) {
     90 		os_free(data);
     91 		return NULL;
     92 	}
     93 
     94 	data->fragment_size = sm->fragment_size > 100 ?
     95 		sm->fragment_size - 98 : 1300;
     96 
     97 	return data;
     98 }
     99 
    100 
    101 static void eap_tnc_reset(struct eap_sm *sm, void *priv)
    102 {
    103 	struct eap_tnc_data *data = priv;
    104 	wpabuf_free(data->in_buf);
    105 	wpabuf_free(data->out_buf);
    106 	tncs_deinit(data->tncs);
    107 	os_free(data);
    108 }
    109 
    110 
    111 static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm,
    112 					   struct eap_tnc_data *data, u8 id)
    113 {
    114 	struct wpabuf *req;
    115 
    116 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, EAP_CODE_REQUEST,
    117 			    id);
    118 	if (req == NULL) {
    119 		wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for "
    120 			   "request");
    121 		eap_tnc_set_state(data, FAIL);
    122 		return NULL;
    123 	}
    124 
    125 	wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION);
    126 
    127 	eap_tnc_set_state(data, CONTINUE);
    128 
    129 	return req;
    130 }
    131 
    132 
    133 static struct wpabuf * eap_tnc_build(struct eap_sm *sm,
    134 				     struct eap_tnc_data *data)
    135 {
    136 	struct wpabuf *req;
    137 	u8 *rpos, *rpos1;
    138 	size_t rlen;
    139 	char *start_buf, *end_buf;
    140 	size_t start_len, end_len;
    141 	size_t imv_len;
    142 
    143 	imv_len = tncs_total_send_len(data->tncs);
    144 
    145 	start_buf = tncs_if_tnccs_start(data->tncs);
    146 	if (start_buf == NULL)
    147 		return NULL;
    148 	start_len = os_strlen(start_buf);
    149 	end_buf = tncs_if_tnccs_end();
    150 	if (end_buf == NULL) {
    151 		os_free(start_buf);
    152 		return NULL;
    153 	}
    154 	end_len = os_strlen(end_buf);
    155 
    156 	rlen = start_len + imv_len + end_len;
    157 	req = wpabuf_alloc(rlen);
    158 	if (req == NULL) {
    159 		os_free(start_buf);
    160 		os_free(end_buf);
    161 		return NULL;
    162 	}
    163 
    164 	wpabuf_put_data(req, start_buf, start_len);
    165 	os_free(start_buf);
    166 
    167 	rpos1 = wpabuf_put(req, 0);
    168 	rpos = tncs_copy_send_buf(data->tncs, rpos1);
    169 	wpabuf_put(req, rpos - rpos1);
    170 
    171 	wpabuf_put_data(req, end_buf, end_len);
    172 	os_free(end_buf);
    173 
    174 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Request",
    175 			  wpabuf_head(req), wpabuf_len(req));
    176 
    177 	return req;
    178 }
    179 
    180 
    181 static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm,
    182 						    struct eap_tnc_data *data)
    183 {
    184 	switch (data->recommendation) {
    185 	case ALLOW:
    186 		eap_tnc_set_state(data, DONE);
    187 		break;
    188 	case ISOLATE:
    189 		eap_tnc_set_state(data, FAIL);
    190 		/* TODO: support assignment to a different VLAN */
    191 		break;
    192 	case NO_ACCESS:
    193 		eap_tnc_set_state(data, FAIL);
    194 		break;
    195 	case NO_RECOMMENDATION:
    196 		eap_tnc_set_state(data, DONE);
    197 		break;
    198 	default:
    199 		wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation");
    200 		return NULL;
    201 	}
    202 
    203 	return eap_tnc_build(sm, data);
    204 }
    205 
    206 
    207 static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code)
    208 {
    209 	struct wpabuf *msg;
    210 
    211 	msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id);
    212 	if (msg == NULL) {
    213 		wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory "
    214 			   "for fragment ack");
    215 		return NULL;
    216 	}
    217 	wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */
    218 
    219 	wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack");
    220 
    221 	return msg;
    222 }
    223 
    224 
    225 static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id)
    226 {
    227 	struct wpabuf *req;
    228 	u8 flags;
    229 	size_t send_len, plen;
    230 
    231 	wpa_printf(MSG_DEBUG, "EAP-TNC: Generating Request");
    232 
    233 	flags = EAP_TNC_VERSION;
    234 	send_len = wpabuf_len(data->out_buf) - data->out_used;
    235 	if (1 + send_len > data->fragment_size) {
    236 		send_len = data->fragment_size - 1;
    237 		flags |= EAP_TNC_FLAGS_MORE_FRAGMENTS;
    238 		if (data->out_used == 0) {
    239 			flags |= EAP_TNC_FLAGS_LENGTH_INCLUDED;
    240 			send_len -= 4;
    241 		}
    242 	}
    243 
    244 	plen = 1 + send_len;
    245 	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
    246 		plen += 4;
    247 	req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, plen,
    248 			    EAP_CODE_REQUEST, id);
    249 	if (req == NULL)
    250 		return NULL;
    251 
    252 	wpabuf_put_u8(req, flags); /* Flags */
    253 	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)
    254 		wpabuf_put_be32(req, wpabuf_len(data->out_buf));
    255 
    256 	wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used,
    257 			send_len);
    258 	data->out_used += send_len;
    259 
    260 	if (data->out_used == wpabuf_len(data->out_buf)) {
    261 		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
    262 			   "(message sent completely)",
    263 			   (unsigned long) send_len);
    264 		wpabuf_free(data->out_buf);
    265 		data->out_buf = NULL;
    266 		data->out_used = 0;
    267 		if (data->was_fail)
    268 			eap_tnc_set_state(data, FAIL);
    269 		else if (data->was_done)
    270 			eap_tnc_set_state(data, DONE);
    271 	} else {
    272 		wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes "
    273 			   "(%lu more to send)", (unsigned long) send_len,
    274 			   (unsigned long) wpabuf_len(data->out_buf) -
    275 			   data->out_used);
    276 		if (data->state == FAIL)
    277 			data->was_fail = 1;
    278 		else if (data->state == DONE)
    279 			data->was_done = 1;
    280 		eap_tnc_set_state(data, WAIT_FRAG_ACK);
    281 	}
    282 
    283 	return req;
    284 }
    285 
    286 
    287 static struct wpabuf * eap_tnc_buildReq(struct eap_sm *sm, void *priv, u8 id)
    288 {
    289 	struct eap_tnc_data *data = priv;
    290 
    291 	switch (data->state) {
    292 	case START:
    293 		tncs_init_connection(data->tncs);
    294 		return eap_tnc_build_start(sm, data, id);
    295 	case CONTINUE:
    296 		if (data->out_buf == NULL) {
    297 			data->out_buf = eap_tnc_build(sm, data);
    298 			if (data->out_buf == NULL) {
    299 				wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
    300 					   "generate message");
    301 				return NULL;
    302 			}
    303 			data->out_used = 0;
    304 		}
    305 		return eap_tnc_build_msg(data, id);
    306 	case RECOMMENDATION:
    307 		if (data->out_buf == NULL) {
    308 			data->out_buf = eap_tnc_build_recommendation(sm, data);
    309 			if (data->out_buf == NULL) {
    310 				wpa_printf(MSG_DEBUG, "EAP-TNC: Failed to "
    311 					   "generate recommendation message");
    312 				return NULL;
    313 			}
    314 			data->out_used = 0;
    315 		}
    316 		return eap_tnc_build_msg(data, id);
    317 	case WAIT_FRAG_ACK:
    318 		return eap_tnc_build_msg(data, id);
    319 	case FRAG_ACK:
    320 		return eap_tnc_build_frag_ack(id, EAP_CODE_REQUEST);
    321 	case DONE:
    322 	case FAIL:
    323 		return NULL;
    324 	}
    325 
    326 	return NULL;
    327 }
    328 
    329 
    330 static Boolean eap_tnc_check(struct eap_sm *sm, void *priv,
    331 			     struct wpabuf *respData)
    332 {
    333 	struct eap_tnc_data *data = priv;
    334 	const u8 *pos;
    335 	size_t len;
    336 
    337 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData,
    338 			       &len);
    339 	if (pos == NULL) {
    340 		wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame");
    341 		return TRUE;
    342 	}
    343 
    344 	if (len == 0 && data->state != WAIT_FRAG_ACK) {
    345 		wpa_printf(MSG_INFO, "EAP-TNC: Invalid frame (empty)");
    346 		return TRUE;
    347 	}
    348 
    349 	if (len == 0)
    350 		return FALSE; /* Fragment ACK does not include flags */
    351 
    352 	if ((*pos & EAP_TNC_VERSION_MASK) != EAP_TNC_VERSION) {
    353 		wpa_printf(MSG_DEBUG, "EAP-TNC: Unsupported version %d",
    354 			   *pos & EAP_TNC_VERSION_MASK);
    355 		return TRUE;
    356 	}
    357 
    358 	if (*pos & EAP_TNC_FLAGS_START) {
    359 		wpa_printf(MSG_DEBUG, "EAP-TNC: Peer used Start flag");
    360 		return TRUE;
    361 	}
    362 
    363 	return FALSE;
    364 }
    365 
    366 
    367 static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf)
    368 {
    369 	enum tncs_process_res res;
    370 
    371 	res = tncs_process_if_tnccs(data->tncs, wpabuf_head(inbuf),
    372 				    wpabuf_len(inbuf));
    373 	switch (res) {
    374 	case TNCCS_RECOMMENDATION_ALLOW:
    375 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access");
    376 		eap_tnc_set_state(data, RECOMMENDATION);
    377 		data->recommendation = ALLOW;
    378 		break;
    379 	case TNCCS_RECOMMENDATION_NO_RECOMMENDATION:
    380 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation");
    381 		eap_tnc_set_state(data, RECOMMENDATION);
    382 		data->recommendation = NO_RECOMMENDATION;
    383 		break;
    384 	case TNCCS_RECOMMENDATION_ISOLATE:
    385 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation");
    386 		eap_tnc_set_state(data, RECOMMENDATION);
    387 		data->recommendation = ISOLATE;
    388 		break;
    389 	case TNCCS_RECOMMENDATION_NO_ACCESS:
    390 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access");
    391 		eap_tnc_set_state(data, RECOMMENDATION);
    392 		data->recommendation = NO_ACCESS;
    393 		break;
    394 	case TNCCS_PROCESS_ERROR:
    395 		wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error");
    396 		eap_tnc_set_state(data, FAIL);
    397 		break;
    398 	default:
    399 		break;
    400 	}
    401 }
    402 
    403 
    404 static int eap_tnc_process_cont(struct eap_tnc_data *data,
    405 				const u8 *buf, size_t len)
    406 {
    407 	/* Process continuation of a pending message */
    408 	if (len > wpabuf_tailroom(data->in_buf)) {
    409 		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow");
    410 		eap_tnc_set_state(data, FAIL);
    411 		return -1;
    412 	}
    413 
    414 	wpabuf_put_data(data->in_buf, buf, len);
    415 	wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes, waiting for %lu "
    416 		   "bytes more", (unsigned long) len,
    417 		   (unsigned long) wpabuf_tailroom(data->in_buf));
    418 
    419 	return 0;
    420 }
    421 
    422 
    423 static int eap_tnc_process_fragment(struct eap_tnc_data *data,
    424 				    u8 flags, u32 message_length,
    425 				    const u8 *buf, size_t len)
    426 {
    427 	/* Process a fragment that is not the last one of the message */
    428 	if (data->in_buf == NULL && !(flags & EAP_TNC_FLAGS_LENGTH_INCLUDED)) {
    429 		wpa_printf(MSG_DEBUG, "EAP-TNC: No Message Length field in a "
    430 			   "fragmented packet");
    431 		return -1;
    432 	}
    433 
    434 	if (data->in_buf == NULL) {
    435 		/* First fragment of the message */
    436 		data->in_buf = wpabuf_alloc(message_length);
    437 		if (data->in_buf == NULL) {
    438 			wpa_printf(MSG_DEBUG, "EAP-TNC: No memory for "
    439 				   "message");
    440 			return -1;
    441 		}
    442 		wpabuf_put_data(data->in_buf, buf, len);
    443 		wpa_printf(MSG_DEBUG, "EAP-TNC: Received %lu bytes in first "
    444 			   "fragment, waiting for %lu bytes more",
    445 			   (unsigned long) len,
    446 			   (unsigned long) wpabuf_tailroom(data->in_buf));
    447 	}
    448 
    449 	return 0;
    450 }
    451 
    452 
    453 static void eap_tnc_process(struct eap_sm *sm, void *priv,
    454 			    struct wpabuf *respData)
    455 {
    456 	struct eap_tnc_data *data = priv;
    457 	const u8 *pos, *end;
    458 	size_t len;
    459 	u8 flags;
    460 	u32 message_length = 0;
    461 	struct wpabuf tmpbuf;
    462 
    463 	pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_TNC, respData, &len);
    464 	if (pos == NULL)
    465 		return; /* Should not happen; message already verified */
    466 
    467 	end = pos + len;
    468 
    469 	if (len == 1 && (data->state == DONE || data->state == FAIL)) {
    470 		wpa_printf(MSG_DEBUG, "EAP-TNC: Peer acknowledged the last "
    471 			   "message");
    472 		return;
    473 	}
    474 
    475 	if (len == 0) {
    476 		/* fragment ack */
    477 		flags = 0;
    478 	} else
    479 		flags = *pos++;
    480 
    481 	if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) {
    482 		if (end - pos < 4) {
    483 			wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow");
    484 			eap_tnc_set_state(data, FAIL);
    485 			return;
    486 		}
    487 		message_length = WPA_GET_BE32(pos);
    488 		pos += 4;
    489 
    490 		if (message_length < (u32) (end - pos)) {
    491 			wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
    492 				   "Length (%d; %ld remaining in this msg)",
    493 				   message_length, (long) (end - pos));
    494 			eap_tnc_set_state(data, FAIL);
    495 			return;
    496 		}
    497 	}
    498 	wpa_printf(MSG_DEBUG, "EAP-TNC: Received packet: Flags 0x%x "
    499 		   "Message Length %u", flags, message_length);
    500 
    501 	if (data->state == WAIT_FRAG_ACK) {
    502 		if (len > 1) {
    503 			wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload "
    504 				   "in WAIT_FRAG_ACK state");
    505 			eap_tnc_set_state(data, FAIL);
    506 			return;
    507 		}
    508 		wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged");
    509 		eap_tnc_set_state(data, CONTINUE);
    510 		return;
    511 	}
    512 
    513 	if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) {
    514 		eap_tnc_set_state(data, FAIL);
    515 		return;
    516 	}
    517 
    518 	if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) {
    519 		if (eap_tnc_process_fragment(data, flags, message_length,
    520 					     pos, end - pos) < 0)
    521 			eap_tnc_set_state(data, FAIL);
    522 		else
    523 			eap_tnc_set_state(data, FRAG_ACK);
    524 		return;
    525 	} else if (data->state == FRAG_ACK) {
    526 		wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received");
    527 		eap_tnc_set_state(data, CONTINUE);
    528 	}
    529 
    530 	if (data->in_buf == NULL) {
    531 		/* Wrap unfragmented messages as wpabuf without extra copy */
    532 		wpabuf_set(&tmpbuf, pos, end - pos);
    533 		data->in_buf = &tmpbuf;
    534 	}
    535 
    536 	wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-TNC: Received payload",
    537 			  wpabuf_head(data->in_buf), wpabuf_len(data->in_buf));
    538 	tncs_process(data, data->in_buf);
    539 
    540 	if (data->in_buf != &tmpbuf)
    541 		wpabuf_free(data->in_buf);
    542 	data->in_buf = NULL;
    543 }
    544 
    545 
    546 static Boolean eap_tnc_isDone(struct eap_sm *sm, void *priv)
    547 {
    548 	struct eap_tnc_data *data = priv;
    549 	return data->state == DONE || data->state == FAIL;
    550 }
    551 
    552 
    553 static Boolean eap_tnc_isSuccess(struct eap_sm *sm, void *priv)
    554 {
    555 	struct eap_tnc_data *data = priv;
    556 	return data->state == DONE;
    557 }
    558 
    559 
    560 int eap_server_tnc_register(void)
    561 {
    562 	struct eap_method *eap;
    563 	int ret;
    564 
    565 	eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
    566 				      EAP_VENDOR_IETF, EAP_TYPE_TNC, "TNC");
    567 	if (eap == NULL)
    568 		return -1;
    569 
    570 	eap->init = eap_tnc_init;
    571 	eap->reset = eap_tnc_reset;
    572 	eap->buildReq = eap_tnc_buildReq;
    573 	eap->check = eap_tnc_check;
    574 	eap->process = eap_tnc_process;
    575 	eap->isDone = eap_tnc_isDone;
    576 	eap->isSuccess = eap_tnc_isSuccess;
    577 
    578 	ret = eap_server_method_register(eap);
    579 	if (ret)
    580 		eap_server_method_free(eap);
    581 	return ret;
    582 }
    583