debug cosmetics
[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 /*
210 * Implements the PB-TNC state machine in receive direction
211 */
212 static bool state_transition_upon_receive(pb_tnc_state_t *state,
213 pb_tnc_batch_type_t batch_type,
214 bool is_server)
215 {
216 switch (*state)
217 {
218 case PB_STATE_INIT:
219 if (is_server && batch_type == PB_BATCH_CDATA)
220 {
221 *state = PB_STATE_SERVER_WORKING;
222 break;
223 }
224 if (!is_server && batch_type == PB_BATCH_SDATA)
225 {
226 *state = PB_STATE_CLIENT_WORKING;
227 break;
228 }
229 if (batch_type == PB_BATCH_CLOSE)
230 {
231 *state = PB_STATE_END;
232 break;
233 }
234 return FALSE;
235 case PB_STATE_SERVER_WORKING:
236 if (!is_server && batch_type == PB_BATCH_SDATA)
237 {
238 *state = PB_STATE_CLIENT_WORKING;
239 break;
240 }
241 if (!is_server && batch_type == PB_BATCH_RESULT)
242 {
243 *state = PB_STATE_DECIDED;
244 break;
245 }
246 if ((is_server && batch_type == PB_BATCH_CRETRY) ||
247 (!is_server && batch_type == PB_BATCH_SRETRY))
248 {
249 break;
250 }
251 if (batch_type == PB_BATCH_CLOSE)
252 {
253 *state = PB_STATE_END;
254 break;
255 }
256 return FALSE;
257 case PB_STATE_CLIENT_WORKING:
258 if (is_server && batch_type == PB_BATCH_CDATA)
259 {
260 *state = PB_STATE_SERVER_WORKING;
261 break;
262 }
263 if (is_server && batch_type == PB_BATCH_CRETRY)
264 {
265 break;
266 }
267 if (batch_type == PB_BATCH_CLOSE)
268 {
269 *state = PB_STATE_END;
270 break;
271 }
272 return FALSE;
273 case PB_STATE_DECIDED:
274 if ((is_server && batch_type == PB_BATCH_CRETRY) ||
275 (!is_server && batch_type == PB_BATCH_SRETRY))
276 {
277 *state = PB_STATE_SERVER_WORKING;
278 break;
279 }
280 if (batch_type == PB_BATCH_CLOSE)
281 {
282 *state = PB_STATE_END;
283 break;
284 }
285 return FALSE;
286 case PB_STATE_END:
287 if (batch_type == PB_BATCH_CLOSE)
288 {
289 break;
290 }
291 return FALSE;
292 }
293 return TRUE;
294 }
295
296 static status_t process_batch_header(private_pb_tnc_batch_t *this,
297 pb_tnc_state_t *state)
298 {
299 tls_reader_t *reader;
300 pb_tnc_message_t *msg;
301 pb_error_message_t *err_msg;
302 u_int8_t version, flags, reserved, type;
303 u_int32_t batch_len;
304 bool directionality;
305
306 if (this->encoding.len < PB_TNC_BATCH_HEADER_SIZE)
307 {
308 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC batch header",
309 this->encoding.len);
310 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
311 PB_ERROR_INVALID_PARAMETER);
312 err_msg = (pb_error_message_t*)msg;
313 err_msg->set_offset(err_msg, 0);
314 goto fatal;
315 }
316
317 reader = tls_reader_create(this->encoding);
318 reader->read_uint8 (reader, &version);
319 reader->read_uint8 (reader, &flags);
320 reader->read_uint8 (reader, &reserved);
321 reader->read_uint8 (reader, &type);
322 reader->read_uint32(reader, &batch_len);
323 reader->destroy(reader);
324
325 /* Version */
326 if (version != PB_TNC_VERSION)
327 {
328 DBG1(DBG_TNC, "unsupported TNCCS Batch Version 0x%01x", version);
329 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
330 PB_ERROR_VERSION_NOT_SUPPORTED);
331 err_msg = (pb_error_message_t*)msg;
332 err_msg->set_bad_version(err_msg, version);
333 goto fatal;
334 }
335
336 /* Directionality */
337 directionality = (flags & PB_TNC_BATCH_FLAG_D) != PB_TNC_BATCH_FLAG_NONE;
338 if (directionality == this->is_server)
339 {
340 DBG1(DBG_TNC, "wrong Directionality: Batch is from a PB %s",
341 directionality ? "Server" : "Client");
342 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
343 PB_ERROR_INVALID_PARAMETER);
344 err_msg = (pb_error_message_t*)msg;
345 err_msg->set_offset(err_msg, 1);
346 goto fatal;
347 }
348
349 /* Batch Type */
350 this->type = type & 0x0F;
351 if (this->type > PB_BATCH_ROOF)
352 {
353 DBG1(DBG_TNC, "unknown PB-TNC Batch Type: %d", this->type);
354 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
355 PB_ERROR_INVALID_PARAMETER);
356 err_msg = (pb_error_message_t*)msg;
357 err_msg->set_offset(err_msg, 3);
358 goto fatal;
359 }
360
361 if (!state_transition_upon_receive(state, this->type, this->is_server))
362 {
363 DBG1(DBG_TNC, "unexpected PB-TNC Batch Type: %N",
364 pb_tnc_batch_type_names, this->type);
365 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
366 PB_ERROR_UNEXPECTED_BATCH_TYPE);
367 goto fatal;
368 }
369
370 /* Batch Length */
371 if (this->encoding.len != batch_len)
372 {
373 DBG1(DBG_TNC, "%u bytes of data is not equal to batch length of %u bytes",
374 this->encoding.len, batch_len);
375 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
376 PB_ERROR_INVALID_PARAMETER);
377 err_msg = (pb_error_message_t*)msg;
378 err_msg->set_offset(err_msg, 4);
379 goto fatal;
380 }
381
382 this->offset = PB_TNC_BATCH_HEADER_SIZE;
383 return SUCCESS;
384
385 fatal:
386 this->errors->insert_last(this->errors, msg);
387 return FAILED;
388 }
389
390 static status_t process_tnc_message(private_pb_tnc_batch_t *this)
391 {
392 tls_reader_t *reader;
393 pb_tnc_message_t *pb_tnc_msg, *msg;
394 pb_error_message_t *err_msg;
395 u_int8_t flags;
396 u_int32_t vendor_id, msg_type, msg_len;
397 chunk_t data, msg_value;
398 status_t status;
399
400 data = chunk_skip(this->encoding, this->offset);
401
402 if (data.len < PB_TNC_HEADER_SIZE)
403 {
404 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message header",
405 data.len);
406 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
407 PB_ERROR_INVALID_PARAMETER);
408 err_msg = (pb_error_message_t*)msg;
409 err_msg->set_offset(err_msg, this->offset);
410 goto fatal;
411 }
412
413 reader = tls_reader_create(data);
414 reader->read_uint8 (reader, &flags);
415 reader->read_uint24(reader, &vendor_id);
416 reader->read_uint32(reader, &msg_type);
417 reader->read_uint32(reader, &msg_len);
418 reader->destroy(reader);
419
420 if (msg_len < PB_TNC_HEADER_SIZE)
421 {
422 DBG1(DBG_TNC, "%u bytes too small for PB-TNC message length", msg_len);
423 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
424 PB_ERROR_INVALID_PARAMETER);
425 err_msg = (pb_error_message_t*)msg;
426 err_msg->set_offset(err_msg, this->offset + 8);
427 goto fatal;
428 }
429
430 if (msg_len > data.len)
431 {
432 DBG1(DBG_TNC, "%u bytes insufficient to parse PB-TNC message", data.len);
433 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
434 PB_ERROR_INVALID_PARAMETER);
435 err_msg = (pb_error_message_t*)msg;
436 err_msg->set_offset(err_msg, this->offset + 8);
437 goto fatal;
438 }
439
440 if (vendor_id == RESERVED_VENDOR_ID)
441 {
442 DBG1(DBG_TNC, "Vendor ID 0x%06x is reserved", RESERVED_VENDOR_ID);
443 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
444 PB_ERROR_INVALID_PARAMETER);
445 err_msg = (pb_error_message_t*)msg;
446 err_msg->set_offset(err_msg, this->offset + 1);
447 goto fatal;
448
449 }
450
451 if (msg_type == PB_TNC_RESERVED_MSG_TYPE)
452 {
453 DBG1(DBG_TNC, "PB-TNC Message Type 0x%08x is reserved",
454 PB_TNC_RESERVED_MSG_TYPE);
455 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
456 PB_ERROR_INVALID_PARAMETER);
457 err_msg = (pb_error_message_t*)msg;
458 err_msg->set_offset(err_msg, this->offset + 4);
459 goto fatal;
460 }
461
462 if (vendor_id != IETF_VENDOR_ID || msg_type > PB_MSG_ROOF)
463 {
464 if (flags & PB_TNC_FLAG_NOSKIP)
465 {
466 DBG1(DBG_TNC, "cannot process PB-TNC Message with Vendor ID 0x%06x "
467 " and type 0x%08x", vendor_id, msg_type);
468 msg = pb_error_message_create(TRUE, IETF_VENDOR_ID,
469 PB_ERROR_UNSUPPORTED_MANDATORY_MESSAGE);
470 err_msg = (pb_error_message_t*)msg;
471 err_msg->set_offset(err_msg, this->offset);
472 goto fatal;
473 }
474 else
475 {
476 DBG1(DBG_TNC, "ignore PB-TNC Message with Vendor ID 0x%06x "
477 " and type 0x%08x", vendor_id, msg_type);
478 this->offset += msg_len;
479 return INVALID_STATE;
480 }
481 }
482
483 if ((msg_type == PB_MSG_ASSESSMENT_RESULT ||
484 msg_type == PB_MSG_ACCESS_RECOMMENDATION ||
485 msg_type == PB_MSG_REMEDIATION_PARAMETERS) &&
486 this->type != PB_BATCH_RESULT)
487 {
488 DBG1(DBG_TNC,"ignore %N Message not received within RESULT batch",
489 pb_tnc_msg_type_names, msg_type);
490 this->offset += msg_len;
491 return INVALID_STATE;
492 }
493
494 DBG2(DBG_TNC, "processing %N Message (%u bytes)", pb_tnc_msg_type_names,
495 msg_type, msg_len);
496 data.len = msg_len;
497 DBG3(DBG_TNC, "%B", &data);
498 msg_value = chunk_skip(data, PB_TNC_HEADER_SIZE);
499 pb_tnc_msg = pb_tnc_message_create(msg_type, msg_value);
500
501 status = pb_tnc_msg->process(pb_tnc_msg);
502 if (status == FAILED)
503 {
504 pb_tnc_msg->destroy(pb_tnc_msg);
505 return FAILED;
506 }
507 this->messages->insert_last(this->messages, pb_tnc_msg);
508 this->offset += msg_len;
509 return SUCCESS;
510
511 fatal:
512 this->errors->insert_last(this->errors, msg);
513 return FAILED;
514 }
515
516 METHOD(pb_tnc_batch_t, process, status_t,
517 private_pb_tnc_batch_t *this, pb_tnc_state_t *state)
518 {
519 status_t status;
520
521 status = process_batch_header(this, state);
522 if (status == FAILED)
523 {
524 return FAILED;
525 }
526 DBG1(DBG_TNC, "processing PB-TNC %N Batch", pb_tnc_batch_type_names,
527 this->type);
528 while (this->offset < this->encoding.len)
529 {
530 status = process_tnc_message(this);
531 if (status == FAILED)
532 {
533 return FAILED;
534 }
535 }
536 return SUCCESS;
537 }
538
539 METHOD(pb_tnc_batch_t, create_msg_enumerator, enumerator_t*,
540 private_pb_tnc_batch_t *this)
541 {
542 return this->messages->create_enumerator(this->messages);
543 }
544
545 METHOD(pb_tnc_batch_t, create_error_enumerator, enumerator_t*,
546 private_pb_tnc_batch_t *this)
547 {
548 return this->errors->create_enumerator(this->errors);
549 }
550
551 METHOD(pb_tnc_batch_t, destroy, void,
552 private_pb_tnc_batch_t *this)
553 {
554 this->messages->destroy_offset(this->messages,
555 offsetof(pb_tnc_message_t, destroy));
556 this->errors->destroy_offset(this->errors,
557 offsetof(pb_tnc_message_t, destroy));
558 free(this->encoding.ptr);
559 free(this);
560 }
561
562 /**
563 * See header
564 */
565 pb_tnc_batch_t* pb_tnc_batch_create(bool is_server, pb_tnc_batch_type_t type)
566 {
567 private_pb_tnc_batch_t *this;
568
569 INIT(this,
570 .public = {
571 .get_type = _get_type,
572 .get_encoding = _get_encoding,
573 .add_message = _add_message,
574 .build = _build,
575 .process = _process,
576 .create_msg_enumerator = _create_msg_enumerator,
577 .create_error_enumerator = _create_error_enumerator,
578 .destroy = _destroy,
579 },
580 .is_server = is_server,
581 .type = type,
582 .messages = linked_list_create(),
583 .errors = linked_list_create(),
584 );
585
586 DBG2(DBG_TNC, "creating PB-TNC %N Batch", pb_tnc_batch_type_names, type);
587
588 return &this->public;
589 }
590
591 /**
592 * See header
593 */
594 pb_tnc_batch_t* pb_tnc_batch_create_from_data(bool is_server, chunk_t data)
595 {
596 private_pb_tnc_batch_t *this;
597
598 INIT(this,
599 .public = {
600 .get_type = _get_type,
601 .get_encoding = _get_encoding,
602 .add_message = _add_message,
603 .build = _build,
604 .process = _process,
605 .create_msg_enumerator = _create_msg_enumerator,
606 .create_error_enumerator = _create_error_enumerator,
607 .destroy = _destroy,
608 },
609 .is_server = is_server,
610 .messages = linked_list_create(),
611 .errors = linked_list_create(),
612 .encoding = chunk_clone(data),
613 );
614
615 return &this->public;
616 }
617