2 * Copyright (C) 2010 Sansar Choinyanbuu
3 * Copyright (C) 2010-2015 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 #include "pb_tnc_batch.h"
18 #include "messages/ietf/pb_error_msg.h"
19 #include "messages/ietf/pb_pa_msg.h"
20 #include "state_machine/pb_tnc_state_machine.h"
22 #include <tnc/tnccs/tnccs.h>
24 #include <collections/linked_list.h>
25 #include <bio/bio_writer.h>
26 #include <bio/bio_reader.h>
28 #include <utils/debug.h>
30 ENUM(pb_tnc_batch_type_names
, PB_BATCH_CDATA
, PB_BATCH_CLOSE
,
39 typedef struct private_pb_tnc_batch_t private_pb_tnc_batch_t
;
42 * PB-Batch Header (see section 4.1 of RFC 5793)
45 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
46 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
47 * | Version |D| Reserved | B-Type|
48 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
53 #define PB_TNC_BATCH_FLAG_NONE 0x00
54 #define PB_TNC_BATCH_FLAG_D (1<<7)
57 * PB-TNC Message (see section 4.2 of RFC 5793)
60 * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
61 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62 * | Flags | PB-TNC Vendor ID |
63 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64 * | PB-TNC Message Type |
65 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
66 * | PB-TNC Message Length |
67 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68 * | PB-TNC Message Value (Variable Length) |
69 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72 #define PB_TNC_FLAG_NONE 0x00
73 #define PB_TNC_FLAG_NOSKIP (1<<7)
75 #define PB_TNC_RESERVED_MSG_TYPE 0xffffffff
78 * Private data of a pb_tnc_batch_t object.
81 struct private_pb_tnc_batch_t
{
83 * Public pb_pa_msg_t interface.
85 pb_tnc_batch_t
public;
88 * from TNC server if TRUE, from TNC client if FALSE
95 pb_tnc_batch_type_t type
;
98 * Current PB-TNC Batch size
103 * Maximum PB-TNC Batch size
105 size_t max_batch_len
;
108 * linked list of PB-TNC messages
110 linked_list_t
*messages
;
113 * linked list of PB-TNC error messages
115 linked_list_t
*errors
;
123 * Offset into encoding (used for error reporting)
128 METHOD(pb_tnc_batch_t
, get_type
, pb_tnc_batch_type_t
,
129 private_pb_tnc_batch_t
*this)
134 METHOD(pb_tnc_batch_t
, get_encoding
, chunk_t
,
135 private_pb_tnc_batch_t
*this)
137 return this->encoding
;
140 METHOD(pb_tnc_batch_t
, add_msg
, bool,
141 private_pb_tnc_batch_t
*this, pb_tnc_msg_t
* msg
)
143 enum_name_t
*msg_type_names
;
149 msg_value
= msg
->get_encoding(msg
);
150 msg_len
= PB_TNC_MSG_HEADER_SIZE
+ msg_value
.len
;
152 if (this->batch_len
+ msg_len
> this->max_batch_len
)
154 /* message just does not fit into this batch */
157 this->batch_len
+= msg_len
;
159 msg_type
= msg
->get_type(msg
);
160 switch (msg_type
.vendor_id
)
164 msg_type_names
= pb_tnc_msg_type_names
;
167 msg_type_names
= pb_tnc_tcg_msg_type_names
;
170 msg_type_names
= pb_tnc_ita_msg_type_names
;
173 DBG2(DBG_TNC
, "adding %N/%N message", pen_names
, msg_type
.vendor_id
,
174 msg_type_names
, msg_type
.type
);
175 this->messages
->insert_last(this->messages
, msg
);
179 METHOD(pb_tnc_batch_t
, build
, void,
180 private_pb_tnc_batch_t
*this)
185 enumerator_t
*enumerator
;
188 pb_tnc_msg_info_t
*msg_infos
;
189 bio_writer_t
*writer
;
191 /* Set wrong PB-TNC version for testing purposes to force a PB-TNC error */
192 version
= lib
->settings
->get_int(lib
->settings
,
193 "%s.plugins.tnccs-20.tests.pb_tnc_version",
194 PB_TNC_VERSION
, lib
->ns
);
196 /* build PB-TNC batch header */
197 writer
= bio_writer_create(this->batch_len
);
198 writer
->write_uint8 (writer
, version
);
199 writer
->write_uint8 (writer
, this->is_server
?
200 PB_TNC_BATCH_FLAG_D
: PB_TNC_BATCH_FLAG_NONE
);
201 writer
->write_uint16(writer
, this->type
);
202 writer
->write_uint32(writer
, this->batch_len
);
204 /* build PB-TNC messages */
205 enumerator
= this->messages
->create_enumerator(this->messages
);
206 while (enumerator
->enumerate(enumerator
, &msg
))
208 u_int8_t flags
= PB_TNC_FLAG_NONE
;
210 /* build PB-TNC message */
211 msg_value
= msg
->get_encoding(msg
);
212 msg_len
= PB_TNC_MSG_HEADER_SIZE
+ msg_value
.len
;
213 msg_type
= msg
->get_type(msg
);
214 switch (msg_type
.vendor_id
)
218 msg_infos
= pb_tnc_msg_infos
;
221 msg_infos
= pb_tnc_tcg_msg_infos
;
224 msg_infos
= pb_tnc_ita_msg_infos
;
227 if (msg_infos
[msg_type
.type
].has_noskip_flag
)
229 flags
|= PB_TNC_FLAG_NOSKIP
;
231 writer
->write_uint8 (writer
, flags
);
232 writer
->write_uint24(writer
, msg_type
.vendor_id
);
233 writer
->write_uint32(writer
, msg_type
.type
);
234 writer
->write_uint32(writer
, msg_len
);
235 writer
->write_data (writer
, msg_value
);
237 enumerator
->destroy(enumerator
);
239 this->encoding
= writer
->extract_buf(writer
);
240 writer
->destroy(writer
);
243 METHOD(pb_tnc_batch_t
, process_header
, status_t
,
244 private_pb_tnc_batch_t
*this, bool directionality
, bool is_server
,
247 bio_reader_t
*reader
;
249 pb_error_msg_t
*err_msg
;
250 u_int8_t version
, flags
, reserved
, type
;
253 if (this->encoding
.len
< PB_TNC_BATCH_HEADER_SIZE
)
255 DBG1(DBG_TNC
, "%u bytes insufficient to parse PB-TNC batch header",
257 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
258 PB_ERROR_INVALID_PARAMETER
, 0);
262 reader
= bio_reader_create(this->encoding
);
263 reader
->read_uint8 (reader
, &version
);
264 reader
->read_uint8 (reader
, &flags
);
265 reader
->read_uint8 (reader
, &reserved
);
266 reader
->read_uint8 (reader
, &type
);
267 reader
->read_uint32(reader
, &batch_len
);
268 reader
->destroy(reader
);
271 if (version
!= PB_TNC_VERSION
)
273 DBG1(DBG_TNC
, "unsupported TNCCS batch version 0x%02x", version
);
274 msg
= pb_error_msg_create(TRUE
, PEN_IETF
,
275 PB_ERROR_VERSION_NOT_SUPPORTED
);
276 err_msg
= (pb_error_msg_t
*)msg
;
277 err_msg
->set_bad_version(err_msg
, version
);
282 *from_server
= (flags
& PB_TNC_BATCH_FLAG_D
) != PB_TNC_BATCH_FLAG_NONE
;
284 if (directionality
& (*from_server
== is_server
))
286 DBG1(DBG_TNC
, "wrong Directionality: batch is from a PB-TNC %s",
287 is_server
? "server" : "client");
288 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
289 PB_ERROR_INVALID_PARAMETER
, 1);
294 this->type
= type
& 0x0F;
295 if (this->type
> PB_BATCH_ROOF
)
297 DBG1(DBG_TNC
, "unknown PB-TNC batch type: %d", this->type
);
298 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
299 PB_ERROR_INVALID_PARAMETER
, 3);
304 if (this->encoding
.len
!= batch_len
)
306 DBG1(DBG_TNC
, "%u bytes of data is not equal to batch length of %u bytes",
307 this->encoding
.len
, batch_len
);
308 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
309 PB_ERROR_INVALID_PARAMETER
, 4);
313 this->offset
= PB_TNC_BATCH_HEADER_SIZE
;
318 this->errors
->insert_last(this->errors
, msg
);
322 static status_t
process_tnc_msg(private_pb_tnc_batch_t
*this)
324 bio_reader_t
*reader
;
325 pb_tnc_msg_t
*pb_tnc_msg
, *msg
;
326 pb_tnc_msg_info_t
*msg_infos
;
328 u_int32_t vendor_id
, msg_type
, msg_len
, offset
;
329 chunk_t data
, msg_value
;
331 enum_name_t
*msg_type_names
;
332 pen_type_t msg_pen_type
;
335 data
= chunk_skip(this->encoding
, this->offset
);
337 if (data
.len
< PB_TNC_MSG_HEADER_SIZE
)
339 DBG1(DBG_TNC
, "%u bytes insufficient to parse PB-TNC message header",
341 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
342 PB_ERROR_INVALID_PARAMETER
, this->offset
);
346 reader
= bio_reader_create(data
);
347 reader
->read_uint8 (reader
, &flags
);
348 reader
->read_uint24(reader
, &vendor_id
);
349 reader
->read_uint32(reader
, &msg_type
);
350 reader
->read_uint32(reader
, &msg_len
);
351 reader
->destroy(reader
);
353 noskip_flag
= (flags
& PB_TNC_FLAG_NOSKIP
) != PB_TNC_FLAG_NONE
;
355 if (msg_len
> data
.len
)
357 DBG1(DBG_TNC
, "%u bytes insufficient to parse PB-TNC message", data
.len
);
358 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
359 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 8);
363 if (vendor_id
== PEN_RESERVED
)
365 DBG1(DBG_TNC
, "Vendor ID 0x%06x is reserved", PEN_RESERVED
);
366 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
367 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 1);
372 if (msg_type
== PB_TNC_RESERVED_MSG_TYPE
)
374 DBG1(DBG_TNC
, "PB-TNC message Type 0x%08x is reserved",
375 PB_TNC_RESERVED_MSG_TYPE
);
376 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
377 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 4);
381 if (vendor_id
== PEN_IETF
&& msg_type
<= PB_MSG_ROOF
)
383 if (msg_type
== PB_MSG_EXPERIMENTAL
&& noskip_flag
)
385 DBG1(DBG_TNC
, "reject IETF/PB-Experimental message with "
387 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
388 PB_ERROR_UNSUPPORTED_MANDATORY_MSG
, this->offset
);
391 msg_type_names
= pb_tnc_msg_type_names
;
392 msg_infos
= pb_tnc_msg_infos
;
394 else if (vendor_id
== PEN_TCG
&& msg_type
<= PB_TCG_MSG_ROOF
&&
395 msg_type
> PB_TCG_MSG_RESERVED
)
397 msg_type_names
= pb_tnc_tcg_msg_type_names
;
398 msg_infos
= pb_tnc_tcg_msg_infos
;
400 else if (vendor_id
== PEN_ITA
&& msg_type
<= PB_ITA_MSG_ROOF
&&
401 msg_type
> PB_ITA_MSG_NOSKIP_TEST
)
403 msg_type_names
= pb_tnc_ita_msg_type_names
;
404 msg_infos
= pb_tnc_ita_msg_infos
;
408 if (msg_len
< PB_TNC_MSG_HEADER_SIZE
)
410 DBG1(DBG_TNC
, "%u bytes too small for PB-TNC message length",
412 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
413 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 8);
419 DBG1(DBG_TNC
, "reject PB-TNC message (0x%06x/0x%08x)",
420 vendor_id
, msg_type
);
421 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
422 PB_ERROR_UNSUPPORTED_MANDATORY_MSG
, this->offset
);
427 DBG1(DBG_TNC
, "ignore PB-TNC message (0x%06x/0x%08x)",
428 vendor_id
, msg_type
);
429 this->offset
+= msg_len
;
434 if (msg_infos
[msg_type
].has_noskip_flag
!= TRUE_OR_FALSE
&&
435 msg_infos
[msg_type
].has_noskip_flag
!= noskip_flag
)
437 DBG1(DBG_TNC
, "%N/%N message must%s have NOSKIP flag set",
438 pen_names
, vendor_id
, msg_type_names
, msg_type
,
439 msg_infos
[msg_type
].has_noskip_flag
? "" : " not");
440 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
441 PB_ERROR_INVALID_PARAMETER
, this->offset
);
445 if (msg_len
< msg_infos
[msg_type
].min_size
||
446 (msg_infos
[msg_type
].exact_size
&&
447 msg_len
!= msg_infos
[msg_type
].min_size
))
449 DBG1(DBG_TNC
, "%N/%N message length must be %s %u bytes but is %u bytes",
450 pen_names
, vendor_id
, msg_type_names
, msg_type
,
451 msg_infos
[msg_type
].exact_size
? "exactly" : "at least",
452 msg_infos
[msg_type
].min_size
, msg_len
);
453 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
454 PB_ERROR_INVALID_PARAMETER
, this->offset
);
458 if (msg_infos
[msg_type
].in_result_batch
&& this->type
!= PB_BATCH_RESULT
)
462 DBG1(DBG_TNC
,"reject %N/%N message received from a PB-TNC client",
463 pen_names
, vendor_id
, msg_type_names
, msg_type
);
464 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
465 PB_ERROR_INVALID_PARAMETER
, this->offset
);
470 DBG1(DBG_TNC
,"ignore %N/%N message not received within RESULT batch",
471 pen_names
, vendor_id
, msg_type_names
, msg_type
);
472 this->offset
+= msg_len
;
477 DBG2(DBG_TNC
, "processing %N/%N message (%u bytes)", pen_names
, vendor_id
,
478 msg_type_names
, msg_type
, msg_len
);
480 msg_value
= chunk_skip(data
, PB_TNC_MSG_HEADER_SIZE
);
481 msg_pen_type
= pen_type_create(vendor_id
, msg_type
);
482 pb_tnc_msg
= pb_tnc_msg_create_from_data(msg_pen_type
, msg_value
);
484 status
= pb_tnc_msg
->process(pb_tnc_msg
, &offset
);
485 if (status
== FAILED
|| status
== VERIFY_ERROR
)
487 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
488 PB_ERROR_INVALID_PARAMETER
, this->offset
+ offset
);
489 this->errors
->insert_last(this->errors
, msg
);
491 if (status
== FAILED
)
493 pb_tnc_msg
->destroy(pb_tnc_msg
);
496 this->messages
->insert_last(this->messages
, pb_tnc_msg
);
497 this->offset
+= msg_len
;
501 this->errors
->insert_last(this->errors
, msg
);
505 METHOD(pb_tnc_batch_t
, process
, status_t
,
506 private_pb_tnc_batch_t
*this, pb_tnc_state_machine_t
*state_machine
)
509 status_t status
= SUCCESS
;
511 if (!state_machine
->receive_batch(state_machine
, this->type
))
513 DBG1(DBG_TNC
, "unexpected PB-TNC batch type: %N",
514 pb_tnc_batch_type_names
, this->type
);
515 msg
= pb_error_msg_create(TRUE
, PEN_IETF
,
516 PB_ERROR_UNEXPECTED_BATCH_TYPE
);
517 this->errors
->insert_last(this->errors
, msg
);
521 /* Register an empty CDATA batch with the state machine */
522 if (this->type
== PB_BATCH_CDATA
)
524 state_machine
->set_empty_cdata(state_machine
,
525 this->offset
== this->encoding
.len
);
528 while (this->offset
< this->encoding
.len
)
530 switch (process_tnc_msg(this))
535 status
= VERIFY_ERROR
;
545 METHOD(pb_tnc_batch_t
, create_msg_enumerator
, enumerator_t
*,
546 private_pb_tnc_batch_t
*this)
548 return this->messages
->create_enumerator(this->messages
);
551 METHOD(pb_tnc_batch_t
, create_error_enumerator
, enumerator_t
*,
552 private_pb_tnc_batch_t
*this)
554 return this->errors
->create_enumerator(this->errors
);
557 METHOD(pb_tnc_batch_t
, destroy
, void,
558 private_pb_tnc_batch_t
*this)
560 this->messages
->destroy_offset(this->messages
,
561 offsetof(pb_tnc_msg_t
, destroy
));
562 this->errors
->destroy_offset(this->errors
,
563 offsetof(pb_tnc_msg_t
, destroy
));
564 free(this->encoding
.ptr
);
571 pb_tnc_batch_t
* pb_tnc_batch_create(bool is_server
, pb_tnc_batch_type_t type
,
572 size_t max_batch_len
)
574 private_pb_tnc_batch_t
*this;
578 .get_type
= _get_type
,
579 .get_encoding
= _get_encoding
,
583 .create_msg_enumerator
= _create_msg_enumerator
,
584 .create_error_enumerator
= _create_error_enumerator
,
587 .is_server
= is_server
,
589 .max_batch_len
= max_batch_len
,
590 .batch_len
= PB_TNC_BATCH_HEADER_SIZE
,
591 .messages
= linked_list_create(),
592 .errors
= linked_list_create(),
595 DBG2(DBG_TNC
, "creating PB-TNC %N batch", pb_tnc_batch_type_names
, type
);
597 return &this->public;
603 pb_tnc_batch_t
* pb_tnc_batch_create_from_data(chunk_t data
)
605 private_pb_tnc_batch_t
*this;
609 .get_type
= _get_type
,
610 .get_encoding
= _get_encoding
,
613 .process_header
= _process_header
,
615 .create_msg_enumerator
= _create_msg_enumerator
,
616 .create_error_enumerator
= _create_error_enumerator
,
619 .messages
= linked_list_create(),
620 .errors
= linked_list_create(),
621 .encoding
= chunk_clone(data
),
624 return &this->public;