Added tnc/tnccs-20-fail-init and tnc/tnccs-20-fail-resp scenarios
[strongswan.git] / src / libtnccs / plugins / tnccs_20 / batch / pb_tnc_batch.c
1 /*
2 * Copyright (C) 2010 Sansar Choinyanbuu
3 * Copyright (C) 2010-2015 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 "messages/ietf/pb_pa_msg.h"
20 #include "state_machine/pb_tnc_state_machine.h"
21
22 #include <tnc/tnccs/tnccs.h>
23
24 #include <collections/linked_list.h>
25 #include <bio/bio_writer.h>
26 #include <bio/bio_reader.h>
27 #include <pen/pen.h>
28 #include <utils/debug.h>
29
30 ENUM(pb_tnc_batch_type_names, PB_BATCH_CDATA, PB_BATCH_CLOSE,
31 "CDATA",
32 "SDATA",
33 "RESULT",
34 "CRETRY",
35 "SRETRY",
36 "CLOSE"
37 );
38
39 typedef struct private_pb_tnc_batch_t private_pb_tnc_batch_t;
40
41 /**
42 * PB-Batch Header (see section 4.1 of RFC 5793)
43 *
44 * 0 1 2 3
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 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
49 * | Batch Length |
50 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
51 */
52
53 #define PB_TNC_BATCH_FLAG_NONE 0x00
54 #define PB_TNC_BATCH_FLAG_D (1<<7)
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
75 #define PB_TNC_RESERVED_MSG_TYPE 0xffffffff
76
77 /**
78 * Private data of a pb_tnc_batch_t object.
79 *
80 */
81 struct private_pb_tnc_batch_t {
82 /**
83 * Public pb_pa_msg_t interface.
84 */
85 pb_tnc_batch_t public;
86
87 /**
88 * from TNC server if TRUE, from TNC client if FALSE
89 */
90 bool is_server;
91
92 /**
93 * PB-TNC Batch type
94 */
95 pb_tnc_batch_type_t type;
96
97 /**
98 * Current PB-TNC Batch size
99 */
100 size_t batch_len;
101
102 /**
103 * Maximum PB-TNC Batch size
104 */
105 size_t max_batch_len;
106
107 /**
108 * linked list of PB-TNC messages
109 */
110 linked_list_t *messages;
111
112 /**
113 * linked list of PB-TNC error messages
114 */
115 linked_list_t *errors;
116
117 /**
118 * Encoded message
119 */
120 chunk_t encoding;
121
122 /**
123 * Offset into encoding (used for error reporting)
124 */
125 u_int32_t offset;
126 };
127
128 METHOD(pb_tnc_batch_t, get_type, pb_tnc_batch_type_t,
129 private_pb_tnc_batch_t *this)
130 {
131 return this->type;
132 }
133
134 METHOD(pb_tnc_batch_t, get_encoding, chunk_t,
135 private_pb_tnc_batch_t *this)
136 {
137 return this->encoding;
138 }
139
140 METHOD(pb_tnc_batch_t, add_msg, bool,
141 private_pb_tnc_batch_t *this, pb_tnc_msg_t* msg)
142 {
143 enum_name_t *msg_type_names;
144 chunk_t msg_value;
145 pen_type_t msg_type;
146 size_t msg_len;
147
148 msg->build(msg);
149 msg_value = msg->get_encoding(msg);
150 msg_len = PB_TNC_MSG_HEADER_SIZE + msg_value.len;
151
152 if (this->batch_len + msg_len > this->max_batch_len)
153 {
154 /* message just does not fit into this batch */
155 return FALSE;
156 }
157 this->batch_len += msg_len;
158
159 msg_type = msg->get_type(msg);
160 switch (msg_type.vendor_id)
161 {
162 default:
163 case PEN_IETF:
164 msg_type_names = pb_tnc_msg_type_names;
165 break;
166 case PEN_TCG:
167 msg_type_names = pb_tnc_tcg_msg_type_names;
168 break;
169 case PEN_ITA:
170 msg_type_names = pb_tnc_ita_msg_type_names;
171 break;
172 }
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);
176 return TRUE;
177 }
178
179 METHOD(pb_tnc_batch_t, build, void,
180 private_pb_tnc_batch_t *this)
181 {
182 u_int8_t version;
183 u_int32_t msg_len;
184 chunk_t msg_value;
185 enumerator_t *enumerator;
186 pen_type_t msg_type;
187 pb_tnc_msg_t *msg;
188 pb_tnc_msg_info_t *msg_infos;
189 bio_writer_t *writer;
190
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);
195
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);
203
204 /* build PB-TNC messages */
205 enumerator = this->messages->create_enumerator(this->messages);
206 while (enumerator->enumerate(enumerator, &msg))
207 {
208 u_int8_t flags = PB_TNC_FLAG_NONE;
209
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)
215 {
216 default:
217 case PEN_IETF:
218 msg_infos = pb_tnc_msg_infos;
219 break;
220 case PEN_TCG:
221 msg_infos = pb_tnc_tcg_msg_infos;
222 break;
223 case PEN_ITA:
224 msg_infos = pb_tnc_ita_msg_infos;
225 break;
226 }
227 if (msg_infos[msg_type.type].has_noskip_flag)
228 {
229 flags |= PB_TNC_FLAG_NOSKIP;
230 }
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);
236 }
237 enumerator->destroy(enumerator);
238
239 this->encoding = writer->extract_buf(writer);
240 writer->destroy(writer);
241 }
242
243 METHOD(pb_tnc_batch_t, process_header, status_t,
244 private_pb_tnc_batch_t *this, bool directionality, bool is_server,
245 bool *from_server)
246 {
247 bio_reader_t *reader;
248 pb_tnc_msg_t *msg;
249 pb_error_msg_t *err_msg;
250 u_int8_t version, flags, reserved, type;
251 u_int32_t batch_len;
252
253 if (this->encoding.len < PB_TNC_BATCH_HEADER_SIZE)
254 {
255 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC batch header",
256 this->encoding.len);
257 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
258 PB_ERROR_INVALID_PARAMETER, 0);
259 goto fatal;
260 }
261
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);
269
270 /* Version */
271 if (version != PB_TNC_VERSION)
272 {
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);
278 goto fatal;
279 }
280
281 /* Directionality */
282 *from_server = (flags & PB_TNC_BATCH_FLAG_D) != PB_TNC_BATCH_FLAG_NONE;
283
284 if (directionality & (*from_server == is_server))
285 {
286 DBG1(DBG_TNC, "wrong Directionality: batch is from a PB %s",
287 is_server ? "server" : "client");
288 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
289 PB_ERROR_INVALID_PARAMETER, 1);
290 goto fatal;
291 }
292
293 /* Batch Type */
294 this->type = type & 0x0F;
295 if (this->type > PB_BATCH_ROOF)
296 {
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);
300 goto fatal;
301 }
302
303 /* Batch Length */
304 if (this->encoding.len != batch_len)
305 {
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);
310 goto fatal;
311 }
312
313 this->offset = PB_TNC_BATCH_HEADER_SIZE;
314
315 return SUCCESS;
316
317 fatal:
318 this->errors->insert_last(this->errors, msg);
319 return FAILED;
320 }
321
322 static status_t process_tnc_msg(private_pb_tnc_batch_t *this)
323 {
324 bio_reader_t *reader;
325 pb_tnc_msg_t *pb_tnc_msg, *msg;
326 pb_tnc_msg_info_t *msg_infos;
327 u_int8_t flags;
328 u_int32_t vendor_id, msg_type, msg_len, offset;
329 chunk_t data, msg_value;
330 bool noskip_flag;
331 enum_name_t *msg_type_names;
332 pen_type_t msg_pen_type;
333 status_t status;
334
335 data = chunk_skip(this->encoding, this->offset);
336
337 if (data.len < PB_TNC_MSG_HEADER_SIZE)
338 {
339 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message header",
340 data.len);
341 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
342 PB_ERROR_INVALID_PARAMETER, this->offset);
343 goto fatal;
344 }
345
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);
352
353 noskip_flag = (flags & PB_TNC_FLAG_NOSKIP) != PB_TNC_FLAG_NONE;
354
355 if (msg_len > data.len)
356 {
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);
360 goto fatal;
361 }
362
363 if (vendor_id == PEN_RESERVED)
364 {
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);
368 goto fatal;
369
370 }
371
372 if (msg_type == PB_TNC_RESERVED_MSG_TYPE)
373 {
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);
378 goto fatal;
379 }
380
381 if (vendor_id == PEN_IETF && msg_type <= PB_MSG_ROOF)
382 {
383 if (msg_type == PB_MSG_EXPERIMENTAL && noskip_flag)
384 {
385 DBG1(DBG_TNC, "reject IETF/PB-Experimental message with "
386 "NOSKIP flag set");
387 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
388 PB_ERROR_UNSUPPORTED_MANDATORY_MSG, this->offset);
389 goto fatal;
390 }
391 msg_type_names = pb_tnc_msg_type_names;
392 msg_infos = pb_tnc_msg_infos;
393 }
394 else if (vendor_id == PEN_TCG && msg_type <= PB_TCG_MSG_ROOF &&
395 msg_type > PB_TCG_MSG_RESERVED)
396 {
397 msg_type_names = pb_tnc_tcg_msg_type_names;
398 msg_infos = pb_tnc_tcg_msg_infos;
399 }
400 else if (vendor_id == PEN_ITA && msg_type <= PB_ITA_MSG_ROOF &&
401 msg_type > PB_ITA_MSG_NOSKIP_TEST)
402 {
403 msg_type_names = pb_tnc_ita_msg_type_names;
404 msg_infos = pb_tnc_ita_msg_infos;
405 }
406 else
407 {
408 if (msg_len < PB_TNC_MSG_HEADER_SIZE)
409 {
410 DBG1(DBG_TNC, "%u bytes too small for PB-TNC message length",
411 msg_len);
412 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
413 PB_ERROR_INVALID_PARAMETER, this->offset + 8);
414 goto fatal;
415 }
416
417 if (noskip_flag)
418 {
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);
423 goto fatal;
424 }
425 else
426 {
427 DBG1(DBG_TNC, "ignore PB-TNC message (0x%06x/0x%08x)",
428 vendor_id, msg_type);
429 this->offset += msg_len;
430 return SUCCESS;
431 }
432 }
433
434 if (msg_infos[msg_type].has_noskip_flag != TRUE_OR_FALSE &&
435 msg_infos[msg_type].has_noskip_flag != noskip_flag)
436 {
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);
442 goto fatal;
443 }
444
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))
448 {
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);
455 goto fatal;
456 }
457
458 if (msg_infos[msg_type].in_result_batch && this->type != PB_BATCH_RESULT)
459 {
460 if (this->is_server)
461 {
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);
466 goto fatal;
467 }
468 else
469 {
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;
473 return SUCCESS;
474 }
475 }
476
477 DBG2(DBG_TNC, "processing %N/%N message (%u bytes)", pen_names, vendor_id,
478 msg_type_names, msg_type, msg_len);
479 data.len = 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);
483
484 status = pb_tnc_msg->process(pb_tnc_msg, &offset);
485 if (status == FAILED || status == VERIFY_ERROR)
486 {
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);
490 }
491 if (status == FAILED)
492 {
493 pb_tnc_msg->destroy(pb_tnc_msg);
494 return FAILED;
495 }
496 this->messages->insert_last(this->messages, pb_tnc_msg);
497 this->offset += msg_len;
498 return status;
499
500 fatal:
501 this->errors->insert_last(this->errors, msg);
502 return FAILED;
503 }
504
505 METHOD(pb_tnc_batch_t, process, status_t,
506 private_pb_tnc_batch_t *this, pb_tnc_state_machine_t *state_machine)
507 {
508 pb_tnc_msg_t *msg;
509 status_t status = SUCCESS;
510
511 if (!state_machine->receive_batch(state_machine, this->type))
512 {
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);
518 return FAILED;
519 }
520
521 /* Register an empty CDATA batch with the state machine */
522 if (this->type == PB_BATCH_CDATA)
523 {
524 state_machine->set_empty_cdata(state_machine,
525 this->offset == this->encoding.len);
526 }
527
528 while (this->offset < this->encoding.len)
529 {
530 switch (process_tnc_msg(this))
531 {
532 case FAILED:
533 return FAILED;
534 case VERIFY_ERROR:
535 status = VERIFY_ERROR;
536 break;
537 case SUCCESS:
538 default:
539 break;
540 }
541 }
542 return status;
543 }
544
545 METHOD(pb_tnc_batch_t, create_msg_enumerator, enumerator_t*,
546 private_pb_tnc_batch_t *this)
547 {
548 return this->messages->create_enumerator(this->messages);
549 }
550
551 METHOD(pb_tnc_batch_t, create_error_enumerator, enumerator_t*,
552 private_pb_tnc_batch_t *this)
553 {
554 return this->errors->create_enumerator(this->errors);
555 }
556
557 METHOD(pb_tnc_batch_t, destroy, void,
558 private_pb_tnc_batch_t *this)
559 {
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);
565 free(this);
566 }
567
568 /**
569 * See header
570 */
571 pb_tnc_batch_t* pb_tnc_batch_create(bool is_server, pb_tnc_batch_type_t type,
572 size_t max_batch_len)
573 {
574 private_pb_tnc_batch_t *this;
575
576 INIT(this,
577 .public = {
578 .get_type = _get_type,
579 .get_encoding = _get_encoding,
580 .add_msg = _add_msg,
581 .build = _build,
582 .process = _process,
583 .create_msg_enumerator = _create_msg_enumerator,
584 .create_error_enumerator = _create_error_enumerator,
585 .destroy = _destroy,
586 },
587 .is_server = is_server,
588 .type = type,
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(),
593 );
594
595 DBG2(DBG_TNC, "creating PB-TNC %N batch", pb_tnc_batch_type_names, type);
596
597 return &this->public;
598 }
599
600 /**
601 * See header
602 */
603 pb_tnc_batch_t* pb_tnc_batch_create_from_data(chunk_t data)
604 {
605 private_pb_tnc_batch_t *this;
606
607 INIT(this,
608 .public = {
609 .get_type = _get_type,
610 .get_encoding = _get_encoding,
611 .add_msg = _add_msg,
612 .build = _build,
613 .process_header = _process_header,
614 .process = _process,
615 .create_msg_enumerator = _create_msg_enumerator,
616 .create_error_enumerator = _create_error_enumerator,
617 .destroy = _destroy,
618 },
619 .messages = linked_list_create(),
620 .errors = linked_list_create(),
621 .encoding = chunk_clone(data),
622 );
623
624 return &this->public;
625 }
626