2d02bf6e0a7fc7bd005c5ffe6d3429c4838ad49c
[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_int32_t msg_len;
183 chunk_t msg_value;
184 enumerator_t *enumerator;
185 pen_type_t msg_type;
186 pb_tnc_msg_t *msg;
187 pb_tnc_msg_info_t *msg_infos;
188 bio_writer_t *writer;
189
190 /* build PB-TNC batch header */
191 writer = bio_writer_create(this->batch_len);
192 writer->write_uint8 (writer, PB_TNC_VERSION);
193 writer->write_uint8 (writer, this->is_server ?
194 PB_TNC_BATCH_FLAG_D : PB_TNC_BATCH_FLAG_NONE);
195 writer->write_uint16(writer, this->type);
196 writer->write_uint32(writer, this->batch_len);
197
198 /* build PB-TNC messages */
199 enumerator = this->messages->create_enumerator(this->messages);
200 while (enumerator->enumerate(enumerator, &msg))
201 {
202 u_int8_t flags = PB_TNC_FLAG_NONE;
203
204 /* build PB-TNC message */
205 msg_value = msg->get_encoding(msg);
206 msg_len = PB_TNC_MSG_HEADER_SIZE + msg_value.len;
207 msg_type = msg->get_type(msg);
208 switch (msg_type.vendor_id)
209 {
210 default:
211 case PEN_IETF:
212 msg_infos = pb_tnc_msg_infos;
213 break;
214 case PEN_TCG:
215 msg_infos = pb_tnc_tcg_msg_infos;
216 break;
217 case PEN_ITA:
218 msg_infos = pb_tnc_ita_msg_infos;
219 break;
220 }
221 if (msg_infos[msg_type.type].has_noskip_flag)
222 {
223 flags |= PB_TNC_FLAG_NOSKIP;
224 }
225 writer->write_uint8 (writer, flags);
226 writer->write_uint24(writer, msg_type.vendor_id);
227 writer->write_uint32(writer, msg_type.type);
228 writer->write_uint32(writer, msg_len);
229 writer->write_data (writer, msg_value);
230 }
231 enumerator->destroy(enumerator);
232
233 this->encoding = writer->extract_buf(writer);
234 writer->destroy(writer);
235 }
236
237 METHOD(pb_tnc_batch_t, process_header, status_t,
238 private_pb_tnc_batch_t *this, bool directionality, bool is_server,
239 bool *from_server)
240 {
241 bio_reader_t *reader;
242 pb_tnc_msg_t *msg;
243 pb_error_msg_t *err_msg;
244 u_int8_t version, flags, reserved, type;
245 u_int32_t batch_len;
246
247 if (this->encoding.len < PB_TNC_BATCH_HEADER_SIZE)
248 {
249 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC batch header",
250 this->encoding.len);
251 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
252 PB_ERROR_INVALID_PARAMETER, 0);
253 goto fatal;
254 }
255
256 reader = bio_reader_create(this->encoding);
257 reader->read_uint8 (reader, &version);
258 reader->read_uint8 (reader, &flags);
259 reader->read_uint8 (reader, &reserved);
260 reader->read_uint8 (reader, &type);
261 reader->read_uint32(reader, &batch_len);
262 reader->destroy(reader);
263
264 /* Version */
265 if (version != PB_TNC_VERSION)
266 {
267 DBG1(DBG_TNC, "unsupported TNCCS batch version 0x%02x", version);
268 msg = pb_error_msg_create(TRUE, PEN_IETF,
269 PB_ERROR_VERSION_NOT_SUPPORTED);
270 err_msg = (pb_error_msg_t*)msg;
271 err_msg->set_bad_version(err_msg, version);
272 goto fatal;
273 }
274
275 /* Directionality */
276 *from_server = (flags & PB_TNC_BATCH_FLAG_D) != PB_TNC_BATCH_FLAG_NONE;
277
278 if (directionality & (*from_server == is_server))
279 {
280 DBG1(DBG_TNC, "wrong Directionality: batch is from a PB %s",
281 is_server ? "server" : "client");
282 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
283 PB_ERROR_INVALID_PARAMETER, 1);
284 goto fatal;
285 }
286
287 /* Batch Type */
288 this->type = type & 0x0F;
289 if (this->type > PB_BATCH_ROOF)
290 {
291 DBG1(DBG_TNC, "unknown PB-TNC batch type: %d", this->type);
292 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
293 PB_ERROR_INVALID_PARAMETER, 3);
294 goto fatal;
295 }
296
297 /* Batch Length */
298 if (this->encoding.len != batch_len)
299 {
300 DBG1(DBG_TNC, "%u bytes of data is not equal to batch length of %u bytes",
301 this->encoding.len, batch_len);
302 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
303 PB_ERROR_INVALID_PARAMETER, 4);
304 goto fatal;
305 }
306
307 this->offset = PB_TNC_BATCH_HEADER_SIZE;
308
309 return SUCCESS;
310
311 fatal:
312 this->errors->insert_last(this->errors, msg);
313 return VERIFY_ERROR;
314 }
315
316 static status_t process_tnc_msg(private_pb_tnc_batch_t *this)
317 {
318 bio_reader_t *reader;
319 pb_tnc_msg_t *pb_tnc_msg, *msg;
320 pb_tnc_msg_info_t *msg_infos;
321 u_int8_t flags;
322 u_int32_t vendor_id, msg_type, msg_len, offset;
323 chunk_t data, msg_value;
324 bool noskip_flag;
325 enum_name_t *msg_type_names;
326 pen_type_t msg_pen_type;
327 status_t status;
328
329 data = chunk_skip(this->encoding, this->offset);
330
331 if (data.len < PB_TNC_MSG_HEADER_SIZE)
332 {
333 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message header",
334 data.len);
335 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
336 PB_ERROR_INVALID_PARAMETER, this->offset);
337 goto fatal;
338 }
339
340 reader = bio_reader_create(data);
341 reader->read_uint8 (reader, &flags);
342 reader->read_uint24(reader, &vendor_id);
343 reader->read_uint32(reader, &msg_type);
344 reader->read_uint32(reader, &msg_len);
345 reader->destroy(reader);
346
347 noskip_flag = (flags & PB_TNC_FLAG_NOSKIP) != PB_TNC_FLAG_NONE;
348
349 if (msg_len > data.len)
350 {
351 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message", data.len);
352 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
353 PB_ERROR_INVALID_PARAMETER, this->offset + 8);
354 goto fatal;
355 }
356
357 if (vendor_id == PEN_RESERVED)
358 {
359 DBG1(DBG_TNC, "Vendor ID 0x%06x is reserved", PEN_RESERVED);
360 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
361 PB_ERROR_INVALID_PARAMETER, this->offset + 1);
362 goto fatal;
363
364 }
365
366 if (msg_type == PB_TNC_RESERVED_MSG_TYPE)
367 {
368 DBG1(DBG_TNC, "PB-TNC message Type 0x%08x is reserved",
369 PB_TNC_RESERVED_MSG_TYPE);
370 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
371 PB_ERROR_INVALID_PARAMETER, this->offset + 4);
372 goto fatal;
373 }
374
375 if (vendor_id == PEN_IETF && msg_type <= PB_MSG_ROOF)
376 {
377 if (msg_type == PB_MSG_EXPERIMENTAL && noskip_flag)
378 {
379 DBG1(DBG_TNC, "reject IETF/PB-Experimental message with "
380 "NOSKIP flag set");
381 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
382 PB_ERROR_UNSUPPORTED_MANDATORY_MSG, this->offset);
383 goto fatal;
384 }
385 msg_type_names = pb_tnc_msg_type_names;
386 msg_infos = pb_tnc_msg_infos;
387 }
388 else if (vendor_id == PEN_TCG && msg_type <= PB_TCG_MSG_ROOF)
389 {
390 msg_type_names = pb_tnc_tcg_msg_type_names;
391 msg_infos = pb_tnc_tcg_msg_infos;
392 }
393 else if (vendor_id == PEN_ITA && msg_type <= PB_ITA_MSG_ROOF)
394 {
395 msg_type_names = pb_tnc_ita_msg_type_names;
396 msg_infos = pb_tnc_ita_msg_infos;
397 }
398 else
399 {
400 if (msg_len < PB_TNC_MSG_HEADER_SIZE)
401 {
402 DBG1(DBG_TNC, "%u bytes too small for PB-TNC message length",
403 msg_len);
404 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
405 PB_ERROR_INVALID_PARAMETER, this->offset + 8);
406 goto fatal;
407 }
408
409 if (noskip_flag)
410 {
411 DBG1(DBG_TNC, "reject PB-TNC message 0x%06x/0x%08x)",
412 vendor_id, msg_type);
413 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
414 PB_ERROR_UNSUPPORTED_MANDATORY_MSG, this->offset);
415 goto fatal;
416 }
417 else
418 {
419 DBG1(DBG_TNC, "ignore PB-TNC message 0x%06x/0x%08x)",
420 vendor_id, msg_type);
421 this->offset += msg_len;
422 return SUCCESS;
423 }
424 }
425
426 if (msg_infos[msg_type].has_noskip_flag != TRUE_OR_FALSE &&
427 msg_infos[msg_type].has_noskip_flag != noskip_flag)
428 {
429 DBG1(DBG_TNC, "%N/%N message must%s have NOSKIP flag set",
430 pen_names, vendor_id, msg_type_names, msg_type,
431 msg_infos[msg_type].has_noskip_flag ? "" : " not");
432 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
433 PB_ERROR_INVALID_PARAMETER, this->offset);
434 goto fatal;
435 }
436
437 if (msg_len < msg_infos[msg_type].min_size ||
438 (msg_infos[msg_type].exact_size &&
439 msg_len != msg_infos[msg_type].min_size))
440 {
441 DBG1(DBG_TNC, "%N/%N message length must be %s %u bytes but is %u bytes",
442 pen_names, vendor_id, msg_type_names, msg_type,
443 msg_infos[msg_type].exact_size ? "exactly" : "at least",
444 msg_infos[msg_type].min_size, msg_len);
445 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
446 PB_ERROR_INVALID_PARAMETER, this->offset);
447 goto fatal;
448 }
449
450 if (msg_infos[msg_type].in_result_batch && this->type != PB_BATCH_RESULT)
451 {
452 if (this->is_server)
453 {
454 DBG1(DBG_TNC,"reject %N/%N message received from a PB-TNC client",
455 pen_names, vendor_id, msg_type_names, msg_type);
456 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
457 PB_ERROR_INVALID_PARAMETER, this->offset);
458 goto fatal;
459 }
460 else
461 {
462 DBG1(DBG_TNC,"ignore %N/%N message not received within RESULT batch",
463 pen_names, vendor_id, msg_type_names, msg_type);
464 this->offset += msg_len;
465 return SUCCESS;
466 }
467 }
468
469 DBG2(DBG_TNC, "processing %N/%N message (%u bytes)", pen_names, vendor_id,
470 msg_type_names, msg_type, msg_len);
471 data.len = msg_len;
472 msg_value = chunk_skip(data, PB_TNC_MSG_HEADER_SIZE);
473 msg_pen_type = pen_type_create(vendor_id, msg_type);
474 pb_tnc_msg = pb_tnc_msg_create_from_data(msg_pen_type, msg_value);
475
476 status = pb_tnc_msg->process(pb_tnc_msg, &offset);
477 if (status == FAILED || status == VERIFY_ERROR)
478 {
479 msg = pb_error_msg_create_with_offset(TRUE, PEN_IETF,
480 PB_ERROR_INVALID_PARAMETER, this->offset + offset);
481 this->errors->insert_last(this->errors, msg);
482 }
483 if (status == FAILED)
484 {
485 pb_tnc_msg->destroy(pb_tnc_msg);
486 return FAILED;
487 }
488 this->messages->insert_last(this->messages, pb_tnc_msg);
489 this->offset += msg_len;
490 return status;
491
492 fatal:
493 this->errors->insert_last(this->errors, msg);
494 return FAILED;
495 }
496
497 METHOD(pb_tnc_batch_t, process, status_t,
498 private_pb_tnc_batch_t *this, pb_tnc_state_machine_t *state_machine)
499 {
500 pb_tnc_msg_t *msg;
501 status_t status = SUCCESS;
502
503 if (!state_machine->receive_batch(state_machine, this->type))
504 {
505 DBG1(DBG_TNC, "unexpected PB-TNC batch type: %N",
506 pb_tnc_batch_type_names, this->type);
507 msg = pb_error_msg_create(TRUE, PEN_IETF,
508 PB_ERROR_UNEXPECTED_BATCH_TYPE);
509 this->errors->insert_last(this->errors, msg);
510 return FAILED;
511 }
512
513 /* Register an empty CDATA batch with the state machine */
514 if (this->type == PB_BATCH_CDATA)
515 {
516 state_machine->set_empty_cdata(state_machine,
517 this->offset == this->encoding.len);
518 }
519
520 while (this->offset < this->encoding.len)
521 {
522 switch (process_tnc_msg(this))
523 {
524 case FAILED:
525 return FAILED;
526 case VERIFY_ERROR:
527 status = VERIFY_ERROR;
528 break;
529 case SUCCESS:
530 default:
531 break;
532 }
533 }
534 return status;
535 }
536
537 METHOD(pb_tnc_batch_t, create_msg_enumerator, enumerator_t*,
538 private_pb_tnc_batch_t *this)
539 {
540 return this->messages->create_enumerator(this->messages);
541 }
542
543 METHOD(pb_tnc_batch_t, create_error_enumerator, enumerator_t*,
544 private_pb_tnc_batch_t *this)
545 {
546 return this->errors->create_enumerator(this->errors);
547 }
548
549 METHOD(pb_tnc_batch_t, destroy, void,
550 private_pb_tnc_batch_t *this)
551 {
552 this->messages->destroy_offset(this->messages,
553 offsetof(pb_tnc_msg_t, destroy));
554 this->errors->destroy_offset(this->errors,
555 offsetof(pb_tnc_msg_t, destroy));
556 free(this->encoding.ptr);
557 free(this);
558 }
559
560 /**
561 * See header
562 */
563 pb_tnc_batch_t* pb_tnc_batch_create(bool is_server, pb_tnc_batch_type_t type,
564 size_t max_batch_len)
565 {
566 private_pb_tnc_batch_t *this;
567
568 INIT(this,
569 .public = {
570 .get_type = _get_type,
571 .get_encoding = _get_encoding,
572 .add_msg = _add_msg,
573 .build = _build,
574 .process = _process,
575 .create_msg_enumerator = _create_msg_enumerator,
576 .create_error_enumerator = _create_error_enumerator,
577 .destroy = _destroy,
578 },
579 .is_server = is_server,
580 .type = type,
581 .max_batch_len = max_batch_len,
582 .batch_len = PB_TNC_BATCH_HEADER_SIZE,
583 .messages = linked_list_create(),
584 .errors = linked_list_create(),
585 );
586
587 DBG2(DBG_TNC, "creating PB-TNC %N batch", pb_tnc_batch_type_names, type);
588
589 return &this->public;
590 }
591
592 /**
593 * See header
594 */
595 pb_tnc_batch_t* pb_tnc_batch_create_from_data(chunk_t data)
596 {
597 private_pb_tnc_batch_t *this;
598
599 INIT(this,
600 .public = {
601 .get_type = _get_type,
602 .get_encoding = _get_encoding,
603 .add_msg = _add_msg,
604 .build = _build,
605 .process_header = _process_header,
606 .process = _process,
607 .create_msg_enumerator = _create_msg_enumerator,
608 .create_error_enumerator = _create_error_enumerator,
609 .destroy = _destroy,
610 },
611 .messages = linked_list_create(),
612 .errors = linked_list_create(),
613 .encoding = chunk_clone(data),
614 );
615
616 return &this->public;
617 }
618