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