8540f46b925dc7e8aeea17e5cad8fe0c0323ba83
[strongswan.git] / src / libcharon / plugins / tnccs_20 / tnccs_20.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 "tnccs_20.h"
18 #include "batch/pb_tnc_batch.h"
19 #include "messages/pb_tnc_msg.h"
20 #include "messages/pb_pa_msg.h"
21 #include "messages/pb_error_msg.h"
22 #include "messages/pb_assessment_result_msg.h"
23 #include "messages/pb_access_recommendation_msg.h"
24 #include "messages/pb_remediation_parameters_msg.h"
25 #include "messages/pb_reason_string_msg.h"
26 #include "messages/pb_language_preference_msg.h"
27 #include "state_machine/pb_tnc_state_machine.h"
28
29 #include <tncif_names.h>
30 #include <tncif_pa_subtypes.h>
31
32 #include <tnc/tnc.h>
33 #include <tnc/tnccs/tnccs_manager.h>
34 #include <tnc/imc/imc_manager.h>
35 #include <tnc/imv/imv_manager.h>
36
37 #include <utils/debug.h>
38 #include <daemon.h>
39 #include <threading/mutex.h>
40 #include <collections/linked_list.h>
41 #include <pen/pen.h>
42
43 typedef struct private_tnccs_20_t private_tnccs_20_t;
44
45 /**
46 * Private data of a tnccs_20_t object.
47 */
48 struct private_tnccs_20_t {
49
50 /**
51 * Public tls_t interface.
52 */
53 tls_t public;
54
55 /**
56 * TNCC if TRUE, TNCS if FALSE
57 */
58 bool is_server;
59
60 /**
61 * PB-TNC State Machine
62 */
63 pb_tnc_state_machine_t *state_machine;
64
65 /**
66 * Connection ID assigned to this TNCCS connection
67 */
68 TNC_ConnectionID connection_id;
69
70 /**
71 * PB-TNC messages to be sent
72 */
73 linked_list_t *messages;
74
75 /**
76 * Type of PB-TNC batch being constructed
77 */
78 pb_tnc_batch_type_t batch_type;
79
80 /**
81 * Maximum PB-TNC batch size
82 */
83 size_t max_batch_len;
84
85 /**
86 * Maximum PA-TNC message size
87 */
88 size_t max_msg_len;
89
90 /**
91 * Mutex locking the batch in construction
92 */
93 mutex_t *mutex;
94
95 /**
96 * Flag set while processing
97 */
98 bool fatal_error;
99
100 /**
101 * Flag set by IMC/IMV RequestHandshakeRetry() function
102 */
103 bool request_handshake_retry;
104
105 /**
106 * SendMessage() by IMC/IMV only allowed if flag is set
107 */
108 bool send_msg;
109
110 /**
111 * Set of IMV recommendations (TNC Server only)
112 */
113 recommendations_t *recs;
114
115 };
116
117 /**
118 * If the batch type changes then delete all accumulated PB-TNC messages
119 */
120 void change_batch_type(private_tnccs_20_t *this, pb_tnc_batch_type_t batch_type)
121 {
122 pb_tnc_msg_t *msg;
123
124 if (batch_type != this->batch_type)
125 {
126 if (this->batch_type != PB_BATCH_NONE)
127 {
128 DBG1(DBG_TNC, "cancelling PB-TNC %N batch",
129 pb_tnc_batch_type_names, this->batch_type);
130
131 while (this->messages->remove_last(this->messages,
132 (void**)&msg) == SUCCESS)
133 {
134 msg->destroy(msg);
135 }
136 }
137 this->batch_type = batch_type;
138 }
139 }
140
141 METHOD(tnccs_t, send_msg, TNC_Result,
142 private_tnccs_20_t* this, TNC_IMCID imc_id, TNC_IMVID imv_id,
143 TNC_UInt32 msg_flags,
144 TNC_BufferReference msg,
145 TNC_UInt32 msg_len,
146 TNC_VendorID msg_vid,
147 TNC_MessageSubtype msg_subtype)
148 {
149 pb_tnc_msg_t *pb_tnc_msg;
150 pb_tnc_batch_type_t batch_type;
151 enum_name_t *pa_subtype_names;
152 bool excl;
153
154 if (!this->send_msg)
155 {
156 DBG1(DBG_TNC, "%s %u not allowed to call SendMessage()",
157 this->is_server ? "IMV" : "IMC",
158 this->is_server ? imv_id : imc_id);
159 return TNC_RESULT_ILLEGAL_OPERATION;
160 }
161 excl = (msg_flags & TNC_MESSAGE_FLAGS_EXCLUSIVE) != 0;
162
163 pb_tnc_msg = pb_pa_msg_create(msg_vid, msg_subtype, imc_id, imv_id,
164 excl, chunk_create(msg, msg_len));
165
166 pa_subtype_names = get_pa_subtype_names(msg_vid);
167 if (pa_subtype_names)
168 {
169 DBG2(DBG_TNC, "creating PB-PA message type '%N/%N' 0x%06x/0x%08x",
170 pen_names, msg_vid, pa_subtype_names, msg_subtype,
171 msg_vid, msg_subtype);
172 }
173 else
174 {
175 DBG2(DBG_TNC, "creating PB-PA message type '%N' 0x%06x/0x%08x",
176 pen_names, msg_vid, msg_vid, msg_subtype);
177 }
178
179 /* adding PA message to SDATA or CDATA batch only */
180 batch_type = this->is_server ? PB_BATCH_SDATA : PB_BATCH_CDATA;
181 this->mutex->lock(this->mutex);
182 if (this->batch_type == PB_BATCH_NONE)
183 {
184 this->batch_type = batch_type;
185 }
186 if (this->batch_type == batch_type)
187 {
188 this->messages->insert_last(this->messages, pb_tnc_msg);
189 }
190 else
191 {
192 pb_tnc_msg->destroy(pb_tnc_msg);
193 }
194 this->mutex->unlock(this->mutex);
195 return TNC_RESULT_SUCCESS;
196 }
197
198 /**
199 * Handle a single PB-TNC message according to its type
200 */
201 static void handle_message(private_tnccs_20_t *this, pb_tnc_msg_t *msg)
202 {
203 switch (msg->get_type(msg))
204 {
205 case PB_MSG_EXPERIMENTAL:
206 /* nothing to do */
207 break;
208 case PB_MSG_PA:
209 {
210 pb_pa_msg_t *pa_msg;
211 pen_type_t msg_subtype;
212 u_int16_t imc_id, imv_id;
213 chunk_t msg_body;
214 bool excl;
215 enum_name_t *pa_subtype_names;
216
217 pa_msg = (pb_pa_msg_t*)msg;
218 msg_subtype = pa_msg->get_subtype(pa_msg);
219 msg_body = pa_msg->get_body(pa_msg);
220 imc_id = pa_msg->get_collector_id(pa_msg);
221 imv_id = pa_msg->get_validator_id(pa_msg);
222 excl = pa_msg->get_exclusive_flag(pa_msg);
223
224 pa_subtype_names = get_pa_subtype_names(msg_subtype.vendor_id);
225 if (pa_subtype_names)
226 {
227 DBG2(DBG_TNC, "handling PB-PA message type '%N/%N' 0x%06x/0x%08x",
228 pen_names, msg_subtype.vendor_id, pa_subtype_names,
229 msg_subtype.type, msg_subtype.vendor_id, msg_subtype.type);
230 }
231 else
232 {
233 DBG2(DBG_TNC, "handling PB-PA message type '%N' 0x%06x/0x%08x",
234 pen_names, msg_subtype.vendor_id, msg_subtype.vendor_id,
235 msg_subtype.type);
236 }
237
238 this->send_msg = TRUE;
239 if (this->is_server)
240 {
241 tnc->imvs->receive_message(tnc->imvs, this->connection_id,
242 excl, msg_body.ptr, msg_body.len,
243 msg_subtype.vendor_id,
244 msg_subtype.type, imc_id, imv_id);
245 }
246 else
247 {
248 tnc->imcs->receive_message(tnc->imcs, this->connection_id,
249 excl, msg_body.ptr, msg_body.len,
250 msg_subtype.vendor_id,
251 msg_subtype.type, imv_id, imc_id);
252 }
253 this->send_msg = FALSE;
254 break;
255 }
256 case PB_MSG_ASSESSMENT_RESULT:
257 {
258 pb_assessment_result_msg_t *assess_msg;
259 u_int32_t result;
260
261 assess_msg = (pb_assessment_result_msg_t*)msg;
262 result = assess_msg->get_assessment_result(assess_msg);
263 DBG1(DBG_TNC, "PB-TNC assessment result is '%N'",
264 TNC_IMV_Evaluation_Result_names, result);
265 break;
266 }
267 case PB_MSG_ACCESS_RECOMMENDATION:
268 {
269 pb_access_recommendation_msg_t *rec_msg;
270 pb_access_recommendation_code_t rec;
271 TNC_ConnectionState state = TNC_CONNECTION_STATE_ACCESS_NONE;
272
273 rec_msg = (pb_access_recommendation_msg_t*)msg;
274 rec = rec_msg->get_access_recommendation(rec_msg);
275 DBG1(DBG_TNC, "PB-TNC access recommendation is '%N'",
276 pb_access_recommendation_code_names, rec);
277 switch (rec)
278 {
279 case PB_REC_ACCESS_ALLOWED:
280 state = TNC_CONNECTION_STATE_ACCESS_ALLOWED;
281 break;
282 case PB_REC_ACCESS_DENIED:
283 state = TNC_CONNECTION_STATE_ACCESS_NONE;
284 break;
285 case PB_REC_QUARANTINED:
286 state = TNC_CONNECTION_STATE_ACCESS_ISOLATED;
287 }
288 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
289 state);
290 break;
291 }
292 case PB_MSG_REMEDIATION_PARAMETERS:
293 {
294 /* TODO : Remediation parameters message processing */
295 break;
296 }
297 case PB_MSG_ERROR:
298 {
299 pb_error_msg_t *err_msg;
300 bool fatal;
301 u_int32_t vendor_id;
302 u_int16_t error_code;
303
304 err_msg = (pb_error_msg_t*)msg;
305 fatal = err_msg->get_fatal_flag(err_msg);
306 vendor_id = err_msg->get_vendor_id(err_msg);
307 error_code = err_msg->get_error_code(err_msg);
308
309 if (fatal)
310 {
311 this->fatal_error = TRUE;
312 }
313
314 if (vendor_id == PEN_IETF)
315 {
316 switch (error_code)
317 {
318 case PB_ERROR_INVALID_PARAMETER:
319 case PB_ERROR_UNSUPPORTED_MANDATORY_MSG:
320 DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
321 "(offset %u bytes)",
322 fatal ? "fatal" : "non-fatal",
323 pb_tnc_error_code_names, error_code,
324 err_msg->get_offset(err_msg));
325 break;
326 case PB_ERROR_VERSION_NOT_SUPPORTED:
327 DBG1(DBG_TNC, "received %s PB-TNC error '%N' "
328 "caused by bad version 0x%02x",
329 fatal ? "fatal" : "non-fatal",
330 pb_tnc_error_code_names, error_code,
331 err_msg->get_bad_version(err_msg));
332 break;
333 case PB_ERROR_UNEXPECTED_BATCH_TYPE:
334 case PB_ERROR_LOCAL_ERROR:
335 default:
336 DBG1(DBG_TNC, "received %s PB-TNC error '%N'",
337 fatal ? "fatal" : "non-fatal",
338 pb_tnc_error_code_names, error_code);
339 break;
340 }
341 }
342 else
343 {
344 DBG1(DBG_TNC, "received %s PB-TNC error (%u) "
345 "with Vendor ID 0x%06x",
346 fatal ? "fatal" : "non-fatal",
347 error_code, vendor_id);
348 }
349 break;
350 }
351 case PB_MSG_LANGUAGE_PREFERENCE:
352 {
353 pb_language_preference_msg_t *lang_msg;
354 chunk_t lang;
355
356 lang_msg = (pb_language_preference_msg_t*)msg;
357 lang = lang_msg->get_language_preference(lang_msg);
358
359 DBG2(DBG_TNC, "setting language preference to '%.*s'",
360 (int)lang.len, lang.ptr);
361 this->recs->set_preferred_language(this->recs, lang);
362 break;
363 }
364 case PB_MSG_REASON_STRING:
365 {
366 pb_reason_string_msg_t *reason_msg;
367 chunk_t reason_string, language_code;
368
369 reason_msg = (pb_reason_string_msg_t*)msg;
370 reason_string = reason_msg->get_reason_string(reason_msg);
371 language_code = reason_msg->get_language_code(reason_msg);
372 DBG2(DBG_TNC, "reason string is '%.*s'", (int)reason_string.len,
373 reason_string.ptr);
374 DBG2(DBG_TNC, "language code is '%.*s'", (int)language_code.len,
375 language_code.ptr);
376 break;
377 }
378 default:
379 break;
380 }
381 }
382
383 /**
384 * Build a CRETRY or SRETRY batch
385 */
386 static void build_retry_batch(private_tnccs_20_t *this)
387 {
388 pb_tnc_batch_type_t batch_retry_type;
389
390 batch_retry_type = this->is_server ? PB_BATCH_SRETRY : PB_BATCH_CRETRY;
391 if (this->batch_type == batch_retry_type)
392 {
393 /* retry batch has already been selected */
394 return;
395 }
396
397 change_batch_type(this, batch_retry_type);
398
399 if (this->is_server)
400 {
401 this->recs->clear_recommendation(this->recs);
402 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
403 TNC_CONNECTION_STATE_HANDSHAKE);
404 }
405 }
406
407 METHOD(tls_t, process, status_t,
408 private_tnccs_20_t *this, void *buf, size_t buflen)
409 {
410 chunk_t data;
411 pb_tnc_batch_t *batch;
412 pb_tnc_msg_t *msg;
413 enumerator_t *enumerator;
414 status_t status;
415
416 if (this->is_server && !this->connection_id)
417 {
418 this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
419 TNCCS_2_0, (tnccs_t*)this, _send_msg,
420 &this->request_handshake_retry,
421 this->max_msg_len, &this->recs);
422 if (!this->connection_id)
423 {
424 return FAILED;
425 }
426 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
427 TNC_CONNECTION_STATE_CREATE);
428 tnc->imvs->notify_connection_change(tnc->imvs, this->connection_id,
429 TNC_CONNECTION_STATE_HANDSHAKE);
430 }
431
432 data = chunk_create(buf, buflen);
433 DBG1(DBG_TNC, "received TNCCS batch (%u bytes) for Connection ID %u",
434 data.len, this->connection_id);
435 DBG3(DBG_TNC, "%B", &data);
436 batch = pb_tnc_batch_create_from_data(this->is_server, data);
437 status = batch->process(batch, this->state_machine);
438
439 if (status != FAILED)
440 {
441 enumerator_t *enumerator;
442 pb_tnc_msg_t *msg;
443 pb_tnc_batch_type_t batch_type;
444 bool empty = TRUE;
445
446 batch_type = batch->get_type(batch);
447
448 if (batch_type == PB_BATCH_CRETRY)
449 {
450 /* Send an SRETRY batch in response */
451 this->mutex->lock(this->mutex);
452 build_retry_batch(this);
453 this->mutex->unlock(this->mutex);
454 }
455 else if (batch_type == PB_BATCH_SRETRY)
456 {
457 /* Restart the measurements */
458 tnc->imcs->notify_connection_change(tnc->imcs,
459 this->connection_id, TNC_CONNECTION_STATE_HANDSHAKE);
460 this->send_msg = TRUE;
461 tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
462 this->send_msg = FALSE;
463 }
464
465 enumerator = batch->create_msg_enumerator(batch);
466 while (enumerator->enumerate(enumerator, &msg))
467 {
468 handle_message(this, msg);
469 empty = FALSE;
470 }
471 enumerator->destroy(enumerator);
472
473 /* received an empty CLOSE batch from PB-TNC client */
474 if (this->is_server && batch_type == PB_BATCH_CLOSE && empty)
475 {
476 batch->destroy(batch);
477 if (this->fatal_error)
478 {
479 DBG1(DBG_TNC, "a fatal PB-TNC error occurred, "
480 "terminating connection");
481 return FAILED;
482 }
483 else
484 {
485 return SUCCESS;
486 }
487 }
488
489 this->send_msg = TRUE;
490 if (this->is_server)
491 {
492 tnc->imvs->batch_ending(tnc->imvs, this->connection_id);
493 }
494 else
495 {
496 tnc->imcs->batch_ending(tnc->imcs, this->connection_id);
497 }
498 this->send_msg = FALSE;
499 }
500
501 switch (status)
502 {
503 case FAILED:
504 this->fatal_error = TRUE;
505 this->mutex->lock(this->mutex);
506 change_batch_type(this, PB_BATCH_CLOSE);
507 this->mutex->unlock(this->mutex);
508 /* fall through to add error messages to outbound batch */
509 case VERIFY_ERROR:
510 enumerator = batch->create_error_enumerator(batch);
511 while (enumerator->enumerate(enumerator, &msg))
512 {
513 this->mutex->lock(this->mutex);
514 this->messages->insert_last(this->messages, msg->get_ref(msg));
515 this->mutex->unlock(this->mutex);
516 }
517 enumerator->destroy(enumerator);
518 break;
519 case SUCCESS:
520 default:
521 break;
522 }
523 batch->destroy(batch);
524
525 return NEED_MORE;
526 }
527
528 /**
529 * Build a RESULT batch if a final recommendation is available
530 */
531 static void check_and_build_recommendation(private_tnccs_20_t *this)
532 {
533 TNC_IMV_Action_Recommendation rec;
534 TNC_IMV_Evaluation_Result eval;
535 TNC_IMVID id;
536 chunk_t reason, language;
537 enumerator_t *enumerator;
538 pb_tnc_msg_t *msg;
539 pb_access_recommendation_code_t pb_rec;
540
541 if (!this->recs->have_recommendation(this->recs, &rec, &eval))
542 {
543 tnc->imvs->solicit_recommendation(tnc->imvs, this->connection_id);
544 }
545 if (this->recs->have_recommendation(this->recs, &rec, &eval))
546 {
547 this->batch_type = PB_BATCH_RESULT;
548
549 msg = pb_assessment_result_msg_create(eval);
550 this->messages->insert_last(this->messages, msg);
551
552 /**
553 * Map IMV Action Recommendation codes to PB Access Recommendation codes
554 */
555 switch (rec)
556 {
557 case TNC_IMV_ACTION_RECOMMENDATION_ALLOW:
558 pb_rec = PB_REC_ACCESS_ALLOWED;
559 break;
560 case TNC_IMV_ACTION_RECOMMENDATION_ISOLATE:
561 pb_rec = PB_REC_QUARANTINED;
562 break;
563 case TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS:
564 case TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION:
565 default:
566 pb_rec = PB_REC_ACCESS_DENIED;
567 }
568 msg = pb_access_recommendation_msg_create(pb_rec);
569 this->messages->insert_last(this->messages, msg);
570
571 enumerator = this->recs->create_reason_enumerator(this->recs);
572 while (enumerator->enumerate(enumerator, &id, &reason, &language))
573 {
574 msg = pb_reason_string_msg_create(reason, language);
575 this->messages->insert_last(this->messages, msg);
576 }
577 enumerator->destroy(enumerator);
578 }
579 }
580
581 METHOD(tls_t, build, status_t,
582 private_tnccs_20_t *this, void *buf, size_t *buflen, size_t *msglen)
583 {
584 status_t status;
585 pb_tnc_state_t state;
586
587 /* Initialize the connection */
588 if (!this->is_server && !this->connection_id)
589 {
590 pb_tnc_msg_t *msg;
591 char *pref_lang;
592
593 this->connection_id = tnc->tnccs->create_connection(tnc->tnccs,
594 TNCCS_2_0, (tnccs_t*)this, _send_msg,
595 &this->request_handshake_retry,
596 this->max_msg_len, NULL);
597 if (!this->connection_id)
598 {
599 return FAILED;
600 }
601
602 /* Create PB-TNC Language Preference message */
603 pref_lang = tnc->imcs->get_preferred_language(tnc->imcs);
604 msg = pb_language_preference_msg_create(chunk_create(pref_lang,
605 strlen(pref_lang)));
606 this->mutex->lock(this->mutex);
607 this->batch_type = PB_BATCH_CDATA;
608 this->messages->insert_last(this->messages, msg);
609 this->mutex->unlock(this->mutex);
610
611 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
612 TNC_CONNECTION_STATE_CREATE);
613 tnc->imcs->notify_connection_change(tnc->imcs, this->connection_id,
614 TNC_CONNECTION_STATE_HANDSHAKE);
615 this->send_msg = TRUE;
616 tnc->imcs->begin_handshake(tnc->imcs, this->connection_id);
617 this->send_msg = FALSE;
618 }
619
620 state = this->state_machine->get_state(this->state_machine);
621
622 if (this->fatal_error && state == PB_STATE_END)
623 {
624 DBG1(DBG_TNC, "a fatal PB-TNC error occurred, terminating connection");
625 return FAILED;
626 }
627
628 /* Do not allow any asynchronous IMCs or IMVs to add additional messages */
629 this->mutex->lock(this->mutex);
630
631 if (this->request_handshake_retry)
632 {
633 if (state != PB_STATE_INIT)
634 {
635 build_retry_batch(this);
636 }
637
638 /* Reset the flag for the next handshake retry request */
639 this->request_handshake_retry = FALSE;
640 }
641
642 if (this->is_server && state == PB_STATE_SERVER_WORKING &&
643 this->recs->have_recommendation(this->recs, NULL, NULL))
644 {
645 check_and_build_recommendation(this);
646 }
647
648 if (this->batch_type == PB_BATCH_NONE)
649 {
650 if (this->is_server && state == PB_STATE_SERVER_WORKING)
651 {
652 if (this->state_machine->get_empty_cdata(this->state_machine))
653 {
654 check_and_build_recommendation(this);
655 }
656 else
657 {
658 DBG2(DBG_TNC, "no recommendation available yet, "
659 "sending empty PB-TNC SDATA batch");
660 this->batch_type = PB_BATCH_SDATA;
661 }
662 }
663 else
664 {
665 /**
666 * In the DECIDED state and if no CRETRY is under way,
667 * a PB-TNC client replies with an empty CLOSE batch.
668 */
669 if (state == PB_STATE_DECIDED)
670 {
671 this->batch_type = PB_BATCH_CLOSE;
672 }
673 }
674 }
675
676 if (this->batch_type != PB_BATCH_NONE)
677 {
678 pb_tnc_batch_t *batch;
679 pb_tnc_msg_t *msg;
680 chunk_t data;
681 int msg_count;
682 enumerator_t *enumerator;
683
684 if (this->state_machine->send_batch(this->state_machine, this->batch_type))
685 {
686 batch = pb_tnc_batch_create(this->is_server, this->batch_type,
687 min(this->max_batch_len, *buflen));
688
689 enumerator = this->messages->create_enumerator(this->messages);
690 while (enumerator->enumerate(enumerator, &msg))
691 {
692 if (batch->add_msg(batch, msg))
693 {
694 this->messages->remove_at(this->messages, enumerator);
695 }
696 else
697 {
698 break;
699 }
700 }
701 enumerator->destroy(enumerator);
702
703 batch->build(batch);
704 data = batch->get_encoding(batch);
705 DBG1(DBG_TNC, "sending PB-TNC %N batch (%d bytes) for Connection ID %u",
706 pb_tnc_batch_type_names, this->batch_type, data.len,
707 this->connection_id);
708 DBG3(DBG_TNC, "%B", &data);
709
710 *buflen = data.len;
711 *msglen = 0;
712 memcpy(buf, data.ptr, *buflen);
713 batch->destroy(batch);
714
715 msg_count = this->messages->get_count(this->messages);
716 if (msg_count)
717 {
718 DBG2(DBG_TNC, "queued %d PB-TNC message%s for next %N batch",
719 msg_count, (msg_count == 1) ? "" : "s",
720 pb_tnc_batch_type_names, this->batch_type);
721 }
722 else
723 {
724 this->batch_type = PB_BATCH_NONE;
725 }
726
727 status = ALREADY_DONE;
728 }
729 else
730 {
731 change_batch_type(this, PB_BATCH_NONE);
732 status = INVALID_STATE;
733 }
734 }
735 else
736 {
737 DBG1(DBG_TNC, "no PB-TNC batch to send");
738 status = INVALID_STATE;
739 }
740 this->mutex->unlock(this->mutex);
741
742 return status;
743 }
744
745 METHOD(tls_t, is_server, bool,
746 private_tnccs_20_t *this)
747 {
748 return this->is_server;
749 }
750
751 METHOD(tls_t, get_purpose, tls_purpose_t,
752 private_tnccs_20_t *this)
753 {
754 return TLS_PURPOSE_EAP_TNC;
755 }
756
757 METHOD(tls_t, is_complete, bool,
758 private_tnccs_20_t *this)
759 {
760 TNC_IMV_Action_Recommendation rec;
761 TNC_IMV_Evaluation_Result eval;
762
763 if (this->recs && this->recs->have_recommendation(this->recs, &rec, &eval))
764 {
765 return tnc->imvs->enforce_recommendation(tnc->imvs, rec, eval);
766 }
767 else
768 {
769 return FALSE;
770 }
771 }
772
773 METHOD(tls_t, get_eap_msk, chunk_t,
774 private_tnccs_20_t *this)
775 {
776 return chunk_empty;
777 }
778
779 METHOD(tls_t, destroy, void,
780 private_tnccs_20_t *this)
781 {
782 tnc->tnccs->remove_connection(tnc->tnccs, this->connection_id,
783 this->is_server);
784 this->state_machine->destroy(this->state_machine);
785 this->mutex->destroy(this->mutex);
786 this->messages->destroy_offset(this->messages,
787 offsetof(pb_tnc_msg_t, destroy));
788 free(this);
789 }
790
791 /**
792 * See header
793 */
794 tls_t *tnccs_20_create(bool is_server)
795 {
796 private_tnccs_20_t *this;
797
798 INIT(this,
799 .public = {
800 .process = _process,
801 .build = _build,
802 .is_server = _is_server,
803 .get_purpose = _get_purpose,
804 .is_complete = _is_complete,
805 .get_eap_msk = _get_eap_msk,
806 .destroy = _destroy,
807 },
808 .is_server = is_server,
809 .state_machine = pb_tnc_state_machine_create(is_server),
810 .mutex = mutex_create(MUTEX_TYPE_DEFAULT),
811 .messages = linked_list_create(),
812 .max_batch_len = lib->settings->get_int(lib->settings,
813 "%s.plugins.tnccs-20.max_batch_size", 65522,
814 charon->name),
815 .max_msg_len = lib->settings->get_int(lib->settings,
816 "%s.plugins.tnccs-20.max_message_size", 65490,
817 charon->name),
818 );
819
820 return &this->public;
821 }