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