2 * Copyright (C) 2010 Sansar Choinyanbuu
3 * Copyright (C) 2010-2012 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 "state_machine/pb_tnc_state_machine.h"
21 #include <tnc/tnccs/tnccs.h>
23 #include <collections/linked_list.h>
24 #include <bio/bio_writer.h>
25 #include <bio/bio_reader.h>
27 #include <utils/debug.h>
29 ENUM(pb_tnc_batch_type_names
, PB_BATCH_CDATA
, PB_BATCH_CLOSE
,
38 typedef struct private_pb_tnc_batch_t private_pb_tnc_batch_t
;
41 * PB-Batch Header (see section 4.1 of RFC 5793)
44 * 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
45 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46 * | Version |D| Reserved | B-Type|
47 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52 #define PB_TNC_BATCH_FLAG_NONE 0x00
53 #define PB_TNC_BATCH_FLAG_D (1<<7)
54 #define PB_TNC_BATCH_HEADER_SIZE 8
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)
74 #define PB_TNC_HEADER_SIZE 12
76 #define PB_TNC_RESERVED_MSG_TYPE 0xffffffff
79 * Private data of a pb_tnc_batch_t object.
82 struct private_pb_tnc_batch_t
{
84 * Public pb_pa_msg_t interface.
86 pb_tnc_batch_t
public;
89 * TNCC if TRUE, TNCS if FALSE
96 pb_tnc_batch_type_t type
;
99 * Current PB-TNC Batch size
104 * Maximum PB-TNC Batch size
106 size_t max_batch_len
;
109 * linked list of PB-TNC messages
111 linked_list_t
*messages
;
114 * linked list of PB-TNC error messages
116 linked_list_t
*errors
;
124 * Offset into encoding (used for error reporting)
129 METHOD(pb_tnc_batch_t
, get_type
, pb_tnc_batch_type_t
,
130 private_pb_tnc_batch_t
*this)
135 METHOD(pb_tnc_batch_t
, get_encoding
, chunk_t
,
136 private_pb_tnc_batch_t
*this)
138 return this->encoding
;
141 METHOD(pb_tnc_batch_t
, add_msg
, bool,
142 private_pb_tnc_batch_t
*this, pb_tnc_msg_t
* msg
)
144 enum_name_t
*msg_type_names
;
150 msg_value
= msg
->get_encoding(msg
);
151 msg_len
= PB_TNC_HEADER_SIZE
+ msg_value
.len
;
153 if (this->batch_len
+ msg_len
> this->max_batch_len
)
155 /* message just does not fit into this batch */
158 this->batch_len
+= msg_len
;
160 msg_type
= msg
->get_type(msg
);
161 switch (msg_type
.vendor_id
)
165 msg_type_names
= pb_tnc_msg_type_names
;
168 msg_type_names
= pb_tnc_tcg_msg_type_names
;
171 DBG2(DBG_TNC
, "adding %N/%N message", pen_names
, msg_type
.vendor_id
,
172 msg_type_names
, msg_type
.type
);
173 this->messages
->insert_last(this->messages
, msg
);
177 METHOD(pb_tnc_batch_t
, build
, void,
178 private_pb_tnc_batch_t
*this)
182 enumerator_t
*enumerator
;
185 pb_tnc_msg_info_t
*msg_infos
;
186 bio_writer_t
*writer
;
188 /* build PB-TNC batch header */
189 writer
= bio_writer_create(this->batch_len
);
190 writer
->write_uint8 (writer
, PB_TNC_VERSION
);
191 writer
->write_uint8 (writer
, this->is_server
?
192 PB_TNC_BATCH_FLAG_D
: PB_TNC_BATCH_FLAG_NONE
);
193 writer
->write_uint16(writer
, this->type
);
194 writer
->write_uint32(writer
, this->batch_len
);
196 /* build PB-TNC messages */
197 enumerator
= this->messages
->create_enumerator(this->messages
);
198 while (enumerator
->enumerate(enumerator
, &msg
))
200 u_int8_t flags
= PB_TNC_FLAG_NONE
;
202 /* build PB-TNC message */
203 msg_value
= msg
->get_encoding(msg
);
204 msg_len
= PB_TNC_HEADER_SIZE
+ msg_value
.len
;
205 msg_type
= msg
->get_type(msg
);
206 switch (msg_type
.vendor_id
)
210 msg_infos
= pb_tnc_msg_infos
;
213 msg_infos
= pb_tnc_tcg_msg_infos
;
216 if (msg_infos
[msg_type
.type
].has_noskip_flag
)
218 flags
|= PB_TNC_FLAG_NOSKIP
;
220 writer
->write_uint8 (writer
, flags
);
221 writer
->write_uint24(writer
, msg_type
.vendor_id
);
222 writer
->write_uint32(writer
, msg_type
.type
);
223 writer
->write_uint32(writer
, msg_len
);
224 writer
->write_data (writer
, msg_value
);
226 enumerator
->destroy(enumerator
);
228 this->encoding
= writer
->extract_buf(writer
);
229 writer
->destroy(writer
);
232 static status_t
process_batch_header(private_pb_tnc_batch_t
*this,
233 pb_tnc_state_machine_t
*state_machine
)
235 bio_reader_t
*reader
;
237 pb_error_msg_t
*err_msg
;
238 u_int8_t version
, flags
, reserved
, type
;
242 if (this->encoding
.len
< PB_TNC_BATCH_HEADER_SIZE
)
244 DBG1(DBG_TNC
, "%u bytes insufficient to parse PB-TNC batch header",
246 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
247 PB_ERROR_INVALID_PARAMETER
, 0);
251 reader
= bio_reader_create(this->encoding
);
252 reader
->read_uint8 (reader
, &version
);
253 reader
->read_uint8 (reader
, &flags
);
254 reader
->read_uint8 (reader
, &reserved
);
255 reader
->read_uint8 (reader
, &type
);
256 reader
->read_uint32(reader
, &batch_len
);
257 reader
->destroy(reader
);
260 if (version
!= PB_TNC_VERSION
)
262 DBG1(DBG_TNC
, "unsupported TNCCS batch version 0x%02x", version
);
263 msg
= pb_error_msg_create(TRUE
, PEN_IETF
,
264 PB_ERROR_VERSION_NOT_SUPPORTED
);
265 err_msg
= (pb_error_msg_t
*)msg
;
266 err_msg
->set_bad_version(err_msg
, version
);
271 directionality
= (flags
& PB_TNC_BATCH_FLAG_D
) != PB_TNC_BATCH_FLAG_NONE
;
272 if (directionality
== this->is_server
)
274 DBG1(DBG_TNC
, "wrong Directionality: batch is from a PB %s",
275 directionality
? "server" : "client");
276 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
277 PB_ERROR_INVALID_PARAMETER
, 1);
282 this->type
= type
& 0x0F;
283 if (this->type
> PB_BATCH_ROOF
)
285 DBG1(DBG_TNC
, "unknown PB-TNC batch type: %d", this->type
);
286 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
287 PB_ERROR_INVALID_PARAMETER
, 3);
291 if (!state_machine
->receive_batch(state_machine
, this->type
))
293 DBG1(DBG_TNC
, "unexpected PB-TNC batch type: %N",
294 pb_tnc_batch_type_names
, this->type
);
295 msg
= pb_error_msg_create(TRUE
, PEN_IETF
,
296 PB_ERROR_UNEXPECTED_BATCH_TYPE
);
299 DBG1(DBG_TNC
, "processing PB-TNC %N batch", pb_tnc_batch_type_names
,
303 if (this->encoding
.len
!= batch_len
)
305 DBG1(DBG_TNC
, "%u bytes of data is not equal to batch length of %u bytes",
306 this->encoding
.len
, batch_len
);
307 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
308 PB_ERROR_INVALID_PARAMETER
, 4);
312 this->offset
= PB_TNC_BATCH_HEADER_SIZE
;
314 /* Register an empty CDATA batch with the state machine */
315 if (this->type
== PB_BATCH_CDATA
)
317 state_machine
->set_empty_cdata(state_machine
,
318 this->offset
== this->encoding
.len
);
323 this->errors
->insert_last(this->errors
, msg
);
327 static status_t
process_tnc_msg(private_pb_tnc_batch_t
*this)
329 bio_reader_t
*reader
;
330 pb_tnc_msg_t
*pb_tnc_msg
, *msg
;
331 pb_tnc_msg_info_t
*msg_infos
;
333 u_int32_t vendor_id
, msg_type
, msg_len
, offset
;
334 chunk_t data
, msg_value
;
336 enum_name_t
*msg_type_names
;
337 pen_type_t msg_pen_type
;
340 data
= chunk_skip(this->encoding
, this->offset
);
342 if (data
.len
< PB_TNC_HEADER_SIZE
)
344 DBG1(DBG_TNC
, "%u bytes insufficient to parse PB-TNC message header",
346 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
347 PB_ERROR_INVALID_PARAMETER
, this->offset
);
351 reader
= bio_reader_create(data
);
352 reader
->read_uint8 (reader
, &flags
);
353 reader
->read_uint24(reader
, &vendor_id
);
354 reader
->read_uint32(reader
, &msg_type
);
355 reader
->read_uint32(reader
, &msg_len
);
356 reader
->destroy(reader
);
358 noskip_flag
= (flags
& PB_TNC_FLAG_NOSKIP
) != PB_TNC_FLAG_NONE
;
360 if (msg_len
> data
.len
)
362 DBG1(DBG_TNC
, "%u bytes insufficient to parse PB-TNC message", data
.len
);
363 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
364 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 8);
368 if (vendor_id
== PEN_RESERVED
)
370 DBG1(DBG_TNC
, "Vendor ID 0x%06x is reserved", PEN_RESERVED
);
371 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
372 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 1);
377 if (msg_type
== PB_TNC_RESERVED_MSG_TYPE
)
379 DBG1(DBG_TNC
, "PB-TNC message Type 0x%08x is reserved",
380 PB_TNC_RESERVED_MSG_TYPE
);
381 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
382 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 4);
386 if (vendor_id
== PEN_IETF
&& msg_type
<= PB_MSG_ROOF
)
388 if (msg_type
== PB_MSG_EXPERIMENTAL
&& noskip_flag
)
390 DBG1(DBG_TNC
, "reject IETF/PB-Experimental message with "
392 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
393 PB_ERROR_UNSUPPORTED_MANDATORY_MSG
, this->offset
);
396 msg_type_names
= pb_tnc_msg_type_names
;
397 msg_infos
= pb_tnc_msg_infos
;
399 else if (vendor_id
== PEN_TCG
&& msg_type
<= PB_TCG_MSG_ROOF
)
401 msg_type_names
= pb_tnc_tcg_msg_type_names
;
402 msg_infos
= pb_tnc_tcg_msg_infos
;
406 if (msg_len
< PB_TNC_HEADER_SIZE
)
408 DBG1(DBG_TNC
, "%u bytes too small for PB-TNC message length",
410 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
411 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 8);
417 DBG1(DBG_TNC
, "reject PB-TNC message 0x%06x/0x%08x)",
418 vendor_id
, msg_type
);
419 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
420 PB_ERROR_UNSUPPORTED_MANDATORY_MSG
, this->offset
);
425 DBG1(DBG_TNC
, "ignore PB-TNC message 0x%06x/0x%08x)",
426 vendor_id
, msg_type
);
427 this->offset
+= msg_len
;
432 if (msg_infos
[msg_type
].has_noskip_flag
!= TRUE_OR_FALSE
&&
433 msg_infos
[msg_type
].has_noskip_flag
!= noskip_flag
)
435 DBG1(DBG_TNC
, "%N/%N message must%s have NOSKIP flag set",
436 pen_names
, vendor_id
, msg_type_names
, msg_type
,
437 msg_infos
[msg_type
].has_noskip_flag
? "" : " not");
438 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
439 PB_ERROR_INVALID_PARAMETER
, this->offset
);
443 if (msg_len
< msg_infos
[msg_type
].min_size
||
444 (msg_infos
[msg_type
].exact_size
&&
445 msg_len
!= msg_infos
[msg_type
].min_size
))
447 DBG1(DBG_TNC
, "%N/%N message length must be %s %u bytes but is %u bytes",
448 pen_names
, vendor_id
, msg_type_names
, msg_type
,
449 msg_infos
[msg_type
].exact_size
? "exactly" : "at least",
450 msg_infos
[msg_type
].min_size
, msg_len
);
451 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
452 PB_ERROR_INVALID_PARAMETER
, this->offset
);
456 if (msg_infos
[msg_type
].in_result_batch
&& this->type
!= PB_BATCH_RESULT
)
460 DBG1(DBG_TNC
,"reject %N/%N message received from a PB-TNC client",
461 pen_names
, vendor_id
, msg_type_names
, msg_type
);
462 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
463 PB_ERROR_INVALID_PARAMETER
, this->offset
);
468 DBG1(DBG_TNC
,"ignore %N/%N message not received within RESULT batch",
469 pen_names
, vendor_id
, msg_type_names
, msg_type
);
470 this->offset
+= msg_len
;
475 DBG2(DBG_TNC
, "processing %N/%N message (%u bytes)", pen_names
, vendor_id
,
476 msg_type_names
, msg_type
, msg_len
);
478 msg_value
= chunk_skip(data
, PB_TNC_HEADER_SIZE
);
479 msg_pen_type
= pen_type_create(vendor_id
, msg_type
);
480 pb_tnc_msg
= pb_tnc_msg_create_from_data(msg_pen_type
, msg_value
);
482 status
= pb_tnc_msg
->process(pb_tnc_msg
, &offset
);
483 if (status
== FAILED
|| status
== VERIFY_ERROR
)
485 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
486 PB_ERROR_INVALID_PARAMETER
, this->offset
+ offset
);
487 this->errors
->insert_last(this->errors
, msg
);
489 if (status
== FAILED
)
491 pb_tnc_msg
->destroy(pb_tnc_msg
);
494 this->messages
->insert_last(this->messages
, pb_tnc_msg
);
495 this->offset
+= msg_len
;
499 this->errors
->insert_last(this->errors
, msg
);
503 METHOD(pb_tnc_batch_t
, process
, status_t
,
504 private_pb_tnc_batch_t
*this, pb_tnc_state_machine_t
*state_machine
)
508 status
= process_batch_header(this, state_machine
);
509 if (status
!= SUCCESS
)
514 while (this->offset
< this->encoding
.len
)
516 switch (process_tnc_msg(this))
521 status
= VERIFY_ERROR
;
531 METHOD(pb_tnc_batch_t
, create_msg_enumerator
, enumerator_t
*,
532 private_pb_tnc_batch_t
*this)
534 return this->messages
->create_enumerator(this->messages
);
537 METHOD(pb_tnc_batch_t
, create_error_enumerator
, enumerator_t
*,
538 private_pb_tnc_batch_t
*this)
540 return this->errors
->create_enumerator(this->errors
);
543 METHOD(pb_tnc_batch_t
, destroy
, void,
544 private_pb_tnc_batch_t
*this)
546 this->messages
->destroy_offset(this->messages
,
547 offsetof(pb_tnc_msg_t
, destroy
));
548 this->errors
->destroy_offset(this->errors
,
549 offsetof(pb_tnc_msg_t
, destroy
));
550 free(this->encoding
.ptr
);
557 pb_tnc_batch_t
* pb_tnc_batch_create(bool is_server
, pb_tnc_batch_type_t type
,
558 size_t max_batch_len
)
560 private_pb_tnc_batch_t
*this;
564 .get_type
= _get_type
,
565 .get_encoding
= _get_encoding
,
569 .create_msg_enumerator
= _create_msg_enumerator
,
570 .create_error_enumerator
= _create_error_enumerator
,
573 .is_server
= is_server
,
575 .max_batch_len
= max_batch_len
,
576 .batch_len
= PB_TNC_BATCH_HEADER_SIZE
,
577 .messages
= linked_list_create(),
578 .errors
= linked_list_create(),
581 DBG2(DBG_TNC
, "creating PB-TNC %N batch", pb_tnc_batch_type_names
, type
);
583 return &this->public;
589 pb_tnc_batch_t
* pb_tnc_batch_create_from_data(bool is_server
, chunk_t data
)
591 private_pb_tnc_batch_t
*this;
595 .get_type
= _get_type
,
596 .get_encoding
= _get_encoding
,
600 .create_msg_enumerator
= _create_msg_enumerator
,
601 .create_error_enumerator
= _create_error_enumerator
,
604 .is_server
= is_server
,
605 .messages
= linked_list_create(),
606 .errors
= linked_list_create(),
607 .encoding
= chunk_clone(data
),
610 return &this->public;