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/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
)
148 msg_value
= msg
->get_encoding(msg
);
149 msg_len
= PB_TNC_HEADER_SIZE
+ msg_value
.len
;
151 if (this->batch_len
+ msg_len
> this->max_batch_len
)
153 /* message just does not fit into this batch */
156 this->batch_len
+= msg_len
;
158 DBG2(DBG_TNC
, "adding %N message", pb_tnc_msg_type_names
,
160 this->messages
->insert_last(this->messages
, msg
);
164 METHOD(pb_tnc_batch_t
, build
, void,
165 private_pb_tnc_batch_t
*this)
169 enumerator_t
*enumerator
;
170 pb_tnc_msg_type_t msg_type
;
172 bio_writer_t
*writer
;
174 /* build PB-TNC batch header */
175 writer
= bio_writer_create(this->batch_len
);
176 writer
->write_uint8 (writer
, PB_TNC_VERSION
);
177 writer
->write_uint8 (writer
, this->is_server ?
178 PB_TNC_BATCH_FLAG_D
: PB_TNC_BATCH_FLAG_NONE
);
179 writer
->write_uint16(writer
, this->type
);
180 writer
->write_uint32(writer
, this->batch_len
);
182 /* build PB-TNC messages */
183 enumerator
= this->messages
->create_enumerator(this->messages
);
184 while (enumerator
->enumerate(enumerator
, &msg
))
186 u_int8_t flags
= PB_TNC_FLAG_NONE
;
188 /* build PB-TNC message */
189 msg_value
= msg
->get_encoding(msg
);
190 msg_len
= PB_TNC_HEADER_SIZE
+ msg_value
.len
;
191 msg_type
= msg
->get_type(msg
);
192 if (pb_tnc_msg_infos
[msg_type
].has_noskip_flag
)
194 flags
|= PB_TNC_FLAG_NOSKIP
;
196 writer
->write_uint8 (writer
, flags
);
197 writer
->write_uint24(writer
, PEN_IETF
);
198 writer
->write_uint32(writer
, msg_type
);
199 writer
->write_uint32(writer
, msg_len
);
200 writer
->write_data (writer
, msg_value
);
202 enumerator
->destroy(enumerator
);
204 this->encoding
= writer
->extract_buf(writer
);
205 writer
->destroy(writer
);
208 static status_t
process_batch_header(private_pb_tnc_batch_t
*this,
209 pb_tnc_state_machine_t
*state_machine
)
211 bio_reader_t
*reader
;
213 pb_error_msg_t
*err_msg
;
214 u_int8_t version
, flags
, reserved
, type
;
218 if (this->encoding
.len
< PB_TNC_BATCH_HEADER_SIZE
)
220 DBG1(DBG_TNC
, "%u bytes insufficient to parse PB-TNC batch header",
222 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
223 PB_ERROR_INVALID_PARAMETER
, 0);
227 reader
= bio_reader_create(this->encoding
);
228 reader
->read_uint8 (reader
, &version
);
229 reader
->read_uint8 (reader
, &flags
);
230 reader
->read_uint8 (reader
, &reserved
);
231 reader
->read_uint8 (reader
, &type
);
232 reader
->read_uint32(reader
, &batch_len
);
233 reader
->destroy(reader
);
236 if (version
!= PB_TNC_VERSION
)
238 DBG1(DBG_TNC
, "unsupported TNCCS batch version 0x%02x", version
);
239 msg
= pb_error_msg_create(TRUE
, PEN_IETF
,
240 PB_ERROR_VERSION_NOT_SUPPORTED
);
241 err_msg
= (pb_error_msg_t
*)msg
;
242 err_msg
->set_bad_version(err_msg
, version
);
247 directionality
= (flags
& PB_TNC_BATCH_FLAG_D
) != PB_TNC_BATCH_FLAG_NONE
;
248 if (directionality
== this->is_server
)
250 DBG1(DBG_TNC
, "wrong Directionality: batch is from a PB %s",
251 directionality ?
"server" : "client");
252 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
253 PB_ERROR_INVALID_PARAMETER
, 1);
258 this->type
= type
& 0x0F;
259 if (this->type
> PB_BATCH_ROOF
)
261 DBG1(DBG_TNC
, "unknown PB-TNC batch type: %d", this->type
);
262 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
263 PB_ERROR_INVALID_PARAMETER
, 3);
267 if (!state_machine
->receive_batch(state_machine
, this->type
))
269 DBG1(DBG_TNC
, "unexpected PB-TNC batch type: %N",
270 pb_tnc_batch_type_names
, this->type
);
271 msg
= pb_error_msg_create(TRUE
, PEN_IETF
,
272 PB_ERROR_UNEXPECTED_BATCH_TYPE
);
275 DBG1(DBG_TNC
, "processing PB-TNC %N batch", pb_tnc_batch_type_names
,
279 if (this->encoding
.len
!= batch_len
)
281 DBG1(DBG_TNC
, "%u bytes of data is not equal to batch length of %u bytes",
282 this->encoding
.len
, batch_len
);
283 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
284 PB_ERROR_INVALID_PARAMETER
, 4);
288 this->offset
= PB_TNC_BATCH_HEADER_SIZE
;
290 /* Register an empty CDATA batch with the state machine */
291 if (this->type
== PB_BATCH_CDATA
)
293 state_machine
->set_empty_cdata(state_machine
,
294 this->offset
== this->encoding
.len
);
299 this->errors
->insert_last(this->errors
, msg
);
303 static status_t
process_tnc_msg(private_pb_tnc_batch_t
*this)
305 bio_reader_t
*reader
;
306 pb_tnc_msg_t
*pb_tnc_msg
, *msg
;
308 u_int32_t vendor_id
, msg_type
, msg_len
, offset
;
309 chunk_t data
, msg_value
;
313 data
= chunk_skip(this->encoding
, this->offset
);
315 if (data
.len
< PB_TNC_HEADER_SIZE
)
317 DBG1(DBG_TNC
, "%u bytes insufficient to parse PB-TNC message header",
319 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
320 PB_ERROR_INVALID_PARAMETER
, this->offset
);
324 reader
= bio_reader_create(data
);
325 reader
->read_uint8 (reader
, &flags
);
326 reader
->read_uint24(reader
, &vendor_id
);
327 reader
->read_uint32(reader
, &msg_type
);
328 reader
->read_uint32(reader
, &msg_len
);
329 reader
->destroy(reader
);
331 noskip_flag
= (flags
& PB_TNC_FLAG_NOSKIP
) != PB_TNC_FLAG_NONE
;
333 if (msg_len
> data
.len
)
335 DBG1(DBG_TNC
, "%u bytes insufficient to parse PB-TNC message", data
.len
);
336 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
337 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 8);
341 if (vendor_id
== PEN_RESERVED
)
343 DBG1(DBG_TNC
, "Vendor ID 0x%06x is reserved", PEN_RESERVED
);
344 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
345 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 1);
350 if (msg_type
== PB_TNC_RESERVED_MSG_TYPE
)
352 DBG1(DBG_TNC
, "PB-TNC message Type 0x%08x is reserved",
353 PB_TNC_RESERVED_MSG_TYPE
);
354 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
355 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 4);
360 if (vendor_id
!= PEN_IETF
|| msg_type
> PB_MSG_ROOF
)
362 if (msg_len
< PB_TNC_HEADER_SIZE
)
364 DBG1(DBG_TNC
, "%u bytes too small for PB-TNC message length",
366 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
367 PB_ERROR_INVALID_PARAMETER
, this->offset
+ 8);
373 DBG1(DBG_TNC
, "reject PB-TNC message (Vendor ID 0x%06x / "
374 "Type 0x%08x)", vendor_id
, msg_type
);
375 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
376 PB_ERROR_UNSUPPORTED_MANDATORY_MSG
, this->offset
);
381 DBG1(DBG_TNC
, "ignore PB-TNC message (Vendor ID 0x%06x / "
382 "Type 0x%08x)", vendor_id
, msg_type
);
383 this->offset
+= msg_len
;
389 if (msg_type
== PB_MSG_EXPERIMENTAL
&& noskip_flag
)
391 DBG1(DBG_TNC
, "reject PB-Experimental message with NOSKIP flag set");
392 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
393 PB_ERROR_UNSUPPORTED_MANDATORY_MSG
, this->offset
);
396 if (pb_tnc_msg_infos
[msg_type
].has_noskip_flag
!= TRUE_OR_FALSE
&&
397 pb_tnc_msg_infos
[msg_type
].has_noskip_flag
!= noskip_flag
)
399 DBG1(DBG_TNC
, "%N message must%s have NOSKIP flag set",
400 pb_tnc_msg_type_names
, msg_type
,
401 pb_tnc_msg_infos
[msg_type
].has_noskip_flag ?
"" : " not");
402 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
403 PB_ERROR_INVALID_PARAMETER
, this->offset
);
407 if (msg_len
< pb_tnc_msg_infos
[msg_type
].min_size
||
408 (pb_tnc_msg_infos
[msg_type
].exact_size
&&
409 msg_len
!= pb_tnc_msg_infos
[msg_type
].min_size
))
411 DBG1(DBG_TNC
, "%N message length must be %s %u bytes but is %u bytes",
412 pb_tnc_msg_type_names
, msg_type
,
413 pb_tnc_msg_infos
[msg_type
].exact_size ?
"exactly" : "at least",
414 pb_tnc_msg_infos
[msg_type
].min_size
, msg_len
);
415 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
416 PB_ERROR_INVALID_PARAMETER
, this->offset
);
421 if (pb_tnc_msg_infos
[msg_type
].in_result_batch
&&
422 this->type
!= PB_BATCH_RESULT
)
426 DBG1(DBG_TNC
,"reject %N message received from a PB-TNC client",
427 pb_tnc_msg_type_names
, msg_type
);
428 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
429 PB_ERROR_INVALID_PARAMETER
, this->offset
);
434 DBG1(DBG_TNC
,"ignore %N message not received within RESULT batch",
435 pb_tnc_msg_type_names
, msg_type
);
436 this->offset
+= msg_len
;
441 DBG2(DBG_TNC
, "processing %N message (%u bytes)", pb_tnc_msg_type_names
,
444 msg_value
= chunk_skip(data
, PB_TNC_HEADER_SIZE
);
445 pb_tnc_msg
= pb_tnc_msg_create_from_data(msg_type
, msg_value
);
447 status
= pb_tnc_msg
->process(pb_tnc_msg
, &offset
);
448 if (status
== FAILED
|| status
== VERIFY_ERROR
)
450 msg
= pb_error_msg_create_with_offset(TRUE
, PEN_IETF
,
451 PB_ERROR_INVALID_PARAMETER
, this->offset
+ offset
);
452 this->errors
->insert_last(this->errors
, msg
);
454 if (status
== FAILED
)
456 pb_tnc_msg
->destroy(pb_tnc_msg
);
459 this->messages
->insert_last(this->messages
, pb_tnc_msg
);
460 this->offset
+= msg_len
;
464 this->errors
->insert_last(this->errors
, msg
);
468 METHOD(pb_tnc_batch_t
, process
, status_t
,
469 private_pb_tnc_batch_t
*this, pb_tnc_state_machine_t
*state_machine
)
473 status
= process_batch_header(this, state_machine
);
474 if (status
!= SUCCESS
)
479 while (this->offset
< this->encoding
.len
)
481 switch (process_tnc_msg(this))
486 status
= VERIFY_ERROR
;
496 METHOD(pb_tnc_batch_t
, create_msg_enumerator
, enumerator_t
*,
497 private_pb_tnc_batch_t
*this)
499 return this->messages
->create_enumerator(this->messages
);
502 METHOD(pb_tnc_batch_t
, create_error_enumerator
, enumerator_t
*,
503 private_pb_tnc_batch_t
*this)
505 return this->errors
->create_enumerator(this->errors
);
508 METHOD(pb_tnc_batch_t
, destroy
, void,
509 private_pb_tnc_batch_t
*this)
511 this->messages
->destroy_offset(this->messages
,
512 offsetof(pb_tnc_msg_t
, destroy
));
513 this->errors
->destroy_offset(this->errors
,
514 offsetof(pb_tnc_msg_t
, destroy
));
515 free(this->encoding
.ptr
);
522 pb_tnc_batch_t
* pb_tnc_batch_create(bool is_server
, pb_tnc_batch_type_t type
,
523 size_t max_batch_len
)
525 private_pb_tnc_batch_t
*this;
529 .get_type
= _get_type
,
530 .get_encoding
= _get_encoding
,
534 .create_msg_enumerator
= _create_msg_enumerator
,
535 .create_error_enumerator
= _create_error_enumerator
,
538 .is_server
= is_server
,
540 .max_batch_len
= max_batch_len
,
541 .batch_len
= PB_TNC_BATCH_HEADER_SIZE
,
542 .messages
= linked_list_create(),
543 .errors
= linked_list_create(),
546 DBG2(DBG_TNC
, "creating PB-TNC %N batch", pb_tnc_batch_type_names
, type
);
548 return &this->public;
554 pb_tnc_batch_t
* pb_tnc_batch_create_from_data(bool is_server
, chunk_t data
)
556 private_pb_tnc_batch_t
*this;
560 .get_type
= _get_type
,
561 .get_encoding
= _get_encoding
,
565 .create_msg_enumerator
= _create_msg_enumerator
,
566 .create_error_enumerator
= _create_error_enumerator
,
569 .is_server
= is_server
,
570 .messages
= linked_list_create(),
571 .errors
= linked_list_create(),
572 .encoding
= chunk_clone(data
),
575 return &this->public;