6dacfc6af5a0d217750b14810399caafaf292600
[strongswan.git] / src / libtnccs / plugins / tnccs_20 / batch / pb_tnc_batch.c
1 /*
2 * Copyright (C) 2010 Sansar Choinyanbuu
3 * Copyright (C) 2010-2012 Andreas Steffen
4 * HSR Hochschule fuer Technik Rapperswil
5 *
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>.
10 *
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
14 * for more details.
15 */
16
17 #include "pb_tnc_batch.h"
18 #include "messages/ietf/pb_error_msg.h"
19 #include "state_machine/pb_tnc_state_machine.h"
20
21 #include <tnc/tnccs/tnccs.h>
22
23 #include <collections/linked_list.h>
24 #include <bio/bio_writer.h>
25 #include <bio/bio_reader.h>
26 #include <pen/pen.h>
27 #include <utils/debug.h>
28
29 ENUM(pb_tnc_batch_type_names, PB_BATCH_CDATA, PB_BATCH_CLOSE,
30 "CDATA",
31 "SDATA",
32 "RESULT",
33 "CRETRY",
34 "SRETRY",
35 "CLOSE"
36 );
37
38 typedef struct private_pb_tnc_batch_t private_pb_tnc_batch_t;
39
40 /**
41 * PB-Batch Header (see section 4.1 of RFC 5793)
42 *
43 * 0 1 2 3
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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48 * | Batch Length |
49 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
50 */
51
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
55
56 /**
57 * PB-TNC Message (see section 4.2 of RFC 5793)
58 *
59 * 0 1 2 3
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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70 */
71
72 #define PB_TNC_FLAG_NONE 0x00
73 #define PB_TNC_FLAG_NOSKIP (1<<7)
74 #define PB_TNC_HEADER_SIZE 12
75
76 #define PB_TNC_RESERVED_MSG_TYPE 0xffffffff
77
78 /**
79 * Private data of a pb_tnc_batch_t object.
80 *
81 */
82 struct private_pb_tnc_batch_t {
83 /**
84 * Public pb_pa_msg_t interface.
85 */
86 pb_tnc_batch_t public;
87
88 /**
89 * TNCC if TRUE, TNCS if FALSE
90 */
91 bool is_server;
92
93 /**
94 * PB-TNC Batch type
95 */
96 pb_tnc_batch_type_t type;
97
98 /**
99 * Current PB-TNC Batch size
100 */
101 size_t batch_len;
102
103 /**
104 * Maximum PB-TNC Batch size
105 */
106 size_t max_batch_len;
107
108 /**
109 * linked list of PB-TNC messages
110 */
111 linked_list_t *messages;
112
113 /**
114 * linked list of PB-TNC error messages
115 */
116 linked_list_t *errors;
117
118 /**
119 * Encoded message
120 */
121 chunk_t encoding;
122
123 /**
124 * Offset into encoding (used for error reporting)
125 */
126 u_int32_t offset;
127 };
128
129 METHOD(pb_tnc_batch_t, get_type, pb_tnc_batch_type_t,
130 private_pb_tnc_batch_t *this)
131 {
132 return this->type;
133 }
134
135 METHOD(pb_tnc_batch_t, get_encoding, chunk_t,
136 private_pb_tnc_batch_t *this)
137 {
138 return this->encoding;
139 }
140
141 METHOD(pb_tnc_batch_t, add_msg, bool,
142 private_pb_tnc_batch_t *this, pb_tnc_msg_t* msg)
143 {
144 enum_name_t *msg_type_names;
145 chunk_t msg_value;
146 pen_type_t msg_type;
147 size_t msg_len;
148
149 msg->build(msg);
150 msg_value = msg->get_encoding(msg);
151 msg_len = PB_TNC_HEADER_SIZE + msg_value.len;
152
153 if (this->batch_len + msg_len > this->max_batch_len)
154 {
155 /* message just does not fit into this batch */
156 return FALSE;
157 }
158 this->batch_len += msg_len;
159
160 msg_type = msg->get_type(msg);
161 switch (msg_type.vendor_id)
162 {
163 default:
164 case PEN_IETF:
165 msg_type_names = pb_tnc_msg_type_names;
166 break;
167 case PEN_TCG:
168 msg_type_names = pb_tnc_tcg_msg_type_names;
169 break;
170 }
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);
174 return TRUE;
175 }
176
177 METHOD(pb_tnc_batch_t, build, void,
178 private_pb_tnc_batch_t *this)
179 {
180 u_int32_t msg_len;
181 chunk_t msg_value;
182 enumerator_t *enumerator;
183 pen_type_t msg_type;
184 pb_tnc_msg_t *msg;
185 pb_tnc_msg_info_t *msg_infos;
186 bio_writer_t *writer;
187
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);
195
196 /* build PB-TNC messages */
197 enumerator = this->messages->create_enumerator(this->messages);
198 while (enumerator->enumerate(enumerator, &msg))
199 {
200 u_int8_t flags = PB_TNC_FLAG_NONE;
201
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)
207 {
208 default:
209 case PEN_IETF:
210 msg_infos = pb_tnc_msg_infos;
211 break;
212 case PEN_TCG:
213 msg_infos = pb_tnc_tcg_msg_infos;
214 break;
215 }
216 if (msg_infos[msg_type.type].has_noskip_flag)
217 {
218 flags |= PB_TNC_FLAG_NOSKIP;
219 }
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);
225 }
226 enumerator->destroy(enumerator);
227
228 this->encoding = writer->extract_buf(writer);
229 writer->destroy(writer);
230 }
231
232 static status_t process_batch_header(private_pb_tnc_batch_t *this,
233 pb_tnc_state_machine_t *state_machine)
234 {
235 bio_reader_t *reader;
236 pb_tnc_msg_t *msg;
237 pb_error_msg_t *err_msg;
238 u_int8_t version, flags, reserved, type;
239 u_int32_t batch_len;
240 bool directionality;
241
242 if (this->encoding.len < PB_TNC_BATCH_HEADER_SIZE)
243 {
244 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC batch header",
245 this->encoding.len);
246 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
247 PB_ERROR_INVALID_PARAMETER, 0);
248 goto fatal;
249 }
250
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);
258
259 /* Version */
260 if (version != PB_TNC_VERSION)
261 {
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);
267 goto fatal;
268 }
269
270 /* Directionality */
271 directionality = (flags & PB_TNC_BATCH_FLAG_D) != PB_TNC_BATCH_FLAG_NONE;
272 if (directionality == this->is_server)
273 {
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);
278 goto fatal;
279 }
280
281 /* Batch Type */
282 this->type = type & 0x0F;
283 if (this->type > PB_BATCH_ROOF)
284 {
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);
288 goto fatal;
289 }
290
291 if (!state_machine->receive_batch(state_machine, this->type))
292 {
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);
297 goto fatal;
298 }
299 DBG1(DBG_TNC, "processing PB-TNC %N batch", pb_tnc_batch_type_names,
300 this->type);
301
302 /* Batch Length */
303 if (this->encoding.len != batch_len)
304 {
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);
309 goto fatal;
310 }
311
312 this->offset = PB_TNC_BATCH_HEADER_SIZE;
313
314 /* Register an empty CDATA batch with the state machine */
315 if (this->type == PB_BATCH_CDATA)
316 {
317 state_machine->set_empty_cdata(state_machine,
318 this->offset == this->encoding.len);
319 }
320 return SUCCESS;
321
322 fatal:
323 this->errors->insert_last(this->errors, msg);
324 return FAILED;
325 }
326
327 static status_t process_tnc_msg(private_pb_tnc_batch_t *this)
328 {
329 bio_reader_t *reader;
330 pb_tnc_msg_t *pb_tnc_msg, *msg;
331 pb_tnc_msg_info_t *msg_infos;
332 u_int8_t flags;
333 u_int32_t vendor_id, msg_type, msg_len, offset;
334 chunk_t data, msg_value;
335 bool noskip_flag;
336 enum_name_t *msg_type_names;
337 pen_type_t msg_pen_type;
338 status_t status;
339
340 data = chunk_skip(this->encoding, this->offset);
341
342 if (data.len < PB_TNC_HEADER_SIZE)
343 {
344 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message header",
345 data.len);
346 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
347 PB_ERROR_INVALID_PARAMETER, this->offset);
348 goto fatal;
349 }
350
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);
357
358 noskip_flag = (flags & PB_TNC_FLAG_NOSKIP) != PB_TNC_FLAG_NONE;
359
360 if (msg_len > data.len)
361 {
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);
365 goto fatal;
366 }
367
368 if (vendor_id == PEN_RESERVED)
369 {
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);
373 goto fatal;
374
375 }
376
377 if (msg_type == PB_TNC_RESERVED_MSG_TYPE)
378 {
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);
383 goto fatal;
384 }
385
386 if (vendor_id == PEN_IETF && msg_type <= PB_MSG_ROOF)
387 {
388 if (msg_type == PB_MSG_EXPERIMENTAL && noskip_flag)
389 {
390 DBG1(DBG_TNC, "reject IETF/PB-Experimental message with "
391 "NOSKIP flag set");
392 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
393 PB_ERROR_UNSUPPORTED_MANDATORY_MSG, this->offset);
394 goto fatal;
395 }
396 msg_type_names = pb_tnc_msg_type_names;
397 msg_infos = pb_tnc_msg_infos;
398 }
399 else if (vendor_id == PEN_IETF && msg_type <= PB_TCG_MSG_ROOF)
400 {
401 msg_type_names = pb_tnc_tcg_msg_type_names;
402 msg_infos = pb_tnc_tcg_msg_infos;
403 }
404 else
405 {
406 if (msg_len < PB_TNC_HEADER_SIZE)
407 {
408 DBG1(DBG_TNC, "%u bytes too small for PB-TNC message length",
409 msg_len);
410 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
411 PB_ERROR_INVALID_PARAMETER, this->offset + 8);
412 goto fatal;
413 }
414
415 if (noskip_flag)
416 {
417 DBG1(DBG_TNC, "reject PB-TNC message (Vendor ID 0x%06x / "
418 "Type 0x%08x)", vendor_id, msg_type);
419 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
420 PB_ERROR_UNSUPPORTED_MANDATORY_MSG, this->offset);
421 goto fatal;
422 }
423 else
424 {
425 DBG1(DBG_TNC, "ignore PB-TNC message (Vendor ID 0x%06x / "
426 "Type 0x%08x)", vendor_id, msg_type);
427 this->offset += msg_len;
428 return SUCCESS;
429 }
430 }
431
432 if (msg_infos[msg_type].has_noskip_flag != TRUE_OR_FALSE &&
433 msg_infos[msg_type].has_noskip_flag != noskip_flag)
434 {
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);
440 goto fatal;
441 }
442
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))
446 {
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);
453 goto fatal;
454 }
455
456 if (msg_infos[msg_type].in_result_batch && this->type != PB_BATCH_RESULT)
457 {
458 if (this->is_server)
459 {
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);
464 goto fatal;
465 }
466 else
467 {
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;
471 return SUCCESS;
472 }
473 }
474
475 DBG2(DBG_TNC, "processing %N/%N message (%u bytes)", pen_names, vendor_id,
476 msg_type_names, msg_type, msg_len);
477 data.len = 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);
481
482 status = pb_tnc_msg->process(pb_tnc_msg, &offset);
483 if (status == FAILED || status == VERIFY_ERROR)
484 {
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);
488 }
489 if (status == FAILED)
490 {
491 pb_tnc_msg->destroy(pb_tnc_msg);
492 return FAILED;
493 }
494 this->messages->insert_last(this->messages, pb_tnc_msg);
495 this->offset += msg_len;
496 return status;
497
498 fatal:
499 this->errors->insert_last(this->errors, msg);
500 return FAILED;
501 }
502
503 METHOD(pb_tnc_batch_t, process, status_t,
504 private_pb_tnc_batch_t *this, pb_tnc_state_machine_t *state_machine)
505 {
506 status_t status;
507
508 status = process_batch_header(this, state_machine);
509 if (status != SUCCESS)
510 {
511 return FAILED;
512 }
513
514 while (this->offset < this->encoding.len)
515 {
516 switch (process_tnc_msg(this))
517 {
518 case FAILED:
519 return FAILED;
520 case VERIFY_ERROR:
521 status = VERIFY_ERROR;
522 break;
523 case SUCCESS:
524 default:
525 break;
526 }
527 }
528 return status;
529 }
530
531 METHOD(pb_tnc_batch_t, create_msg_enumerator, enumerator_t*,
532 private_pb_tnc_batch_t *this)
533 {
534 return this->messages->create_enumerator(this->messages);
535 }
536
537 METHOD(pb_tnc_batch_t, create_error_enumerator, enumerator_t*,
538 private_pb_tnc_batch_t *this)
539 {
540 return this->errors->create_enumerator(this->errors);
541 }
542
543 METHOD(pb_tnc_batch_t, destroy, void,
544 private_pb_tnc_batch_t *this)
545 {
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);
551 free(this);
552 }
553
554 /**
555 * See header
556 */
557 pb_tnc_batch_t* pb_tnc_batch_create(bool is_server, pb_tnc_batch_type_t type,
558 size_t max_batch_len)
559 {
560 private_pb_tnc_batch_t *this;
561
562 INIT(this,
563 .public = {
564 .get_type = _get_type,
565 .get_encoding = _get_encoding,
566 .add_msg = _add_msg,
567 .build = _build,
568 .process = _process,
569 .create_msg_enumerator = _create_msg_enumerator,
570 .create_error_enumerator = _create_error_enumerator,
571 .destroy = _destroy,
572 },
573 .is_server = is_server,
574 .type = type,
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(),
579 );
580
581 DBG2(DBG_TNC, "creating PB-TNC %N batch", pb_tnc_batch_type_names, type);
582
583 return &this->public;
584 }
585
586 /**
587 * See header
588 */
589 pb_tnc_batch_t* pb_tnc_batch_create_from_data(bool is_server, chunk_t data)
590 {
591 private_pb_tnc_batch_t *this;
592
593 INIT(this,
594 .public = {
595 .get_type = _get_type,
596 .get_encoding = _get_encoding,
597 .add_msg = _add_msg,
598 .build = _build,
599 .process = _process,
600 .create_msg_enumerator = _create_msg_enumerator,
601 .create_error_enumerator = _create_error_enumerator,
602 .destroy = _destroy,
603 },
604 .is_server = is_server,
605 .messages = linked_list_create(),
606 .errors = linked_list_create(),
607 .encoding = chunk_clone(data),
608 );
609
610 return &this->public;
611 }
612