pb_tnc_batch_t class implements parsing and building of PB-TNC batches
[strongswan.git] / src / libcharon / plugins / tnccs_20 / batch / pb_tnc_batch.c
1 /*
2 * Copyright (C) 2010 Sansar Choinyanbuu
3 * Copyright (C) 2010 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 "tnccs_20_types.h"
18 #include "pb_tnc_batch.h"
19 #include "messages/pb_error_message.h"
20
21 #include <debug.h>
22 #include <utils/linked_list.h>
23 #include <tls_writer.h>
24 #include <tls_reader.h>
25 #include <tnc/tnccs/tnccs.h>
26
27 ENUM(pb_tnc_state_names, PB_STATE_INIT, PB_STATE_END,
28 "Init",
29 "Server Working",
30 "Client Working",
31 "Decided",
32 "End"
33 );
34
35 ENUM(pb_tnc_batch_type_names, PB_BATCH_CDATA, PB_BATCH_CLOSE,
36 "CDATA",
37 "SDATA",
38 "RESULT",
39 "CRETRY",
40 "SRETRY",
41 "CLOSE"
42 );
43
44 typedef struct private_pb_tnc_batch_t private_pb_tnc_batch_t;
45
46 /**
47 * PB-Batch Header (see section 4.1 of RFC 5793)
48 *
49 * 0 1 2 3
50 * 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
51 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
52 * | Version |D| Reserved | B-Type|
53 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
54 * | Batch Length |
55 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
56 */
57
58 #define PB_TNC_BATCH_FLAG_NONE 0x00
59 #define PB_TNC_BATCH_FLAG_D (1<<7)
60 #define PB_TNC_BATCH_HEADER_SIZE 8
61
62 /**
63 * PB-TNC Message (see section 4.2 of RFC 5793)
64 *
65 * 0 1 2 3
66 * 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
67 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68 * | Flags | PB-TNC Vendor ID |
69 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70 * | PB-TNC Message Type |
71 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72 * | PB-TNC Message Length |
73 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74 * | PB-TNC Message Value (Variable Length) |
75 * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76 */
77
78 #define PB_TNC_FLAG_NONE 0x00
79 #define PB_TNC_FLAG_NOSKIP (1<<7)
80 #define PB_TNC_HEADER_SIZE 12
81
82 #define PB_TNC_RESERVED_MSG_TYPE 0xffffffff
83
84 /**
85 * Private data of a pb_tnc_batch_t object.
86 *
87 */
88 struct private_pb_tnc_batch_t {
89 /**
90 * Public pb_pa_message_t interface.
91 */
92 pb_tnc_batch_t public;
93
94 /**
95 * TNCC if TRUE, TNCS if FALSE
96 */
97 bool is_server;
98
99 /**
100 * PB-TNC Batch type
101 */
102 pb_tnc_batch_type_t type;
103
104 /**
105 * linked list of PB-TNC messages
106 */
107 linked_list_t *messages;
108
109 /**
110 * linked list of PB-TNC error messages
111 */
112 linked_list_t *errors;
113
114 /**
115 * Encoded message
116 */
117 chunk_t encoding;
118
119 /**
120 * Offset into encoding (used for error reporting)
121 */
122 size_t offset;
123 };
124
125 METHOD(pb_tnc_batch_t, get_type, pb_tnc_batch_type_t,
126 private_pb_tnc_batch_t *this)
127 {
128 return this->type;
129 }
130
131 METHOD(pb_tnc_batch_t, get_encoding, chunk_t,
132 private_pb_tnc_batch_t *this)
133 {
134 return this->encoding;
135 }
136
137 METHOD(pb_tnc_batch_t, add_message, void,
138 private_pb_tnc_batch_t *this, pb_tnc_message_t* msg)
139 {
140 DBG2(DBG_TNC, " adding %N Message", pb_tnc_msg_type_names,
141 msg->get_type(msg));
142 this->messages->insert_last(this->messages, msg);
143 }
144
145 METHOD(pb_tnc_batch_t, build, void,
146 private_pb_tnc_batch_t *this)
147 {
148 u_int32_t batch_len, msg_len;
149 u_int8_t flags = PB_TNC_FLAG_NONE;
150 chunk_t msg_value;
151 enumerator_t *enumerator;
152 pb_tnc_msg_type_t msg_type;
153 pb_tnc_message_t *msg;
154 tls_writer_t *writer;
155
156 /* compute total PB-TNC batch size by summing over all messages */
157 batch_len = PB_TNC_BATCH_HEADER_SIZE;
158 enumerator = this->messages->create_enumerator(this->messages);
159 while (enumerator->enumerate(enumerator, &msg))
160 {
161 msg->build(msg);
162 msg_value = msg->get_encoding(msg);
163 batch_len += PB_TNC_HEADER_SIZE + msg_value.len;
164 }
165 enumerator->destroy(enumerator);
166
167 /* build PB-TNC batch header */
168 writer = tls_writer_create(batch_len);
169 writer->write_uint8 (writer, PB_TNC_VERSION);
170 writer->write_uint8 (writer, this->is_server ?
171 PB_TNC_BATCH_FLAG_D : PB_TNC_BATCH_FLAG_NONE);
172 writer->write_uint16(writer, this->type);
173 writer->write_uint32(writer, batch_len);
174
175 /* build PB-TNC messages */
176 enumerator = this->messages->create_enumerator(this->messages);
177 while (enumerator->enumerate(enumerator, &msg))
178 {
179 /* build PB-TNC message */
180 msg_value = msg->get_encoding(msg);
181 msg_len = PB_TNC_HEADER_SIZE + msg_value.len;
182 msg_type = msg->get_type(msg);
183 switch (msg_type)
184 {
185 case PB_MSG_PA:
186 case PB_MSG_ASSESSMENT_RESULT:
187 case PB_MSG_ERROR:
188 flags |= PB_TNC_FLAG_NOSKIP;
189 break;
190 case PB_MSG_EXPERIMENTAL:
191 case PB_MSG_ACCESS_RECOMMENDATION:
192 case PB_MSG_REMEDIATION_PARAMETERS:
193 case PB_MSG_LANGUAGE_PREFERENCE:
194 case PB_MSG_REASON_STRING:
195 break;
196 }
197 writer->write_uint8 (writer, flags);
198 writer->write_uint24(writer, IETF_VENDOR_ID);
199 writer->write_uint32(writer, msg_type);
200 writer->write_uint32(writer, msg_len);
201 writer->write_data (writer, msg_value);
202 }
203 enumerator->destroy(enumerator);
204
205 this->encoding = chunk_clone(writer->get_buf(writer));
206 writer->destroy(writer);
207 }
208
209 static status_t process_batch_header(private_pb_tnc_batch_t *this,
210 pb_tnc_state_t *state)
211 {
212 tls_reader_t *reader;
213 pb_tnc_message_t *msg;
214 pb_error_message_t *err_msg;
215 u_int8_t version, flags, reserved, type;
216 u_int32_t batch_len;
217 bool directionality, unexpected_batch_type = FALSE;
218
219 if (this->encoding.len < PB_TNC_BATCH_HEADER_SIZE)
220 {
221 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC batch header",
222 this->encoding.len);
223 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
224 PB_ERROR_INVALID_PARAMETER);
225 err_msg = (pb_error_message_t*)msg;
226 err_msg->set_offset(err_msg, 0);
227 goto fatal;
228 }
229
230 reader = tls_reader_create(this->encoding);
231 reader->read_uint8 (reader, &version);
232 reader->read_uint8 (reader, &flags);
233 reader->read_uint8 (reader, &reserved);
234 reader->read_uint8 (reader, &type);
235 reader->read_uint32(reader, &batch_len);
236 reader->destroy(reader);
237
238 /* Version */
239 if (version != PB_TNC_VERSION)
240 {
241 DBG1(DBG_TNC, "Unsupported TNCCS Batch Version 0x%01x", version);
242 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
243 PB_ERROR_VERSION_NOT_SUPPORTED);
244 err_msg = (pb_error_message_t*)msg;
245 err_msg->set_bad_version(err_msg, version);
246 goto fatal;
247 }
248
249 /* Directionality */
250 directionality = (flags & PB_TNC_BATCH_FLAG_D) != PB_TNC_BATCH_FLAG_NONE;
251 if (directionality == this->is_server)
252 {
253 DBG1(DBG_TNC, "Wrong Directionality: Batch is from a PB %s",
254 directionality ? "Server" : "Client");
255 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
256 PB_ERROR_INVALID_PARAMETER);
257 err_msg = (pb_error_message_t*)msg;
258 err_msg->set_offset(err_msg, 1);
259 goto fatal;
260 }
261
262 /* Batch Type */
263 this->type = type & 0x0F;
264 if (this->type > PB_BATCH_ROOF)
265 {
266 DBG1(DBG_TNC, "Unknown PB-TNC Batch Type: %d", this->type);
267 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
268 PB_ERROR_INVALID_PARAMETER);
269 err_msg = (pb_error_message_t*)msg;
270 err_msg->set_offset(err_msg, 3);
271 goto fatal;
272 }
273 switch (*state)
274 {
275 case PB_STATE_INIT:
276 if (this->is_server && this->type == PB_BATCH_CDATA)
277 {
278 *state = PB_STATE_SERVER_WORKING;
279 break;
280 }
281 if (!this->is_server && this->type == PB_BATCH_SDATA)
282 {
283 *state = PB_STATE_CLIENT_WORKING;
284 break;
285 }
286 if (this->type == PB_BATCH_CLOSE)
287 {
288 *state = PB_STATE_END;
289 break;
290 }
291 unexpected_batch_type = TRUE;
292 break;
293 case PB_STATE_SERVER_WORKING:
294 if (!this->is_server && this->type == PB_BATCH_SDATA)
295 {
296 *state = PB_STATE_CLIENT_WORKING;
297 break;
298 }
299 if (!this->is_server && this->type == PB_BATCH_RESULT)
300 {
301 *state = PB_STATE_DECIDED;
302 break;
303 }
304 if ((this->is_server && this->type == PB_BATCH_CRETRY) ||
305 (!this->is_server && this->type == PB_BATCH_SRETRY))
306 {
307 break;
308 }
309 if (this->type == PB_BATCH_CLOSE)
310 {
311 *state = PB_STATE_END;
312 break;
313 }
314 unexpected_batch_type = TRUE;
315 break;
316 case PB_STATE_CLIENT_WORKING:
317 if (this->is_server && this->type == PB_BATCH_CDATA)
318 {
319 *state = PB_STATE_SERVER_WORKING;
320 break;
321 }
322 if (this->is_server && this->type == PB_BATCH_CRETRY)
323 {
324 break;
325 }
326 if (this->type == PB_BATCH_CLOSE)
327 {
328 *state = PB_STATE_END;
329 break;
330 }
331 unexpected_batch_type = TRUE;
332 break;
333 case PB_STATE_DECIDED:
334 if ((this->is_server && this->type == PB_BATCH_CRETRY) ||
335 (!this->is_server && this->type == PB_BATCH_SRETRY))
336 {
337 *state = PB_STATE_SERVER_WORKING;
338 break;
339 }
340 if (this->type == PB_BATCH_CLOSE)
341 {
342 *state = PB_STATE_END;
343 break;
344 }
345 unexpected_batch_type = TRUE;
346 break;
347 case PB_STATE_END:
348 if (this->type == PB_BATCH_CLOSE)
349 {
350 break;
351 }
352 unexpected_batch_type = TRUE;
353 }
354 if (unexpected_batch_type)
355 {
356 DBG1(DBG_TNC, "Unexpected PB-TNC Batch Type: %N",
357 pb_tnc_batch_type_names, this->type);
358 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
359 PB_ERROR_UNEXPECTED_BATCH_TYPE);
360 goto fatal;
361 }
362
363 /* Batch Length */
364 if (this->encoding.len != batch_len)
365 {
366 DBG1(DBG_TNC, "%u bytes of data is not equal to batch length of %u bytes",
367 this->encoding.len, batch_len);
368 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
369 PB_ERROR_INVALID_PARAMETER);
370 err_msg = (pb_error_message_t*)msg;
371 err_msg->set_offset(err_msg, 4);
372 goto fatal;
373 }
374
375 this->offset = PB_TNC_BATCH_HEADER_SIZE;
376 return SUCCESS;
377
378 fatal:
379 this->errors->insert_last(this->errors, msg);
380 return FAILED;
381 }
382
383 static status_t process_tnc_message(private_pb_tnc_batch_t *this)
384 {
385 tls_reader_t *reader;
386 pb_tnc_message_t *pb_tnc_msg, *msg;
387 pb_error_message_t *err_msg;
388 u_int8_t flags;
389 u_int32_t vendor_id, msg_type, msg_len;
390 chunk_t data, msg_value;
391 status_t status;
392
393 data = chunk_skip(this->encoding, this->offset);
394
395 if (data.len < PB_TNC_HEADER_SIZE)
396 {
397 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message header",
398 data.len);
399 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
400 PB_ERROR_INVALID_PARAMETER);
401 err_msg = (pb_error_message_t*)msg;
402 err_msg->set_offset(err_msg, this->offset);
403 goto fatal;
404 }
405
406 reader = tls_reader_create(data);
407 reader->read_uint8 (reader, &flags);
408 reader->read_uint24(reader, &vendor_id);
409 reader->read_uint32(reader, &msg_type);
410 reader->read_uint32(reader, &msg_len);
411 reader->destroy(reader);
412
413 if (msg_len < PB_TNC_HEADER_SIZE)
414 {
415 DBG1(DBG_TNC, "%u bytes too small for PB-TNC message length", msg_len);
416 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
417 PB_ERROR_INVALID_PARAMETER);
418 err_msg = (pb_error_message_t*)msg;
419 err_msg->set_offset(err_msg, this->offset + 8);
420 goto fatal;
421 }
422
423 if (msg_len > data.len)
424 {
425 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message", data.len);
426 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
427 PB_ERROR_INVALID_PARAMETER);
428 err_msg = (pb_error_message_t*)msg;
429 err_msg->set_offset(err_msg, this->offset + 8);
430 goto fatal;
431 }
432
433 if (vendor_id == RESERVED_VENDOR_ID)
434 {
435 DBG1(DBG_TNC, "Vendor ID 0x%06x is reserved", RESERVED_VENDOR_ID);
436 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
437 PB_ERROR_INVALID_PARAMETER);
438 err_msg = (pb_error_message_t*)msg;
439 err_msg->set_offset(err_msg, this->offset + 1);
440 goto fatal;
441
442 }
443
444 if (msg_type == PB_TNC_RESERVED_MSG_TYPE)
445 {
446 DBG1(DBG_TNC, "PB-TNC Message Type 0x%08x is reserved",
447 PB_TNC_RESERVED_MSG_TYPE);
448 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
449 PB_ERROR_INVALID_PARAMETER);
450 err_msg = (pb_error_message_t*)msg;
451 err_msg->set_offset(err_msg, this->offset + 4);
452 goto fatal;
453 }
454
455 if (vendor_id != IETF_VENDOR_ID || msg_type > PB_MSG_ROOF)
456 {
457 if (flags & PB_TNC_FLAG_NOSKIP)
458 {
459 DBG1(DBG_TNC, "cannot process PB-TNC message with Vendor ID 0x%06x "
460 " and type 0x%08x", vendor_id, msg_type);
461 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
462 PB_ERROR_UNSUPPORTED_MANDATORY_MESSAGE);
463 err_msg = (pb_error_message_t*)msg;
464 err_msg->set_offset(err_msg, this->offset);
465 goto fatal;
466 }
467 else
468 {
469 DBG1(DBG_TNC, "ignore PB-TNC message with Vendor ID 0x%06x "
470 " and type 0x%08x", vendor_id, msg_type);
471 this->offset += msg_len;
472 return INVALID_STATE;
473 }
474 }
475
476 DBG2(DBG_TNC, "processing %N Message (%u bytes)", pb_tnc_msg_type_names,
477 msg_type, msg_len);
478 data.len = msg_len;
479 DBG3(DBG_TNC, "%B", &data);
480 msg_value = chunk_skip(data, PB_TNC_HEADER_SIZE);
481 pb_tnc_msg = pb_tnc_message_create(msg_type, msg_value);
482
483 status = pb_tnc_msg->process(pb_tnc_msg);
484 if (status == FAILED)
485 {
486 pb_tnc_msg->destroy(pb_tnc_msg);
487 return FAILED;
488 }
489 this->messages->insert_last(this->messages, pb_tnc_msg);
490 this->offset += msg_len;
491 return SUCCESS;
492
493 fatal:
494 this->errors->insert_last(this->errors, msg);
495 return FAILED;
496 }
497
498 METHOD(pb_tnc_batch_t, process, status_t,
499 private_pb_tnc_batch_t *this, pb_tnc_state_t *state)
500 {
501 status_t status;
502
503 status = process_batch_header(this, state);
504 if (status == FAILED)
505 {
506 return FAILED;
507 }
508
509 while (this->offset < this->encoding.len)
510 {
511 status = process_tnc_message(this);
512 if (status == FAILED)
513 {
514 return FAILED;
515 }
516 }
517 return SUCCESS;
518 }
519
520 METHOD(pb_tnc_batch_t, create_msg_enumerator, enumerator_t*,
521 private_pb_tnc_batch_t *this)
522 {
523 return this->messages->create_enumerator(this->messages);
524 }
525
526 METHOD(pb_tnc_batch_t, create_error_enumerator, enumerator_t*,
527 private_pb_tnc_batch_t *this)
528 {
529 return this->errors->create_enumerator(this->errors);
530 }
531
532 METHOD(pb_tnc_batch_t, destroy, void,
533 private_pb_tnc_batch_t *this)
534 {
535 this->messages->destroy_offset(this->messages,
536 offsetof(pb_tnc_message_t, destroy));
537 this->errors->destroy_offset(this->errors,
538 offsetof(pb_tnc_message_t, destroy));
539 free(this->encoding.ptr);
540 free(this);
541 }
542
543 /**
544 * See header
545 */
546 pb_tnc_batch_t* pb_tnc_batch_create(bool is_server, pb_tnc_batch_type_t type)
547 {
548 private_pb_tnc_batch_t *this;
549
550 INIT(this,
551 .public = {
552 .get_type = _get_type,
553 .get_encoding = _get_encoding,
554 .add_message = _add_message,
555 .build = _build,
556 .process = _process,
557 .create_msg_enumerator = _create_msg_enumerator,
558 .create_error_enumerator = _create_error_enumerator,
559 .destroy = _destroy,
560 },
561 .is_server = is_server,
562 .type = type,
563 .messages = linked_list_create(),
564 .errors = linked_list_create(),
565 );
566
567 DBG2(DBG_TNC, "creating PB-TNC %N Batch", pb_tnc_batch_type_names, type);
568
569 return &this->public;
570 }
571
572 /**
573 * See header
574 */
575 pb_tnc_batch_t* pb_tnc_batch_create_from_data(bool is_server, chunk_t data)
576 {
577 private_pb_tnc_batch_t *this;
578
579 INIT(this,
580 .public = {
581 .get_type = _get_type,
582 .get_encoding = _get_encoding,
583 .add_message = _add_message,
584 .build = _build,
585 .process = _process,
586 .create_msg_enumerator = _create_msg_enumerator,
587 .create_error_enumerator = _create_error_enumerator,
588 .destroy = _destroy,
589 },
590 .is_server = is_server,
591 .messages = linked_list_create(),
592 .errors = linked_list_create(),
593 .encoding = chunk_clone(data),
594 );
595
596 return &this->public;
597 }
598