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